]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil.c
mDNSResponder-878.200.35.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/CFUtils.h>
10 #include <CoreUtils/CommandLineUtils.h>
11 #include <CoreUtils/DataBufferUtils.h>
12 #include <CoreUtils/DebugServices.h>
13 #include <CoreUtils/HTTPUtils.h>
14 #include <CoreUtils/JSONUtils.h>
15 #include <CoreUtils/LogUtils.h>
16 #include <CoreUtils/MiscUtils.h>
17 #include <CoreUtils/NetUtils.h>
18 #include <CoreUtils/PrintFUtils.h>
19 #include <CoreUtils/RandomNumberUtils.h>
20 #include <CoreUtils/SoftLinking.h>
21 #include <CoreUtils/StringUtils.h>
22 #include <CoreUtils/TickUtils.h>
23 #include <dns_sd.h>
24 #include <dns_sd_private.h>
25
26 #include CF_RUNTIME_HEADER
27
28 #if( TARGET_OS_DARWIN )
29 #include <CFNetwork/CFHost.h>
30 #include <CoreFoundation/CoreFoundation.h>
31 #include <SystemConfiguration/SCPrivate.h>
32 #include <dnsinfo.h>
33 #include <libproc.h>
34 #include <netdb.h>
35 #include <pcap.h>
36 #include <spawn.h>
37 #include <sys/proc_info.h>
38 #endif
39
40 #if( TARGET_OS_POSIX )
41 #include <sys/resource.h>
42 #endif
43
44 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
45 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
46 #endif
47
48 //===========================================================================================================================
49 // Versioning
50 //===========================================================================================================================
51
52 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
53
54 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
55 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
56 #endif
57
58 //===========================================================================================================================
59 // DNS-SD
60 //===========================================================================================================================
61
62 // DNS-SD API flag descriptors
63
64 #define kDNSServiceFlagsDescriptors \
65 "\x00" "AutoTrigger\0" \
66 "\x01" "Add\0" \
67 "\x02" "Default\0" \
68 "\x03" "NoAutoRename\0" \
69 "\x04" "Shared\0" \
70 "\x05" "Unique\0" \
71 "\x06" "BrowseDomains\0" \
72 "\x07" "RegistrationDomains\0" \
73 "\x08" "LongLivedQuery\0" \
74 "\x09" "AllowRemoteQuery\0" \
75 "\x0A" "ForceMulticast\0" \
76 "\x0B" "KnownUnique\0" \
77 "\x0C" "ReturnIntermediates\0" \
78 "\x0D" "NonBrowsable\0" \
79 "\x0E" "ShareConnection\0" \
80 "\x0F" "SuppressUnusable\0" \
81 "\x10" "Timeout\0" \
82 "\x11" "IncludeP2P\0" \
83 "\x12" "WakeOnResolve\0" \
84 "\x13" "BackgroundTrafficClass\0" \
85 "\x14" "IncludeAWDL\0" \
86 "\x15" "Validate\0" \
87 "\x16" "UnicastResponse\0" \
88 "\x17" "ValidateOptional\0" \
89 "\x18" "WakeOnlyService\0" \
90 "\x19" "ThresholdOne\0" \
91 "\x1A" "ThresholdFinder\0" \
92 "\x1B" "DenyCellular\0" \
93 "\x1C" "ServiceIndex\0" \
94 "\x1D" "DenyExpensive\0" \
95 "\x1E" "PathEvaluationDone\0" \
96 "\x00"
97
98 #define kDNSServiceProtocolDescriptors \
99 "\x00" "IPv4\0" \
100 "\x01" "IPv6\0" \
101 "\x04" "UDP\0" \
102 "\x05" "TCP\0" \
103 "\x00"
104
105 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
106
107 //===========================================================================================================================
108 // DNS
109 //===========================================================================================================================
110
111 #define kDNSPort 53
112 #define kDNSCompressionOffsetMax 0x3FFF
113 #define kDNSMaxUDPMessageSize 512
114 #define kDNSMaxTCPMessageSize UINT16_MAX
115
116 #define kDomainLabelLengthMax 63
117 #define kDomainNameLengthMax 256
118
119 typedef struct
120 {
121 uint8_t id[ 2 ];
122 uint8_t flags[ 2 ];
123 uint8_t questionCount[ 2 ];
124 uint8_t answerCount[ 2 ];
125 uint8_t authorityCount[ 2 ];
126 uint8_t additionalCount[ 2 ];
127
128 } DNSHeader;
129
130 #define kDNSHeaderLength 12
131 check_compile_time( sizeof( DNSHeader ) == kDNSHeaderLength );
132
133 #define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id )
134 #define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags )
135 #define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount )
136 #define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount )
137 #define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount )
138 #define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount )
139
140 #define DNSHeaderSetID( HDR, X ) WriteBig16( ( HDR )->id, (X) )
141 #define DNSHeaderSetFlags( HDR, X ) WriteBig16( ( HDR )->flags, (X) )
142 #define DNSHeaderSetQuestionCount( HDR, X ) WriteBig16( ( HDR )->questionCount, (X) )
143 #define DNSHeaderSetAnswerCount( HDR, X ) WriteBig16( ( HDR )->answerCount, (X) )
144 #define DNSHeaderSetAuthorityCount( HDR, X ) WriteBig16( ( HDR )->authorityCount, (X) )
145 #define DNSHeaderSetAdditionalCount( HDR, X ) WriteBig16( ( HDR )->additionalCount, (X) )
146
147 // Single-bit DNS header fields
148
149 #define kDNSHeaderFlag_Response ( 1 << 15 ) // QR (bit 15), Query (0)/Response (1)
150 #define kDNSHeaderFlag_AuthAnswer ( 1 << 10 ) // AA (bit 10), Authoritative Answer
151 #define kDNSHeaderFlag_Truncation ( 1 << 9 ) // TC (bit 9), TrunCation
152 #define kDNSHeaderFlag_RecursionDesired ( 1 << 8 ) // RD (bit 8), Recursion Desired
153 #define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 ) // RA (bit 7), Recursion Available
154 #define kDNSHeaderFlag_Z ( 1 << 6 ) // Z (bit 6), Reserved (must be zero)
155 #define kDNSHeaderFlag_AuthenticData ( 1 << 5 ) // AD (bit 5), Authentic Data (RFC 2535, Section 6)
156 #define kDNSHeaderFlag_CheckingDisabled ( 1 << 4 ) // CD (bit 4), Checking Disabled (RFC 2535, Section 6)
157
158 // OPCODE (bits 14-11), Operation Code
159
160 #define DNSFlagsGetOpCode( FLAGS ) ( ( (FLAGS) >> 11 ) & 0x0FU )
161 #define DNSFlagsSetOpCode( FLAGS, OPCODE ) \
162 do{ (FLAGS) = ( (FLAGS) & ~0x7800U ) | ( ( (OPCODE) & 0x0FU ) << 11 ); } while( 0 )
163
164 #define kDNSOpCode_Query 0 // QUERY (standard query)
165 #define kDNSOpCode_InverseQuery 1 // IQUERY (inverse query)
166 #define kDNSOpCode_Status 2 // STATUS
167 #define kDNSOpCode_Notify 4 // NOTIFY
168 #define kDNSOpCode_Update 5 // UPDATE
169
170 // RCODE (bits 3-0), Response Code
171
172 #define DNSFlagsGetRCode( FLAGS ) ( (FLAGS) & 0x0FU )
173 #define DNSFlagsSetRCode( FLAGS, RCODE ) \
174 do{ (FLAGS) = ( (FLAGS) & ~0x000FU ) | ( (RCODE) & 0x0FU ); } while( 0 )
175
176 #define kDNSRCode_NoError 0
177 #define kDNSRCode_FormatError 1
178 #define kDNSRCode_ServerFailure 2
179 #define kDNSRCode_NXDomain 3
180 #define kDNSRCode_NotImplemented 4
181 #define kDNSRCode_Refused 5
182
183 typedef struct
184 {
185 uint8_t type[ 2 ];
186 uint8_t class[ 2 ];
187
188 } DNSQuestionFixedFields;
189
190 check_compile_time( sizeof( DNSQuestionFixedFields ) == 4 );
191
192 #define DNSQuestionFixedFieldsInit( FIELDS, QTYPE, QCLASS ) \
193 do { WriteBig16( (FIELDS)->type, QTYPE ); WriteBig16( (FIELDS)->class, QCLASS ); } while( 0 )
194
195 #define DNSQuestionFixedFieldsGetType( FIELDS ) ReadBig16( (FIELDS)->type )
196 #define DNSQuestionFixedFieldsGetClass( FIELDS ) ReadBig16( (FIELDS)->class )
197
198 typedef struct
199 {
200 uint8_t type[ 2 ];
201 uint8_t class[ 2 ];
202 uint8_t ttl[ 4 ];
203 uint8_t rdlength[ 2 ];
204
205 } DNSRecordFixedFields;
206
207 check_compile_time( sizeof( DNSRecordFixedFields ) == 10 );
208
209 #define DNSRecordFixedFieldsInit( FIELDS, TYPE, CLASS, TTL, RDLENGTH ) \
210 do \
211 { \
212 WriteBig16( (FIELDS)->type, TYPE ); \
213 WriteBig16( (FIELDS)->class, CLASS ); \
214 WriteBig32( (FIELDS)->ttl, TTL ); \
215 WriteBig16( (FIELDS)->rdlength, RDLENGTH ); \
216 \
217 } while( 0 )
218
219 //===========================================================================================================================
220 // mDNS
221 //===========================================================================================================================
222
223 #define kMDNSPort 5353
224
225 #define kDefaultMDNSMessageID 0
226 #define kDefaultMDNSQueryFlags 0
227
228 #define kQClassUnicastResponseBit ( 1U << 15 )
229 #define kRRClassCacheFlushBit ( 1U << 15 )
230
231 //===========================================================================================================================
232 // Test DNS Server
233 //===========================================================================================================================
234
235 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
236
237 #define kTestDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0
238
239 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
240
241 #define kTestDNSServerBaseAddrV6 \
242 ( (const uint8_t *) "\x20\x01\x0D\xB8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" )
243
244 //===========================================================================================================================
245 // Gerneral Command Options
246 //===========================================================================================================================
247
248 // Command option macros
249
250 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
251 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
252 (SHORT_HELP), NULL )
253
254 #define kRequiredOptionSuffix " [REQUIRED]"
255
256 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
257 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
258 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
259 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
260
261 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
262 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
263
264 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
265 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
266 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
267 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
268
269 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
270 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
271
272 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
273 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
274 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
275 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
276
277 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
278 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
279
280 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
281 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
282 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
283 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
284
285 // DNS-SD API flag options
286
287 static int gDNSSDFlags = 0;
288 static int gDNSSDFlag_BrowseDomains = false;
289 static int gDNSSDFlag_DenyCellular = false;
290 static int gDNSSDFlag_DenyExpensive = false;
291 static int gDNSSDFlag_ForceMulticast = false;
292 static int gDNSSDFlag_IncludeAWDL = false;
293 static int gDNSSDFlag_NoAutoRename = false;
294 static int gDNSSDFlag_PathEvaluationDone = false;
295 static int gDNSSDFlag_RegistrationDomains = false;
296 static int gDNSSDFlag_ReturnIntermediates = false;
297 static int gDNSSDFlag_Shared = false;
298 static int gDNSSDFlag_SuppressUnusable = false;
299 static int gDNSSDFlag_Timeout = false;
300 static int gDNSSDFlag_UnicastResponse = false;
301 static int gDNSSDFlag_Unique = false;
302 static int gDNSSDFlag_WakeOnResolve = false;
303
304 #define DNSSDFlagsOption() \
305 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
306 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
307
308 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
309 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
310
311 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
312 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
313 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
314 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
315 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
316 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
317 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
318 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
319 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
320 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
321 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
322 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
323 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
324
325 // Interface option
326
327 static const char * gInterface = NULL;
328
329 #define InterfaceOption() \
330 StringOption( 'i', "interface", &gInterface, "interface", \
331 "Network interface by name or index. Use index -1 for local-only.", false )
332
333 // Connection options
334
335 #define kConnectionArg_Normal ""
336 #define kConnectionArgPrefix_PID "pid:"
337 #define kConnectionArgPrefix_UUID "uuid:"
338
339 static const char * gConnectionOpt = kConnectionArg_Normal;
340
341 #define ConnectionOptions() \
342 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
343 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
344 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
345
346 #define kConnectionSection_Name "Connection Option"
347 #define kConnectionSection_Text \
348 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
349 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
350 "specifying the connection option without an argument, i.e.,\n" \
351 "\n" \
352 " --connection\n" \
353 "\n" \
354 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
355 "\n" \
356 " --connection=pid:<PID>\n" \
357 "\n" \
358 "to specify the delegator by PID, or use\n" \
359 "\n" \
360 " --connection=uuid:<UUID>\n" \
361 "\n" \
362 "to specify the delegator by UUID.\n" \
363 "\n" \
364 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
365 "\n" \
366 " --no-connection\n"
367
368 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
369
370 // Help text for record data options
371
372 #define kRDataArgPrefix_Domain "domain:"
373 #define kRDataArgPrefix_File "file:"
374 #define kRDataArgPrefix_HexString "hex:"
375 #define kRDataArgPrefix_IPv4 "ipv4:"
376 #define kRDataArgPrefix_IPv6 "ipv6:"
377 #define kRDataArgPrefix_SRV "srv:"
378 #define kRDataArgPrefix_String "string:"
379 #define kRDataArgPrefix_TXT "txt:"
380
381 #define kRecordDataSection_Name "Record Data Arguments"
382 #define kRecordDataSection_Text \
383 "A record data argument is specified in one of the following formats:\n" \
384 "\n" \
385 "Format Syntax Example\n" \
386 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
387 "File containing record data file:<file path> file:/path/to/rdata.bin\n" \
388 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
389 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
390 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
391 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
392 "String (w/escaped hex bytes) string:<string> string:'\\x09color=red'\n" \
393 "TXT record keys and values txt:<comma-delimited keys and values> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n"
394
395 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
396
397 //===========================================================================================================================
398 // Browse Command Options
399 //===========================================================================================================================
400
401 static char ** gBrowse_ServiceTypes = NULL;
402 static size_t gBrowse_ServiceTypesCount = 0;
403 static const char * gBrowse_Domain = NULL;
404 static int gBrowse_DoResolve = false;
405 static int gBrowse_QueryTXT = false;
406 static int gBrowse_TimeLimitSecs = 0;
407
408 static CLIOption kBrowseOpts[] =
409 {
410 InterfaceOption(),
411 MultiStringOption( 't', "type", &gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
412 StringOption( 'd', "domain", &gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ),
413
414 CLI_OPTION_GROUP( "Flags" ),
415 DNSSDFlagsOption(),
416 DNSSDFlagsOption_IncludeAWDL(),
417
418 CLI_OPTION_GROUP( "Operation" ),
419 ConnectionOptions(),
420 BooleanOption( 0 , "resolve", &gBrowse_DoResolve, "Resolve service instances." ),
421 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT, "Query TXT records of service instances." ),
422 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
423
424 ConnectionSection(),
425 CLI_OPTION_END()
426 };
427
428 //===========================================================================================================================
429 // GetAddrInfo Command Options
430 //===========================================================================================================================
431
432 static const char * gGetAddrInfo_Name = NULL;
433 static int gGetAddrInfo_ProtocolIPv4 = false;
434 static int gGetAddrInfo_ProtocolIPv6 = false;
435 static int gGetAddrInfo_OneShot = false;
436 static int gGetAddrInfo_TimeLimitSecs = 0;
437
438 static CLIOption kGetAddrInfoOpts[] =
439 {
440 InterfaceOption(),
441 StringOption( 'n', "name", &gGetAddrInfo_Name, "domain name", "Domain name to resolve.", true ),
442 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ),
443 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ),
444
445 CLI_OPTION_GROUP( "Flags" ),
446 DNSSDFlagsOption(),
447 DNSSDFlagsOption_DenyCellular(),
448 DNSSDFlagsOption_DenyExpensive(),
449 DNSSDFlagsOption_PathEvalDone(),
450 DNSSDFlagsOption_ReturnIntermediates(),
451 DNSSDFlagsOption_SuppressUnusable(),
452 DNSSDFlagsOption_Timeout(),
453
454 CLI_OPTION_GROUP( "Operation" ),
455 ConnectionOptions(),
456 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot, "Finish after first set of results." ),
457 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
458
459 ConnectionSection(),
460 CLI_OPTION_END()
461 };
462
463 //===========================================================================================================================
464 // QueryRecord Command Options
465 //===========================================================================================================================
466
467 static const char * gQueryRecord_Name = NULL;
468 static const char * gQueryRecord_Type = NULL;
469 static int gQueryRecord_OneShot = false;
470 static int gQueryRecord_TimeLimitSecs = 0;
471 static int gQueryRecord_RawRData = false;
472
473 static CLIOption kQueryRecordOpts[] =
474 {
475 InterfaceOption(),
476 StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ),
477 StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
478
479 CLI_OPTION_GROUP( "Flags" ),
480 DNSSDFlagsOption(),
481 DNSSDFlagsOption_IncludeAWDL(),
482 DNSSDFlagsOption_ForceMulticast(),
483 DNSSDFlagsOption_Timeout(),
484 DNSSDFlagsOption_ReturnIntermediates(),
485 DNSSDFlagsOption_SuppressUnusable(),
486 DNSSDFlagsOption_UnicastResponse(),
487 DNSSDFlagsOption_DenyCellular(),
488 DNSSDFlagsOption_DenyExpensive(),
489 DNSSDFlagsOption_PathEvalDone(),
490
491 CLI_OPTION_GROUP( "Operation" ),
492 ConnectionOptions(),
493 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot, "Finish after first set of results." ),
494 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
495 BooleanOption( 0 , "raw", &gQueryRecord_RawRData, "Show record data as a hexdump." ),
496
497 ConnectionSection(),
498 CLI_OPTION_END()
499 };
500
501 //===========================================================================================================================
502 // Register Command Options
503 //===========================================================================================================================
504
505 static const char * gRegister_Name = NULL;
506 static const char * gRegister_Type = NULL;
507 static const char * gRegister_Domain = NULL;
508 static int gRegister_Port = 0;
509 static const char * gRegister_TXT = NULL;
510 static int gRegister_LifetimeMs = -1;
511 static const char ** gAddRecord_Types = NULL;
512 static size_t gAddRecord_TypesCount = 0;
513 static const char ** gAddRecord_Data = NULL;
514 static size_t gAddRecord_DataCount = 0;
515 static const char ** gAddRecord_TTLs = NULL;
516 static size_t gAddRecord_TTLsCount = 0;
517 static const char * gUpdateRecord_Data = NULL;
518 static int gUpdateRecord_DelayMs = 0;
519 static int gUpdateRecord_TTL = 0;
520
521 static CLIOption kRegisterOpts[] =
522 {
523 InterfaceOption(),
524 StringOption( 'n', "name", &gRegister_Name, "service name", "Name of service.", false ),
525 StringOption( 't', "type", &gRegister_Type, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
526 StringOption( 'd', "domain", &gRegister_Domain, "domain", "Domain in which to advertise the service.", false ),
527 IntegerOption( 'p', "port", &gRegister_Port, "port number", "Service's port number.", true ),
528 StringOption( 0 , "txt", &gRegister_TXT, "record data", "The TXT record data. See " kRecordDataSection_Name " below.", false ),
529
530 CLI_OPTION_GROUP( "Flags" ),
531 DNSSDFlagsOption(),
532 DNSSDFlagsOption_IncludeAWDL(),
533 DNSSDFlagsOption_NoAutoRename(),
534
535 CLI_OPTION_GROUP( "Operation" ),
536 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
537
538 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
539 StringOption( 0 , "updateData", &gUpdateRecord_Data, "record data", "Record data for the record update. See " kRecordDataSection_Name " below.", false ),
540 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
541 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL, "seconds", "Time-to-live of the updated record.", false ),
542
543 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
544 MultiStringOption( 0 , "addType", &gAddRecord_Types, &gAddRecord_TypesCount, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
545 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data, &gAddRecord_DataCount, "record data", "Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ),
546 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs, &gAddRecord_TTLsCount, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
547
548 RecordDataSection(),
549 CLI_OPTION_END()
550 };
551
552 //===========================================================================================================================
553 // RegisterRecord Command Options
554 //===========================================================================================================================
555
556 static const char * gRegisterRecord_Name = NULL;
557 static const char * gRegisterRecord_Type = NULL;
558 static const char * gRegisterRecord_Data = NULL;
559 static int gRegisterRecord_TTL = 0;
560 static int gRegisterRecord_LifetimeMs = -1;
561 static const char * gRegisterRecord_UpdateData = NULL;
562 static int gRegisterRecord_UpdateDelayMs = 0;
563 static int gRegisterRecord_UpdateTTL = 0;
564
565 static CLIOption kRegisterRecordOpts[] =
566 {
567 InterfaceOption(),
568 StringOption( 'n', "name", &gRegisterRecord_Name, "record name", "Fully qualified domain name of record.", true ),
569 StringOption( 't', "type", &gRegisterRecord_Type, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
570 StringOption( 'd', "data", &gRegisterRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
571 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
572
573 CLI_OPTION_GROUP( "Flags" ),
574 DNSSDFlagsOption(),
575 DNSSDFlagsOption_IncludeAWDL(),
576 DNSSDFlagsOption_Shared(),
577 DNSSDFlagsOption_Unique(),
578
579 CLI_OPTION_GROUP( "Operation" ),
580 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
581
582 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
583 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData, "record data", "Record data for the record update.", false ),
584 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
585 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL, "seconds", "Time-to-live of the updated record.", false ),
586
587 RecordDataSection(),
588 CLI_OPTION_END()
589 };
590
591 //===========================================================================================================================
592 // Resolve Command Options
593 //===========================================================================================================================
594
595 static char * gResolve_Name = NULL;
596 static char * gResolve_Type = NULL;
597 static char * gResolve_Domain = NULL;
598 static int gResolve_TimeLimitSecs = 0;
599
600 static CLIOption kResolveOpts[] =
601 {
602 InterfaceOption(),
603 StringOption( 'n', "name", &gResolve_Name, "service name", "Name of the service instance to resolve.", true ),
604 StringOption( 't', "type", &gResolve_Type, "service type", "Type of the service instance to resolve.", true ),
605 StringOption( 'd', "domain", &gResolve_Domain, "domain", "Domain of the service instance to resolve.", true ),
606
607 CLI_OPTION_GROUP( "Flags" ),
608 DNSSDFlagsOption(),
609 DNSSDFlagsOption_ForceMulticast(),
610 DNSSDFlagsOption_IncludeAWDL(),
611 DNSSDFlagsOption_ReturnIntermediates(),
612 DNSSDFlagsOption_WakeOnResolve(),
613
614 CLI_OPTION_GROUP( "Operation" ),
615 ConnectionOptions(),
616 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
617
618 ConnectionSection(),
619 CLI_OPTION_END()
620 };
621
622 //===========================================================================================================================
623 // Reconfirm Command Options
624 //===========================================================================================================================
625
626 static const char * gReconfirmRecord_Name = NULL;
627 static const char * gReconfirmRecord_Type = NULL;
628 static const char * gReconfirmRecord_Class = NULL;
629 static const char * gReconfirmRecord_Data = NULL;
630
631 static CLIOption kReconfirmOpts[] =
632 {
633 InterfaceOption(),
634 StringOption( 'n', "name", &gReconfirmRecord_Name, "record name", "Full name of the record to reconfirm.", true ),
635 StringOption( 't', "type", &gReconfirmRecord_Type, "record type", "Type of the record to reconfirm.", true ),
636 StringOption( 'c', "class", &gReconfirmRecord_Class, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
637 StringOption( 'd', "data", &gReconfirmRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
638
639 CLI_OPTION_GROUP( "Flags" ),
640 DNSSDFlagsOption(),
641
642 RecordDataSection(),
643 CLI_OPTION_END()
644 };
645
646 //===========================================================================================================================
647 // getaddrinfo-POSIX Command Options
648 //===========================================================================================================================
649
650 static const char * gGAIPOSIX_HostName = NULL;
651 static const char * gGAIPOSIX_ServName = NULL;
652 static const char * gGAIPOSIX_Family = NULL;
653 static int gGAIPOSIXFlag_AddrConfig = false;
654 static int gGAIPOSIXFlag_All = false;
655 static int gGAIPOSIXFlag_CanonName = false;
656 static int gGAIPOSIXFlag_NumericHost = false;
657 static int gGAIPOSIXFlag_NumericServ = false;
658 static int gGAIPOSIXFlag_Passive = false;
659 static int gGAIPOSIXFlag_V4Mapped = false;
660 #if( defined( AI_V4MAPPED_CFG ) )
661 static int gGAIPOSIXFlag_V4MappedCFG = false;
662 #endif
663 #if( defined( AI_DEFAULT ) )
664 static int gGAIPOSIXFlag_Default = false;
665 #endif
666 #if( defined( AI_UNUSABLE ) )
667 static int gGAIPOSIXFlag_Unusable = false;
668 #endif
669
670 static CLIOption kGetAddrInfoPOSIXOpts[] =
671 {
672 StringOption( 'n', "hostname", &gGAIPOSIX_HostName, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
673 StringOption( 's', "servname", &gGAIPOSIX_ServName, "servname", "Port number in decimal or service name from services(5).", false ),
674
675 CLI_OPTION_GROUP( "Hints " ),
676 StringOptionEx( 'f', "family", &gGAIPOSIX_Family, "address family", "Address family to use for hints ai_family field.", false,
677 "\n"
678 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
679 "address family is specified, then AF_UNSPEC is used.\n"
680 "\n" ),
681 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig, "In hints ai_flags field, set AI_ADDRCONFIG." ),
682 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All, "In hints ai_flags field, set AI_ALL." ),
683 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName, "In hints ai_flags field, set AI_CANONNAME." ),
684 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost, "In hints ai_flags field, set AI_NUMERICHOST." ),
685 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ, "In hints ai_flags field, set AI_NUMERICSERV." ),
686 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive, "In hints ai_flags field, set AI_PASSIVE." ),
687 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped, "In hints ai_flags field, set AI_V4MAPPED." ),
688 #if( defined( AI_V4MAPPED_CFG ) )
689 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
690 #endif
691 #if( defined( AI_DEFAULT ) )
692 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ),
693 #endif
694 #if( defined( AI_UNUSABLE ) )
695 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable, "In hints ai_flags field, set AI_UNUSABLE." ),
696 #endif
697
698 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
699 CLI_OPTION_END()
700 };
701
702 //===========================================================================================================================
703 // ReverseLookup Command Options
704 //===========================================================================================================================
705
706 static const char * gReverseLookup_IPAddr = NULL;
707 static int gReverseLookup_OneShot = false;
708 static int gReverseLookup_TimeLimitSecs = 0;
709
710 static CLIOption kReverseLookupOpts[] =
711 {
712 InterfaceOption(),
713 StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
714
715 CLI_OPTION_GROUP( "Flags" ),
716 DNSSDFlagsOption(),
717 DNSSDFlagsOption_ForceMulticast(),
718 DNSSDFlagsOption_ReturnIntermediates(),
719 DNSSDFlagsOption_SuppressUnusable(),
720
721 CLI_OPTION_GROUP( "Operation" ),
722 ConnectionOptions(),
723 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot, "Finish after first set of results." ),
724 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
725
726 ConnectionSection(),
727 CLI_OPTION_END()
728 };
729
730 //===========================================================================================================================
731 // PortMapping Command Options
732 //===========================================================================================================================
733
734 static int gPortMapping_ProtocolTCP = false;
735 static int gPortMapping_ProtocolUDP = false;
736 static int gPortMapping_InternalPort = 0;
737 static int gPortMapping_ExternalPort = 0;
738 static int gPortMapping_TTL = 0;
739
740 static CLIOption kPortMappingOpts[] =
741 {
742 InterfaceOption(),
743 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP, "Use kDNSServiceProtocol_TCP." ),
744 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP, "Use kDNSServiceProtocol_UDP." ),
745 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort, "port number", "Internal port.", false ),
746 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort, "port number", "Requested external port. Use '0' for any external port.", false ),
747 IntegerOption( 0, "ttl", &gPortMapping_TTL, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
748
749 CLI_OPTION_GROUP( "Flags" ),
750 DNSSDFlagsOption(),
751
752 CLI_OPTION_GROUP( "Operation" ),
753 ConnectionOptions(),
754
755 ConnectionSection(),
756 CLI_OPTION_END()
757 };
758
759 //===========================================================================================================================
760 // BrowseAll Command Options
761 //===========================================================================================================================
762
763 static const char * gBrowseAll_Domain = NULL;
764 static char ** gBrowseAll_ServiceTypes = NULL;
765 static size_t gBrowseAll_ServiceTypesCount = 0;
766 static int gBrowseAll_BrowseTimeSecs = 5;
767 static int gBrowseAll_MaxConnectTimeSecs = 0;
768
769 static CLIOption kBrowseAllOpts[] =
770 {
771 InterfaceOption(),
772 StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ),
773 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 ),
774
775 CLI_OPTION_GROUP( "Flags" ),
776 DNSSDFlagsOption_IncludeAWDL(),
777
778 CLI_OPTION_GROUP( "Operation" ),
779 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Amount of time to spend browsing. (Default: 5 seconds)", false ),
780 IntegerOption( 'c', "maxConnectTime", &gBrowseAll_MaxConnectTimeSecs, "seconds", "Max duration of connection attempts. If <= 0, then no connections are attempted. (Default: 0 seconds)", false ),
781 CLI_OPTION_END()
782 };
783
784 //===========================================================================================================================
785 // GetNameInfo Command Options
786 //===========================================================================================================================
787
788 static void GetNameInfoCmd( void );
789
790 static char * gGetNameInfo_IPAddress = NULL;
791 static int gGetNameInfoFlag_DGram = false;
792 static int gGetNameInfoFlag_NameReqd = false;
793 static int gGetNameInfoFlag_NoFQDN = false;
794 static int gGetNameInfoFlag_NumericHost = false;
795 static int gGetNameInfoFlag_NumericScope = false;
796 static int gGetNameInfoFlag_NumericServ = false;
797
798 static CLIOption kGetNameInfoOpts[] =
799 {
800 StringOption( 'a', "address", &gGetNameInfo_IPAddress, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
801
802 CLI_OPTION_GROUP( "Flags" ),
803 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram, "Use NI_DGRAM flag." ),
804 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd, "Use NI_NAMEREQD flag." ),
805 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN, "Use NI_NOFQDN flag." ),
806 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost, "Use NI_NUMERICHOST flag." ),
807 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope, "Use NI_NUMERICSCOPE flag." ),
808 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ, "Use NI_NUMERICSERV flag." ),
809
810 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
811 CLI_OPTION_END()
812 };
813
814 //===========================================================================================================================
815 // GetAddrInfoStress Command Options
816 //===========================================================================================================================
817
818 static int gGAIStress_TestDurationSecs = 0;
819 static int gGAIStress_ConnectionCount = 0;
820 static int gGAIStress_DurationMinMs = 0;
821 static int gGAIStress_DurationMaxMs = 0;
822 static int gGAIStress_RequestCountMax = 0;
823
824 static CLIOption kGetAddrInfoStressOpts[] =
825 {
826 InterfaceOption(),
827
828 CLI_OPTION_GROUP( "Flags" ),
829 DNSSDFlagsOption_ReturnIntermediates(),
830 DNSSDFlagsOption_SuppressUnusable(),
831
832 CLI_OPTION_GROUP( "Operation" ),
833 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
834 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount, "integer", "Number of simultaneous DNS-SD connections.", true ),
835 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
836 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
837 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax, "integer", "Maximum number of requests on a connection before restarting it.", true ),
838 CLI_OPTION_END()
839 };
840
841 //===========================================================================================================================
842 // DNSQuery Command Options
843 //===========================================================================================================================
844
845 static char * gDNSQuery_Name = NULL;
846 static char * gDNSQuery_Type = "A";
847 static char * gDNSQuery_Server = NULL;
848 static int gDNSQuery_TimeLimitSecs = 5;
849 static int gDNSQuery_UseTCP = false;
850 static int gDNSQuery_Flags = kDNSHeaderFlag_RecursionDesired;
851 static int gDNSQuery_RawRData = false;
852 static int gDNSQuery_Verbose = false;
853
854 #if( TARGET_OS_DARWIN )
855 #define kDNSQueryServerOptionIsRequired false
856 #else
857 #define kDNSQueryServerOptionIsRequired true
858 #endif
859
860 static CLIOption kDNSQueryOpts[] =
861 {
862 StringOption( 'n', "name", &gDNSQuery_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
863 StringOption( 't', "type", &gDNSQuery_Type, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
864 StringOption( 's', "server", &gDNSQuery_Server, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ),
865 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
866 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP, "Send the DNS query via TCP instead of UDP." ),
867 IntegerOption( 'f', "flags", &gDNSQuery_Flags, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
868 BooleanOption( 0 , "raw", &gDNSQuery_RawRData, "Present record data as a hexdump." ),
869 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose, "Prints the DNS message to be sent to the server." ),
870 CLI_OPTION_END()
871 };
872
873 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
874 //===========================================================================================================================
875 // DNSCrypt Command Options
876 //===========================================================================================================================
877
878 static char * gDNSCrypt_ProviderName = NULL;
879 static char * gDNSCrypt_ProviderKey = NULL;
880 static char * gDNSCrypt_Name = NULL;
881 static char * gDNSCrypt_Type = NULL;
882 static char * gDNSCrypt_Server = NULL;
883 static int gDNSCrypt_TimeLimitSecs = 5;
884 static int gDNSCrypt_RawRData = false;
885 static int gDNSCrypt_Verbose = false;
886
887 static CLIOption kDNSCryptOpts[] =
888 {
889 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName, "name", "The DNSCrypt provider name.", true ),
890 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey, "hex string", "The DNSCrypt provider's public signing key.", true ),
891 StringOption( 'n', "name", &gDNSCrypt_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
892 StringOption( 't', "type", &gDNSCrypt_Type, "type", "Question type (QTYPE) to put in DNS query message.", true ),
893 StringOption( 's', "server", &gDNSCrypt_Server, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
894 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
895 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData, "Present record data as a hexdump." ),
896 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose, "Prints the DNS message to be sent to the server." ),
897 CLI_OPTION_END()
898 };
899 #endif
900
901 //===========================================================================================================================
902 // MDNSQuery Command Options
903 //===========================================================================================================================
904
905 static char * gMDNSQuery_Name = NULL;
906 static char * gMDNSQuery_Type = NULL;
907 static int gMDNSQuery_SourcePort = 0;
908 static int gMDNSQuery_IsQU = false;
909 static int gMDNSQuery_RawRData = false;
910 static int gMDNSQuery_UseIPv4 = false;
911 static int gMDNSQuery_UseIPv6 = false;
912 static int gMDNSQuery_AllResponses = false;
913 static int gMDNSQuery_ReceiveSecs = 1;
914
915 static CLIOption kMDNSQueryOpts[] =
916 {
917 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
918 StringOption( 'n', "name", &gMDNSQuery_Name, "name", "Question name (QNAME) to put in mDNS message.", true ),
919 StringOption( 't', "type", &gMDNSQuery_Type, "type", "Question type (QTYPE) to put in mDNS message.", true ),
920 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
921 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU, "Set the unicast-response bit, i.e., send a QU question." ),
922 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData, "Present record data as a hexdump." ),
923 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4, "Use IPv4." ),
924 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6, "Use IPv6." ),
925 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses, "Print all received mDNS messages, not just those containing answers." ),
926 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 ),
927 CLI_OPTION_END()
928 };
929
930 //===========================================================================================================================
931 // PIDToUUID Command Options
932 //===========================================================================================================================
933
934 static int gPIDToUUID_PID = 0;
935
936 static CLIOption kPIDToUUIDOpts[] =
937 {
938 IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ),
939 CLI_OPTION_END()
940 };
941
942 //===========================================================================================================================
943 // DNSServer Command Options
944 //===========================================================================================================================
945
946 #define kDNSServerInfoText_Intro \
947 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
948 "presence of special labels in the query's QNAME. There are currently seven types of special labels that can be\n" \
949 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
950 "IPv4 label, and the IPv6 label.\n"
951
952 #define kDNSServerInfoText_NameExistence \
953 "A name is considered to exist if and only if it ends in d.test., and the other labels, if\n" \
954 "any, consist of\n" \
955 "\n" \
956 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
957 " 2. at most one Count label;\n" \
958 " 3. zero or more Tag labels;\n" \
959 " 4. at most one TTL label; and\n" \
960 " 5. at most one IPv4 or IPv6 label.\n"
961
962 #define kDNSServerInfoText_ResourceRecords \
963 "Currently, the server only provides CNAME, A, and AAAA records.\n" \
964 "\n" \
965 "Names that exist and begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
966 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
967 "\n" \
968 "Names that exist and have an IPv4 label have at least one A record, but no AAAA records. Names that exist and\n" \
969 "have an IPv6 label, have at least one AAAA record, but no A records. All other names that exist have at least\n" \
970 "one A record and at least one AAAA record. See \"Count Labels\" for how the number of address records for a\n" \
971 "given name is determined.\n" \
972 "\n" \
973 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
974 "2001:db8::/32 block. Both of these address blocks are reserved for documentation.\n" \
975 "See <https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
976 "\n" \
977 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
978 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for records with\n" \
979 "specific TTL values.\n"
980
981 #define kDNSServerInfoText_AliasLabel \
982 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2 .. 2^31 - 1].\n" \
983 "\n" \
984 "If QNAME exist and its first label is Alias label \"alias-N\", then the response will contain exactly N CNAME\n" \
985 "records:\n" \
986 "\n" \
987 " 1. For each i in [3 .. N], the response will contain a CNAME record whose name is identical to QNAME,\n" \
988 " except that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME\n" \
989 " record whose name has \"alias-(i - 1)\" as its first label.\n" \
990 "\n" \
991 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
992 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
993 " \"alias\" instead.\n" \
994 "\n" \
995 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
996 " is \"alias\" instead, and whose RDATA is the name identical to QNAME stripped of its first label.\n" \
997 "\n" \
998 "If QNAME exist and its first label is Alias label \"alias\", then the response will contain a single CNAME\n" \
999 "record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to QNAME\n" \
1000 "stripped of its first label.\n" \
1001 "\n" \
1002 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1003 "records:\n" \
1004 "\n" \
1005 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1006 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1007 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1008 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1009
1010 #define kDNSServerInfoText_AliasTTLLabel \
1011 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1012 "[0 .. 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1013 "\n" \
1014 "If QNAME exists and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response will contain\n" \
1015 "exactly N CNAME records:\n" \
1016 "\n" \
1017 " 1. For each i in [1 .. N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1018 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1019 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1020 "\n" \
1021 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1022 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1023 " label.\n" \
1024 "\n" \
1025 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1026 "CNAME records:\n" \
1027 "\n" \
1028 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1029 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1030 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1031
1032 #define kDNSServerInfoText_CountLabel \
1033 "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\n" \
1034 "is an integer in [N_1 .. 255].\n" \
1035 "\n" \
1036 "If QNAME exists, contains Count label \"count-N\", and has the type of address records specified by QTYPE, then\n" \
1037 "the response will contain exactly N address records:\n" \
1038 "\n" \
1039 " 1. For i in [1 .. N], the response will contain an address record of type QTYPE whose name is equal to\n" \
1040 " QNAME and whose RDATA is an address equal to a constant base address + i.\n" \
1041 "\n" \
1042 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1043 "\n" \
1044 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1045 "records:\n" \
1046 "\n" \
1047 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1048 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1049 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1050 "\n" \
1051 "If QNAME exists, contains Count label \"count-N_1-N_2\", and has the type of address records specified by\n" \
1052 "QTYPE, then the response will contain exactly N_1 address records:\n" \
1053 "\n" \
1054 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1055 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1 .. N_2].\n" \
1056 "\n" \
1057 " 2. The order of the address records will be random.\n" \
1058 "\n" \
1059 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1060 "following AAAA records:\n" \
1061 "\n" \
1062 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8::c\n" \
1063 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8::3a\n" \
1064 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8::4f\n" \
1065 "\n" \
1066 "If QNAME exists, but doesn't have the type of address records specified by QTYPE, then the response will\n" \
1067 "contain no address records, regardless of whether it contains a Count label.\n" \
1068 "\n" \
1069 "QNAMEs that exist, but don't have a Count label are treated as though they contain a count label equal to\n" \
1070 "\"count-1\".\n"
1071
1072 #define kDNSServerInfoText_TagLabel \
1073 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1074 "\n" \
1075 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1076 "to increase the sizes of domain names.\n"
1077
1078 #define kDNSServerInfoText_TTLLabel \
1079 "TTL labels are of the form \"ttl-T\", where T is an integer in [0 .. 2^31 - 1].\n" \
1080 "\n" \
1081 "If the name specified by QNAME exists, and contains TTL label \"ttl-T\", then all non-CNAME records contained\n" \
1082 "in the response will have a TTL value equal to T.\n"
1083
1084 #define kDNSServerInfoText_IPv4Label \
1085 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1086
1087 #define kDNSServerInfoText_IPv6Label \
1088 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1089
1090 #define kDNSServerDefaultTTL 60
1091
1092 static int gDNSServer_LoopbackOnly = false;
1093 static int gDNSServer_Foreground = false;
1094 static int gDNSServer_ResponseDelayMs = 0;
1095 static int gDNSServer_DefaultTTL = kDNSServerDefaultTTL;
1096 #if( TARGET_OS_DARWIN )
1097 static const char * gDNSServer_FollowPID = NULL;
1098 #endif
1099
1100 static CLIOption kDNSServerOpts[] =
1101 {
1102 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly, "Bind to to the loopback interface." ),
1103 BooleanOption( 'f', "foreground", &gDNSServer_Foreground, "Directlog output to stdout instead of system logging." ),
1104 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1105 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1106 #if( TARGET_OS_DARWIN )
1107 StringOption( 0 , "followPID", &gDNSServer_FollowPID, "pid", "Exit when the process (usually the parent proccess) specified by PID exits.", false ),
1108 #endif
1109
1110 CLI_SECTION( "Intro", kDNSServerInfoText_Intro ),
1111 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence ),
1112 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords ),
1113 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel ),
1114 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel ),
1115 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel ),
1116 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel ),
1117 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel ),
1118 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label ),
1119 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label ),
1120 CLI_OPTION_END()
1121 };
1122
1123 static void DNSServerCmd( void );
1124
1125 //===========================================================================================================================
1126 // Test Command Options
1127 //===========================================================================================================================
1128
1129 static const char * gGAIPerf_TestSuite = NULL;
1130 static int gGAIPerf_CallDelayMs = 10;
1131 static int gGAIPerf_ServerDelayMs = 10;
1132 static int gGAIPerf_DefaultIterCount = 100;
1133 static const char * gGAIPerf_OutputFilePath = NULL;
1134 static const char * gGAIPerf_OutputFormat = "json";
1135 static int gGAIPerf_OutputAppendNewLine = false;
1136
1137 static void GAIPerfCmd( void );
1138
1139 #define kGAIPerfSectionTitle_TestSuiteBasic "Test Suite \"Basic\""
1140 #define kGAIPerfSectionText_TestSuiteBasic \
1141 "This test suite consists of the following three test cases:\n" \
1142 "\n" \
1143 "Test Case #1: Resolve a domain name with\n" \
1144 "\n" \
1145 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1146 "\n" \
1147 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1148 "server queries.\n" \
1149 "\n" \
1150 "Test Case #2: Resolve a domain name with\n" \
1151 "\n" \
1152 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1153 "\n" \
1154 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1155 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1156 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1157 "\n" \
1158 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1159 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1160 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1161 "\n" \
1162 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1163
1164 #define kGAIPerfSectionTitle_TestSuiteAdvanced "Test Suite \"Advanced\""
1165 #define kGAIPerfSectionText_TestSuiteAdvanced \
1166 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1167 "\n" \
1168 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1169 "\n" \
1170 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1171 "\n" \
1172 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1173 "server queries.\n" \
1174 "\n" \
1175 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1176 "\n" \
1177 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1178 "\n" \
1179 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1180 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1181 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1182 "\n" \
1183 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1184 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1185 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1186 "\n" \
1187 "N_c and N_a take on the following values, depending on the value of N:\n" \
1188 "\n" \
1189 " N_c is 0 if N is in [1, 8].\n" \
1190 " N_c is 1 if N is in [9, 16].\n" \
1191 " N_c is 2 if N is in [17, 24].\n" \
1192 " N_c is 4 if N is in [25, 32].\n" \
1193 "\n" \
1194 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1195 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1196 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1197 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1198 "\n" \
1199 "Finally,\n" \
1200 "\n" \
1201 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1202
1203 static CLIOption kGAIPerfOpts[] =
1204 {
1205 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite, "name", "Name of the predefined test suite to run.", true,
1206 "\n"
1207 "There are currently two predefined test suites, 'basic' and 'advanced', which are described below.\n"
1208 "\n"
1209 ),
1210 StringOption( 'o', "output", &gGAIPerf_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1211 StringOptionEx( 'f', "format", &gGAIPerf_OutputFormat, "format", "Specifies the test results output format. (default: json)", false,
1212 "\n"
1213 "Use 'json' for JavaScript Object Notation (JSON).\n"
1214 "Use 'xml' for property list XML version 1.0.\n"
1215 "Use 'binary' for property list binary version 1.0.\n"
1216 "\n"
1217 ),
1218 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewLine, "If the output format is JSON, output a trailing newline character." ),
1219 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1220 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs, "ms", "Additional delay in milliseconds to have the test DNS server apply to responses. (default: 0)", false ),
1221 IntegerOption( 'i', "iterations", &gGAIPerf_DefaultIterCount, "count", "The default number of test case iterations. (default: 100)", false ),
1222
1223 CLI_SECTION( kGAIPerfSectionTitle_TestSuiteBasic, kGAIPerfSectionText_TestSuiteBasic ),
1224 CLI_SECTION( kGAIPerfSectionTitle_TestSuiteAdvanced, kGAIPerfSectionText_TestSuiteAdvanced ),
1225 CLI_OPTION_END()
1226 };
1227
1228 static CLIOption kTestOpts[] =
1229 {
1230 Command( "gaiperf", GAIPerfCmd, kGAIPerfOpts, "Run DNSServiceGetAddrInfo() performance tests.", false ),
1231 CLI_OPTION_END()
1232 };
1233
1234 //===========================================================================================================================
1235 // SSDP Command Options
1236 //===========================================================================================================================
1237
1238 static int gSSDPDiscover_MX = 1;
1239 static const char * gSSDPDiscover_ST = "ssdp:all";
1240 static int gSSDPDiscover_ReceiveSecs = 1;
1241 static int gSSDPDiscover_UseIPv4 = false;
1242 static int gSSDPDiscover_UseIPv6 = false;
1243 static int gSSDPDiscover_Verbose = false;
1244
1245 static CLIOption kSSDPDiscoverOpts[] =
1246 {
1247 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1248 IntegerOption( 'm', "mx", &gSSDPDiscover_MX, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1249 StringOption( 's', "st", &gSSDPDiscover_ST, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1250 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1251 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1252 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1253 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose, "Prints the search request(s) that were sent." ),
1254 CLI_OPTION_END()
1255 };
1256
1257 static void SSDPDiscoverCmd( void );
1258
1259 static CLIOption kSSDPOpts[] =
1260 {
1261 Command( "discover", SSDPDiscoverCmd, kSSDPDiscoverOpts, "Crafts and multicasts an SSDP search message.", false ),
1262 CLI_OPTION_END()
1263 };
1264
1265 #if( TARGET_OS_DARWIN )
1266 //===========================================================================================================================
1267 // res_query Command Options
1268 //===========================================================================================================================
1269
1270 static void ResQueryCmd( void );
1271
1272 static const char * gResQuery_Name = NULL;
1273 static const char * gResQuery_Type = NULL;
1274 static const char * gResQuery_Class = NULL;
1275 static int gResQuery_UseLibInfo = false;
1276
1277 static CLIOption kResQueryOpts[] =
1278 {
1279 StringOption( 'n', "name", &gResQuery_Name, "domain name", "Full domain name of record to query.", true ),
1280 StringOption( 't', "type", &gResQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1281 StringOption( 'c', "class", &gResQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1282 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo, "Use res_query from libinfo instead of libresolv." ),
1283 CLI_OPTION_END()
1284 };
1285
1286 //===========================================================================================================================
1287 // dns_query Command Options
1288 //===========================================================================================================================
1289
1290 static void ResolvDNSQueryCmd( void );
1291
1292 static const char * gResolvDNSQuery_Name = NULL;
1293 static const char * gResolvDNSQuery_Type = NULL;
1294 static const char * gResolvDNSQuery_Class = NULL;
1295 static const char * gResolvDNSQuery_Path = NULL;
1296
1297 static CLIOption kResolvDNSQueryOpts[] =
1298 {
1299 StringOption( 'n', "name", &gResolvDNSQuery_Name, "domain name", "Full domain name of record to query.", true ),
1300 StringOption( 't', "type", &gResolvDNSQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1301 StringOption( 'c', "class", &gResolvDNSQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1302 StringOption( 'p', "path", &gResolvDNSQuery_Path, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1303 CLI_OPTION_END()
1304 };
1305
1306 //===========================================================================================================================
1307 // CFHost Command Options
1308 //===========================================================================================================================
1309
1310 static void CFHostCmd( void );
1311
1312 static const char * gCFHost_Name = NULL;
1313 static int gCFHost_WaitSecs = 0;
1314
1315 static CLIOption kCFHostOpts[] =
1316 {
1317 StringOption( 'n', "name", &gCFHost_Name, "hostname", "Hostname to resolve.", true ),
1318 IntegerOption( 'w', "wait", &gCFHost_WaitSecs, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1319 CLI_OPTION_END()
1320 };
1321
1322 static CLIOption kLegacyOpts[] =
1323 {
1324 Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1325 Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ),
1326 Command( "cfhost", CFHostCmd, kCFHostOpts, "Uses CFHost to resolve a hostname.", true ),
1327 CLI_OPTION_END()
1328 };
1329
1330 //===========================================================================================================================
1331 // DNSConfigAdd Command Options
1332 //===========================================================================================================================
1333
1334 static void DNSConfigAddCmd( void );
1335
1336 static CFStringRef gDNSConfigAdd_ID = NULL;
1337 static char ** gDNSConfigAdd_IPAddrArray = NULL;
1338 static size_t gDNSConfigAdd_IPAddrCount = 0;
1339 static char ** gDNSConfigAdd_DomainArray = NULL;
1340 static size_t gDNSConfigAdd_DomainCount = 0;
1341 static const char * gDNSConfigAdd_Interface = NULL;
1342
1343 static CLIOption kDNSConfigAddOpts[] =
1344 {
1345 CFStringOption( 0 , "id", &gDNSConfigAdd_ID, "ID", "Arbitrary ID to use for resolver entry.", true ),
1346 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray, &gDNSConfigAdd_IPAddrCount, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1347 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray, &gDNSConfigAdd_DomainCount, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1348 StringOption( 'i', "interface", &gDNSConfigAdd_Interface, "interface name", "Specific interface for the resolver entry.", false ),
1349
1350 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1351 CLI_OPTION_END()
1352 };
1353
1354 //===========================================================================================================================
1355 // DNSConfigRemove Command Options
1356 //===========================================================================================================================
1357
1358 static void DNSConfigRemoveCmd( void );
1359
1360 static CFStringRef gDNSConfigRemove_ID = NULL;
1361
1362 static CLIOption kDNSConfigRemoveOpts[] =
1363 {
1364 CFStringOption( 0, "id", &gDNSConfigRemove_ID, "ID", "ID of resolver entry to remove.", true ),
1365
1366 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1367 CLI_OPTION_END()
1368 };
1369
1370 static CLIOption kDNSConfigOpts[] =
1371 {
1372 Command( "add", DNSConfigAddCmd, kDNSConfigAddOpts, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
1373 Command( "remove", DNSConfigRemoveCmd, kDNSConfigRemoveOpts, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
1374 CLI_OPTION_END()
1375 };
1376 #endif // TARGET_OS_DARWIN
1377
1378 //===========================================================================================================================
1379 // Command Table
1380 //===========================================================================================================================
1381
1382 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
1383
1384 static void BrowseCmd( void );
1385 static void GetAddrInfoCmd( void );
1386 static void QueryRecordCmd( void );
1387 static void RegisterCmd( void );
1388 static void RegisterRecordCmd( void );
1389 static void ResolveCmd( void );
1390 static void ReconfirmCmd( void );
1391 static void GetAddrInfoPOSIXCmd( void );
1392 static void ReverseLookupCmd( void );
1393 static void PortMappingCmd( void );
1394 static void BrowseAllCmd( void );
1395 static void GetAddrInfoStressCmd( void );
1396 static void DNSQueryCmd( void );
1397 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
1398 static void DNSCryptCmd( void );
1399 #endif
1400 static void MDNSQueryCmd( void );
1401 static void PIDToUUIDCmd( void );
1402 static void DaemonVersionCmd( void );
1403
1404 static CLIOption kGlobalOpts[] =
1405 {
1406 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
1407 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
1408 CLI_OPTION_HELP(),
1409
1410 // Common commands.
1411
1412 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
1413 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
1414 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
1415 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ),
1416 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ),
1417 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ),
1418 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
1419 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
1420 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
1421 Command( "portMapping", PortMappingCmd, kPortMappingOpts, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
1422 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
1423
1424 // Uncommon commands.
1425
1426 Command( "getnameinfo", GetNameInfoCmd, kGetNameInfoOpts, "Calls getnameinfo() and prints results.", true ),
1427 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
1428 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ),
1429 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
1430 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ),
1431 #endif
1432 Command( "mDNSQuery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ),
1433 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ),
1434 Command( "server", DNSServerCmd, kDNSServerOpts, "DNS server for testing.", true ),
1435 Command( "test", NULL, kTestOpts, "Commands for testing DNS-SD.", true ),
1436 Command( "ssdp", NULL, kSSDPOpts, "Commands for testing Simple Service Discovery Protocol (SSDP).", true ),
1437 #if( TARGET_OS_DARWIN )
1438 Command( "legacy", NULL, kLegacyOpts, "Commands for legacy non-DNS-SD API.", true ),
1439 Command( "dnsconfig", NULL, kDNSConfigOpts, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
1440 #endif
1441 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ),
1442
1443 CLI_COMMAND_HELP(),
1444 CLI_OPTION_END()
1445 };
1446
1447 //===========================================================================================================================
1448 // Helper Prototypes
1449 //===========================================================================================================================
1450
1451 #define kExitReason_OneShotDone "one-shot done"
1452 #define kExitReason_ReceivedResponse "received response"
1453 #define kExitReason_SIGINT "interrupt signal"
1454 #define kExitReason_Timeout "timeout"
1455 #define kExitReason_TimeLimit "time limit"
1456
1457 static void Exit( void *inContext ) ATTRIBUTE_NORETURN;
1458
1459 static int
1460 PrintFTimestampHandler(
1461 PrintFContext * inContext,
1462 PrintFFormat * inFormat,
1463 PrintFVAList * inArgs,
1464 void * inUserContext );
1465 static int
1466 PrintFDNSMessageHandler(
1467 PrintFContext * inContext,
1468 PrintFFormat * inFormat,
1469 PrintFVAList * inArgs,
1470 void * inUserContext );
1471
1472 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
1473
1474 typedef enum
1475 {
1476 kConnectionType_None = 0,
1477 kConnectionType_Normal = 1,
1478 kConnectionType_DelegatePID = 2,
1479 kConnectionType_DelegateUUID = 3
1480
1481 } ConnectionType;
1482
1483 typedef struct
1484 {
1485 ConnectionType type;
1486 union
1487 {
1488 int32_t pid;
1489 uint8_t uuid[ 16 ];
1490
1491 } delegate;
1492
1493 } ConnectionDesc;
1494
1495 static OSStatus
1496 CreateConnectionFromArgString(
1497 const char * inString,
1498 dispatch_queue_t inQueue,
1499 DNSServiceRef * outSDRef,
1500 ConnectionDesc * outDesc );
1501 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
1502 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
1503 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue );
1504 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue );
1505
1506 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
1507
1508 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
1509 static const char * RecordTypeToString( unsigned int inValue );
1510
1511 static OSStatus
1512 DNSMessageExtractDomainName(
1513 const uint8_t * inMsgPtr,
1514 size_t inMsgLen,
1515 const uint8_t * inNamePtr,
1516 uint8_t inBuf[ kDomainNameLengthMax ],
1517 const uint8_t ** outNextPtr );
1518 static OSStatus
1519 DNSMessageExtractDomainNameString(
1520 const void * inMsgPtr,
1521 size_t inMsgLen,
1522 const void * inNamePtr,
1523 char inBuf[ kDNSServiceMaxDomainName ],
1524 const uint8_t ** outNextPtr );
1525 static OSStatus
1526 DNSMessageExtractRecord(
1527 const uint8_t * inMsgPtr,
1528 size_t inMsgLen,
1529 const uint8_t * inPtr,
1530 uint8_t inNameBuf[ kDomainNameLengthMax ],
1531 uint16_t * outType,
1532 uint16_t * outClass,
1533 uint32_t * outTTL,
1534 const uint8_t ** outRDataPtr,
1535 size_t * outRDataLen,
1536 const uint8_t ** outPtr );
1537 static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr );
1538 static OSStatus
1539 DNSRecordDataToString(
1540 const void * inRDataPtr,
1541 size_t inRDataLen,
1542 unsigned int inRDataType,
1543 const void * inMsgPtr,
1544 size_t inMsgLen,
1545 char ** outString );
1546 static OSStatus
1547 DomainNameAppendString(
1548 uint8_t inDomainName[ kDomainNameLengthMax ],
1549 const char * inString,
1550 uint8_t ** outEndPtr );
1551 static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 );
1552 static size_t DomainNameLength( const uint8_t *inName );
1553 static OSStatus
1554 DomainNameFromString(
1555 uint8_t inDomainName[ kDomainNameLengthMax ],
1556 const char * inString,
1557 uint8_t ** outEndPtr );
1558 static OSStatus
1559 DomainNameToString(
1560 const uint8_t * inDomainName,
1561 const uint8_t * inEnd,
1562 char inBuf[ kDNSServiceMaxDomainName ],
1563 const uint8_t ** outNextPtr );
1564
1565 static OSStatus
1566 DNSMessageToText(
1567 const uint8_t * inMsgPtr,
1568 size_t inMsgLen,
1569 Boolean inIsMDNS,
1570 Boolean inPrintRaw,
1571 char ** outText );
1572
1573 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 )
1574
1575 static OSStatus
1576 WriteDNSQueryMessage(
1577 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
1578 uint16_t inMsgID,
1579 uint16_t inFlags,
1580 const char * inQName,
1581 uint16_t inQType,
1582 uint16_t inQClass,
1583 size_t * outMsgLen );
1584
1585 // Dispatch helpers
1586
1587 typedef void ( *DispatchHandler )( void *inContext );
1588
1589 static OSStatus
1590 DispatchSignalSourceCreate(
1591 int inSignal,
1592 DispatchHandler inEventHandler,
1593 void * inContext,
1594 dispatch_source_t * outSource );
1595 static OSStatus
1596 DispatchSocketSourceCreate(
1597 SocketRef inSock,
1598 dispatch_source_type_t inType,
1599 dispatch_queue_t inQueue,
1600 DispatchHandler inEventHandler,
1601 DispatchHandler inCancelHandler,
1602 void * inContext,
1603 dispatch_source_t * outSource );
1604
1605 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
1606 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
1607
1608 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
1609 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
1610
1611 static OSStatus
1612 DispatchTimerCreate(
1613 dispatch_time_t inStart,
1614 uint64_t inIntervalNs,
1615 uint64_t inLeewayNs,
1616 dispatch_queue_t inQueue,
1617 DispatchHandler inEventHandler,
1618 DispatchHandler inCancelHandler,
1619 void * inContext,
1620 dispatch_source_t * outTimer );
1621 static OSStatus
1622 DispatchProcessMonitorCreate(
1623 pid_t inPID,
1624 unsigned long inFlags,
1625 dispatch_queue_t inQueue,
1626 DispatchHandler inEventHandler,
1627 DispatchHandler inCancelHandler,
1628 void * inContext,
1629 dispatch_source_t * outMonitor );
1630
1631 static const char * ServiceTypeDescription( const char *inName );
1632
1633 typedef struct
1634 {
1635 SocketRef sock; // Socket.
1636 void * userContext; // User context.
1637 int32_t refCount; // Reference count.
1638
1639 } SocketContext;
1640
1641 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext );
1642 static SocketContext * SocketContextRetain( SocketContext *inContext );
1643 static void SocketContextRelease( SocketContext *inContext );
1644 static void SocketContextCancelHandler( void *inContext );
1645
1646 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
1647
1648 static OSStatus StringToInt32( const char *inString, int32_t *outValue );
1649 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue );
1650 static OSStatus StringToLongLong( const char *inString, long long *outValue );
1651 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
1652 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
1653 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen );
1654 #if( TARGET_OS_DARWIN )
1655 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr );
1656 #endif
1657 static OSStatus
1658 _ServerSocketOpenEx2(
1659 int inFamily,
1660 int inType,
1661 int inProtocol,
1662 const void * inAddr,
1663 int inPort,
1664 int * outPort,
1665 int inRcvBufSize,
1666 Boolean inNoPortReuse,
1667 SocketRef * outSock );
1668
1669 typedef uint64_t MicroTime64;
1670
1671 static MicroTime64 GetCurrentMicroTime( void ); // Gets the number of milliseconds since 1970-01-01T00:00:00Z
1672
1673 #define AddRmvString( X ) ( ( (X) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
1674 #define Unused( X ) (void)(X)
1675
1676 //===========================================================================================================================
1677 // main
1678 //===========================================================================================================================
1679
1680 int main( int argc, const char **argv )
1681 {
1682 OSStatus err;
1683
1684 // Route DebugServices logging output to stderr.
1685
1686 dlog_control( "DebugServices:output=file;stderr" );
1687
1688 PrintFRegisterExtension( "du:time", PrintFTimestampHandler, NULL );
1689 PrintFRegisterExtension( "du:dnsmsg", PrintFDNSMessageHandler, NULL );
1690 CLIInit( argc, argv );
1691 err = CLIParse( kGlobalOpts, kCLIFlags_None );
1692 if( err ) exit( 1 );
1693
1694 return( gExitCode );
1695 }
1696
1697 //===========================================================================================================================
1698 // VersionOptionCallback
1699 //===========================================================================================================================
1700
1701 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
1702 {
1703 const char * srcVers;
1704 #if( MDNSRESPONDER_PROJECT )
1705 char srcStr[ 16 ];
1706 #endif
1707
1708 Unused( inOption );
1709 Unused( inArg );
1710 Unused( inUnset );
1711
1712 #if( MDNSRESPONDER_PROJECT )
1713 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
1714 #else
1715 srcVers = DNSSDUTIL_SOURCE_VERSION;
1716 #endif
1717 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
1718
1719 return( kEndingErr );
1720 }
1721
1722 //===========================================================================================================================
1723 // BrowseCmd
1724 //===========================================================================================================================
1725
1726 typedef struct BrowseResolveOp BrowseResolveOp;
1727
1728 struct BrowseResolveOp
1729 {
1730 BrowseResolveOp * next; // Next resolve operation in list.
1731 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
1732 char * fullName; // Full name of the service to resolve.
1733 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
1734 };
1735
1736 typedef struct
1737 {
1738 DNSServiceRef mainRef; // Main sdRef for shared connection.
1739 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions.
1740 size_t opRefsCount; // Count of array of sdRefs for non-shared connections.
1741 const char * domain; // Domain for DNSServiceBrowse operation(s).
1742 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s).
1743 char ** serviceTypes; // Array of service types to browse for.
1744 size_t serviceTypesCount; // Count of array of service types to browse for.
1745 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds.
1746 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations.
1747 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s).
1748 Boolean printedHeader; // True if results header has been printed.
1749 Boolean doResolve; // True if service instances are to be resolved.
1750 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried.
1751
1752 } BrowseContext;
1753
1754 static void BrowsePrintPrologue( const BrowseContext *inContext );
1755 static void BrowseContextFree( BrowseContext *inContext );
1756 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
1757 static void BrowseResolveOpFree( BrowseResolveOp *inOp );
1758 static void DNSSD_API
1759 BrowseCallback(
1760 DNSServiceRef inSDRef,
1761 DNSServiceFlags inFlags,
1762 uint32_t inInterfaceIndex,
1763 DNSServiceErrorType inError,
1764 const char * inName,
1765 const char * inRegType,
1766 const char * inDomain,
1767 void * inContext );
1768 static void DNSSD_API
1769 BrowseResolveCallback(
1770 DNSServiceRef inSDRef,
1771 DNSServiceFlags inFlags,
1772 uint32_t inInterfaceIndex,
1773 DNSServiceErrorType inError,
1774 const char * inFullName,
1775 const char * inHostname,
1776 uint16_t inPort,
1777 uint16_t inTXTLen,
1778 const unsigned char * inTXTPtr,
1779 void * inContext );
1780 static void DNSSD_API
1781 BrowseQueryRecordCallback(
1782 DNSServiceRef inSDRef,
1783 DNSServiceFlags inFlags,
1784 uint32_t inInterfaceIndex,
1785 DNSServiceErrorType inError,
1786 const char * inFullName,
1787 uint16_t inType,
1788 uint16_t inClass,
1789 uint16_t inRDataLen,
1790 const void * inRDataPtr,
1791 uint32_t inTTL,
1792 void * inContext );
1793
1794 static void BrowseCmd( void )
1795 {
1796 OSStatus err;
1797 size_t i;
1798 BrowseContext * context = NULL;
1799 dispatch_source_t signalSource = NULL;
1800 int useMainConnection;
1801
1802 // Set up SIGINT handler.
1803
1804 signal( SIGINT, SIG_IGN );
1805 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
1806 require_noerr( err, exit );
1807 dispatch_resume( signalSource );
1808
1809 // Create context.
1810
1811 context = (BrowseContext *) calloc( 1, sizeof( *context ) );
1812 require_action( context, exit, err = kNoMemoryErr );
1813
1814 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
1815 require_action( context->opRefs, exit, err = kNoMemoryErr );
1816 context->opRefsCount = gBrowse_ServiceTypesCount;
1817
1818 // Check command parameters.
1819
1820 if( gBrowse_TimeLimitSecs < 0 )
1821 {
1822 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
1823 err = kParamErr;
1824 goto exit;
1825 }
1826
1827 // Create main connection.
1828
1829 if( gConnectionOpt )
1830 {
1831 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
1832 require_noerr_quiet( err, exit );
1833 useMainConnection = true;
1834 }
1835 else
1836 {
1837 useMainConnection = false;
1838 }
1839
1840 // Get flags.
1841
1842 context->flags = GetDNSSDFlagsFromOpts();
1843 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
1844
1845 // Get interface.
1846
1847 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
1848 require_noerr_quiet( err, exit );
1849
1850 // Set remaining parameters.
1851
1852 context->serviceTypes = gBrowse_ServiceTypes;
1853 context->serviceTypesCount = gBrowse_ServiceTypesCount;
1854 context->domain = gBrowse_Domain;
1855 context->doResolve = gBrowse_DoResolve ? true : false;
1856 context->timeLimitSecs = gBrowse_TimeLimitSecs;
1857 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false;
1858
1859 // Print prologue.
1860
1861 BrowsePrintPrologue( context );
1862
1863 // Start operation(s).
1864
1865 for( i = 0; i < context->serviceTypesCount; ++i )
1866 {
1867 DNSServiceRef sdRef;
1868
1869 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
1870 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
1871 BrowseCallback, context );
1872 require_noerr( err, exit );
1873
1874 context->opRefs[ i ] = sdRef;
1875 if( !useMainConnection )
1876 {
1877 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
1878 require_noerr( err, exit );
1879 }
1880 }
1881
1882 // Set time limit.
1883
1884 if( context->timeLimitSecs > 0 )
1885 {
1886 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
1887 kExitReason_TimeLimit, Exit );
1888 }
1889 dispatch_main();
1890
1891 exit:
1892 dispatch_source_forget( &signalSource );
1893 if( context ) BrowseContextFree( context );
1894 if( err ) exit( 1 );
1895 }
1896
1897 //===========================================================================================================================
1898 // BrowsePrintPrologue
1899 //===========================================================================================================================
1900
1901 static void BrowsePrintPrologue( const BrowseContext *inContext )
1902 {
1903 const int timeLimitSecs = inContext->timeLimitSecs;
1904 const char * const * ptr = (const char **) inContext->serviceTypes;
1905 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
1906 char ifName[ kInterfaceNameBufLen ];
1907
1908 InterfaceIndexToName( inContext->ifIndex, ifName );
1909
1910 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
1911 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
1912 FPrintF( stdout, "Service types: %s", *ptr++ );
1913 while( ptr < end ) FPrintF( stdout, ", %s", *ptr++ );
1914 FPrintF( stdout, "\n" );
1915 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
1916 FPrintF( stdout, "Time limit: " );
1917 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
1918 else FPrintF( stdout, "∞\n" );
1919 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
1920 FPrintF( stdout, "---\n" );
1921 }
1922
1923 //===========================================================================================================================
1924 // BrowseContextFree
1925 //===========================================================================================================================
1926
1927 static void BrowseContextFree( BrowseContext *inContext )
1928 {
1929 size_t i;
1930
1931 for( i = 0; i < inContext->opRefsCount; ++i )
1932 {
1933 DNSServiceForget( &inContext->opRefs[ i ] );
1934 }
1935 if( inContext->serviceTypes )
1936 {
1937 StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
1938 inContext->serviceTypes = NULL;
1939 inContext->serviceTypesCount = 0;
1940 }
1941 DNSServiceForget( &inContext->mainRef );
1942 free( inContext );
1943 }
1944
1945 //===========================================================================================================================
1946 // BrowseResolveOpCreate
1947 //===========================================================================================================================
1948
1949 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
1950 {
1951 OSStatus err;
1952 BrowseResolveOp * resolveOp;
1953
1954 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
1955 require_action( resolveOp, exit, err = kNoMemoryErr );
1956
1957 resolveOp->fullName = strdup( inFullName );
1958 require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
1959
1960 resolveOp->interfaceIndex = inInterfaceIndex;
1961
1962 *outOp = resolveOp;
1963 resolveOp = NULL;
1964 err = kNoErr;
1965
1966 exit:
1967 if( resolveOp ) BrowseResolveOpFree( resolveOp );
1968 return( err );
1969 }
1970
1971 //===========================================================================================================================
1972 // BrowseResolveOpFree
1973 //===========================================================================================================================
1974
1975 static void BrowseResolveOpFree( BrowseResolveOp *inOp )
1976 {
1977 DNSServiceForget( &inOp->sdRef );
1978 ForgetMem( &inOp->fullName );
1979 free( inOp );
1980 }
1981
1982 //===========================================================================================================================
1983 // BrowseCallback
1984 //===========================================================================================================================
1985
1986 static void DNSSD_API
1987 BrowseCallback(
1988 DNSServiceRef inSDRef,
1989 DNSServiceFlags inFlags,
1990 uint32_t inInterfaceIndex,
1991 DNSServiceErrorType inError,
1992 const char * inName,
1993 const char * inRegType,
1994 const char * inDomain,
1995 void * inContext )
1996 {
1997 BrowseContext * const context = (BrowseContext *) inContext;
1998 OSStatus err;
1999 BrowseResolveOp * newOp = NULL;
2000 BrowseResolveOp ** p;
2001 char fullName[ kDNSServiceMaxDomainName ];
2002 struct timeval now;
2003
2004 Unused( inSDRef );
2005
2006 gettimeofday( &now, NULL );
2007
2008 err = inError;
2009 require_noerr( err, exit );
2010
2011 if( !context->printedHeader )
2012 {
2013 FPrintF( stdout, "%-26s A/R Flags IF %-20s %-20s Instance Name\n", "Timestamp", "Domain", "Service Type" );
2014 context->printedHeader = true;
2015 }
2016 FPrintF( stdout, "%{du:time} %-3s %5X %2d %-20s %-20s %s\n",
2017 &now, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
2018
2019 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
2020
2021 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
2022 require_noerr( err, exit );
2023
2024 if( inFlags & kDNSServiceFlagsAdd )
2025 {
2026 DNSServiceRef sdRef;
2027 DNSServiceFlags flags;
2028
2029 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
2030 require_noerr( err, exit );
2031
2032 if( context->mainRef )
2033 {
2034 sdRef = context->mainRef;
2035 flags = kDNSServiceFlagsShareConnection;
2036 }
2037 else
2038 {
2039 flags = 0;
2040 }
2041 if( context->doResolve )
2042 {
2043 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
2044 NULL );
2045 require_noerr( err, exit );
2046 }
2047 else
2048 {
2049 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
2050 BrowseQueryRecordCallback, NULL );
2051 require_noerr( err, exit );
2052 }
2053
2054 newOp->sdRef = sdRef;
2055 if( !context->mainRef )
2056 {
2057 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
2058 require_noerr( err, exit );
2059 }
2060 for( p = &context->resolveList; *p; p = &( *p )->next ) {}
2061 *p = newOp;
2062 newOp = NULL;
2063 }
2064 else
2065 {
2066 BrowseResolveOp * resolveOp;
2067
2068 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
2069 {
2070 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
2071 {
2072 break;
2073 }
2074 }
2075 if( resolveOp )
2076 {
2077 *p = resolveOp->next;
2078 BrowseResolveOpFree( resolveOp );
2079 }
2080 }
2081
2082 exit:
2083 if( newOp ) BrowseResolveOpFree( newOp );
2084 if( err ) exit( 1 );
2085 }
2086
2087 //===========================================================================================================================
2088 // BrowseQueryRecordCallback
2089 //===========================================================================================================================
2090
2091 static void DNSSD_API
2092 BrowseQueryRecordCallback(
2093 DNSServiceRef inSDRef,
2094 DNSServiceFlags inFlags,
2095 uint32_t inInterfaceIndex,
2096 DNSServiceErrorType inError,
2097 const char * inFullName,
2098 uint16_t inType,
2099 uint16_t inClass,
2100 uint16_t inRDataLen,
2101 const void * inRDataPtr,
2102 uint32_t inTTL,
2103 void * inContext )
2104 {
2105 OSStatus err;
2106 struct timeval now;
2107
2108 Unused( inSDRef );
2109 Unused( inClass );
2110 Unused( inTTL );
2111 Unused( inContext );
2112
2113 gettimeofday( &now, NULL );
2114
2115 err = inError;
2116 require_noerr( err, exit );
2117 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
2118
2119 FPrintF( stdout, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2120 &now, AddRmvString( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr, (size_t) inRDataLen );
2121
2122 exit:
2123 if( err ) exit( 1 );
2124 }
2125
2126 //===========================================================================================================================
2127 // BrowseResolveCallback
2128 //===========================================================================================================================
2129
2130 static void DNSSD_API
2131 BrowseResolveCallback(
2132 DNSServiceRef inSDRef,
2133 DNSServiceFlags inFlags,
2134 uint32_t inInterfaceIndex,
2135 DNSServiceErrorType inError,
2136 const char * inFullName,
2137 const char * inHostname,
2138 uint16_t inPort,
2139 uint16_t inTXTLen,
2140 const unsigned char * inTXTPtr,
2141 void * inContext )
2142 {
2143 struct timeval now;
2144 char errorStr[ 64 ];
2145
2146 Unused( inSDRef );
2147 Unused( inFlags );
2148 Unused( inContext );
2149
2150 gettimeofday( &now, NULL );
2151
2152 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
2153
2154 FPrintF( stdout, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
2155 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
2156 if( inTXTLen == 1 )
2157 {
2158 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
2159 }
2160 else
2161 {
2162 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
2163 }
2164 }
2165
2166 //===========================================================================================================================
2167 // GetAddrInfoCmd
2168 //===========================================================================================================================
2169
2170 typedef struct
2171 {
2172 DNSServiceRef mainRef; // Main sdRef for shared connection.
2173 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
2174 const char * name; // Hostname to resolve.
2175 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
2176 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
2177 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
2178 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
2179 Boolean printedHeader; // True if the results header has been printed.
2180 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
2181 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed.
2182 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed.
2183
2184 } GetAddrInfoContext;
2185
2186 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
2187 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext );
2188 static void DNSSD_API
2189 GetAddrInfoCallback(
2190 DNSServiceRef inSDRef,
2191 DNSServiceFlags inFlags,
2192 uint32_t inInterfaceIndex,
2193 DNSServiceErrorType inError,
2194 const char * inHostname,
2195 const struct sockaddr * inSockAddr,
2196 uint32_t inTTL,
2197 void * inContext );
2198
2199 static void GetAddrInfoCmd( void )
2200 {
2201 OSStatus err;
2202 DNSServiceRef sdRef;
2203 GetAddrInfoContext * context = NULL;
2204 dispatch_source_t signalSource = NULL;
2205 int useMainConnection;
2206
2207 // Set up SIGINT handler.
2208
2209 signal( SIGINT, SIG_IGN );
2210 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2211 require_noerr( err, exit );
2212 dispatch_resume( signalSource );
2213
2214 // Check command parameters.
2215
2216 if( gGetAddrInfo_TimeLimitSecs < 0 )
2217 {
2218 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
2219 err = kParamErr;
2220 goto exit;
2221 }
2222
2223 // Create context.
2224
2225 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
2226 require_action( context, exit, err = kNoMemoryErr );
2227
2228 // Create main connection.
2229
2230 if( gConnectionOpt )
2231 {
2232 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2233 require_noerr_quiet( err, exit );
2234 useMainConnection = true;
2235 }
2236 else
2237 {
2238 useMainConnection = false;
2239 }
2240
2241 // Get flags.
2242
2243 context->flags = GetDNSSDFlagsFromOpts();
2244 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2245
2246 // Get interface.
2247
2248 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2249 require_noerr_quiet( err, exit );
2250
2251 // Set remaining parameters.
2252
2253 context->name = gGetAddrInfo_Name;
2254 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs;
2255 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
2256 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
2257 if( gGetAddrInfo_OneShot )
2258 {
2259 context->oneShotMode = true;
2260 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
2261 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
2262 }
2263
2264 // Print prologue.
2265
2266 GetAddrInfoPrintPrologue( context );
2267
2268 // Start operation.
2269
2270 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
2271 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
2272 GetAddrInfoCallback, context );
2273 require_noerr( err, exit );
2274
2275 context->opRef = sdRef;
2276 if( !useMainConnection )
2277 {
2278 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
2279 require_noerr( err, exit );
2280 }
2281
2282 // Set time limit.
2283
2284 if( context->timeLimitSecs > 0 )
2285 {
2286 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
2287 kExitReason_TimeLimit, Exit );
2288 }
2289 dispatch_main();
2290
2291 exit:
2292 dispatch_source_forget( &signalSource );
2293 if( context ) GetAddrInfoContextFree( context );
2294 if( err ) exit( 1 );
2295 }
2296
2297 //===========================================================================================================================
2298 // GetAddrInfoPrintPrologue
2299 //===========================================================================================================================
2300
2301 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
2302 {
2303 const int timeLimitSecs = inContext->timeLimitSecs;
2304 char ifName[ kInterfaceNameBufLen ];
2305
2306 InterfaceIndexToName( inContext->ifIndex, ifName );
2307
2308 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2309 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2310 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
2311 FPrintF( stdout, "Name: %s\n", inContext->name );
2312 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
2313 FPrintF( stdout, "Time limit: " );
2314 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2315 else FPrintF( stdout, "∞\n" );
2316 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
2317 FPrintF( stdout, "---\n" );
2318 }
2319
2320 //===========================================================================================================================
2321 // GetAddrInfoContextFree
2322 //===========================================================================================================================
2323
2324 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext )
2325 {
2326 DNSServiceForget( &inContext->opRef );
2327 DNSServiceForget( &inContext->mainRef );
2328 free( inContext );
2329 }
2330
2331 //===========================================================================================================================
2332 // GetAddrInfoCallback
2333 //===========================================================================================================================
2334
2335 static void DNSSD_API
2336 GetAddrInfoCallback(
2337 DNSServiceRef inSDRef,
2338 DNSServiceFlags inFlags,
2339 uint32_t inInterfaceIndex,
2340 DNSServiceErrorType inError,
2341 const char * inHostname,
2342 const struct sockaddr * inSockAddr,
2343 uint32_t inTTL,
2344 void * inContext )
2345 {
2346 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext;
2347 struct timeval now;
2348 OSStatus err;
2349 const char * addrStr;
2350 char addrStrBuf[ kSockAddrStringMaxSize ];
2351
2352 Unused( inSDRef );
2353
2354 gettimeofday( &now, NULL );
2355
2356 switch( inError )
2357 {
2358 case kDNSServiceErr_NoError:
2359 case kDNSServiceErr_NoSuchRecord:
2360 err = kNoErr;
2361 break;
2362
2363 case kDNSServiceErr_Timeout:
2364 Exit( kExitReason_Timeout );
2365
2366 default:
2367 err = inError;
2368 goto exit;
2369 }
2370
2371 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
2372 {
2373 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
2374 err = kTypeErr;
2375 goto exit;
2376 }
2377
2378 if( !inError )
2379 {
2380 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
2381 require_noerr( err, exit );
2382 addrStr = addrStrBuf;
2383 }
2384 else
2385 {
2386 addrStr = ( inSockAddr->sa_family == AF_INET ) ? "No Such Record (A)" : "No Such Record (AAAA)";
2387 }
2388
2389 if( !context->printedHeader )
2390 {
2391 FPrintF( stdout, "%-26s A/R Flags IF %-32s %-38s %6s\n", "Timestamp", "Hostname", "Address", "TTL" );
2392 context->printedHeader = true;
2393 }
2394 FPrintF( stdout, "%{du:time} %s %5X %2d %-32s %-38s %6u\n",
2395 &now, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
2396
2397 if( context->oneShotMode )
2398 {
2399 if( inFlags & kDNSServiceFlagsAdd )
2400 {
2401 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false;
2402 else context->needIPv6 = false;
2403 }
2404 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
2405 {
2406 Exit( kExitReason_OneShotDone );
2407 }
2408 }
2409
2410 exit:
2411 if( err ) exit( 1 );
2412 }
2413
2414 //===========================================================================================================================
2415 // QueryRecordCmd
2416 //===========================================================================================================================
2417
2418 typedef struct
2419 {
2420 DNSServiceRef mainRef; // Main sdRef for shared connection.
2421 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation.
2422 const char * recordName; // Resource record name argument for DNSServiceQueryRecord().
2423 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord().
2424 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord().
2425 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds.
2426 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord().
2427 Boolean printedHeader; // True if the results header was printed.
2428 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
2429 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type.
2430 Boolean printRawRData; // True if RDATA results are not to be formatted when printed.
2431
2432 } QueryRecordContext;
2433
2434 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext );
2435 static void QueryRecordContextFree( QueryRecordContext *inContext );
2436 static void DNSSD_API
2437 QueryRecordCallback(
2438 DNSServiceRef inSDRef,
2439 DNSServiceFlags inFlags,
2440 uint32_t inInterfaceIndex,
2441 DNSServiceErrorType inError,
2442 const char * inFullName,
2443 uint16_t inType,
2444 uint16_t inClass,
2445 uint16_t inRDataLen,
2446 const void * inRDataPtr,
2447 uint32_t inTTL,
2448 void * inContext );
2449
2450 static void QueryRecordCmd( void )
2451 {
2452 OSStatus err;
2453 DNSServiceRef sdRef;
2454 QueryRecordContext * context = NULL;
2455 dispatch_source_t signalSource = NULL;
2456 int useMainConnection;
2457
2458 // Set up SIGINT handler.
2459
2460 signal( SIGINT, SIG_IGN );
2461 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2462 require_noerr( err, exit );
2463 dispatch_resume( signalSource );
2464
2465 // Create context.
2466
2467 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
2468 require_action( context, exit, err = kNoMemoryErr );
2469
2470 // Check command parameters.
2471
2472 if( gQueryRecord_TimeLimitSecs < 0 )
2473 {
2474 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
2475 err = kParamErr;
2476 goto exit;
2477 }
2478
2479 // Create main connection.
2480
2481 if( gConnectionOpt )
2482 {
2483 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2484 require_noerr_quiet( err, exit );
2485 useMainConnection = true;
2486 }
2487 else
2488 {
2489 useMainConnection = false;
2490 }
2491
2492 // Get flags.
2493
2494 context->flags = GetDNSSDFlagsFromOpts();
2495 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2496
2497 // Get interface.
2498
2499 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2500 require_noerr_quiet( err, exit );
2501
2502 // Get record type.
2503
2504 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
2505 require_noerr( err, exit );
2506
2507 // Set remaining parameters.
2508
2509 context->recordName = gQueryRecord_Name;
2510 context->timeLimitSecs = gQueryRecord_TimeLimitSecs;
2511 context->oneShotMode = gQueryRecord_OneShot ? true : false;
2512 context->printRawRData = gQueryRecord_RawRData ? true : false;
2513
2514 // Print prologue.
2515
2516 QueryRecordPrintPrologue( context );
2517
2518 // Start operation.
2519
2520 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
2521 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
2522 kDNSServiceClass_IN, QueryRecordCallback, context );
2523 require_noerr( err, exit );
2524
2525 context->opRef = sdRef;
2526 if( !useMainConnection )
2527 {
2528 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
2529 require_noerr( err, exit );
2530 }
2531
2532 // Set time limit.
2533
2534 if( context->timeLimitSecs > 0 )
2535 {
2536 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
2537 Exit );
2538 }
2539 dispatch_main();
2540
2541 exit:
2542 dispatch_source_forget( &signalSource );
2543 if( context ) QueryRecordContextFree( context );
2544 if( err ) exit( 1 );
2545 }
2546
2547 //===========================================================================================================================
2548 // QueryRecordContextFree
2549 //===========================================================================================================================
2550
2551 static void QueryRecordContextFree( QueryRecordContext *inContext )
2552 {
2553 DNSServiceForget( &inContext->opRef );
2554 DNSServiceForget( &inContext->mainRef );
2555 free( inContext );
2556 }
2557
2558 //===========================================================================================================================
2559 // QueryRecordPrintPrologue
2560 //===========================================================================================================================
2561
2562 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext )
2563 {
2564 const int timeLimitSecs = inContext->timeLimitSecs;
2565 char ifName[ kInterfaceNameBufLen ];
2566
2567 InterfaceIndexToName( inContext->ifIndex, ifName );
2568
2569 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2570 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2571 FPrintF( stdout, "Name: %s\n", inContext->recordName );
2572 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
2573 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
2574 FPrintF( stdout, "Time limit: " );
2575 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2576 else FPrintF( stdout, "∞\n" );
2577 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
2578 FPrintF( stdout, "---\n" );
2579
2580 }
2581
2582 //===========================================================================================================================
2583 // QueryRecordCallback
2584 //===========================================================================================================================
2585
2586 static void DNSSD_API
2587 QueryRecordCallback(
2588 DNSServiceRef inSDRef,
2589 DNSServiceFlags inFlags,
2590 uint32_t inInterfaceIndex,
2591 DNSServiceErrorType inError,
2592 const char * inFullName,
2593 uint16_t inType,
2594 uint16_t inClass,
2595 uint16_t inRDataLen,
2596 const void * inRDataPtr,
2597 uint32_t inTTL,
2598 void * inContext )
2599 {
2600 QueryRecordContext * const context = (QueryRecordContext *) inContext;
2601 struct timeval now;
2602 OSStatus err;
2603 char * rdataStr = NULL;
2604
2605 Unused( inSDRef );
2606
2607 gettimeofday( &now, NULL );
2608
2609 switch( inError )
2610 {
2611 case kDNSServiceErr_NoError:
2612 case kDNSServiceErr_NoSuchRecord:
2613 err = kNoErr;
2614 break;
2615
2616 case kDNSServiceErr_Timeout:
2617 Exit( kExitReason_Timeout );
2618
2619 default:
2620 err = inError;
2621 goto exit;
2622 }
2623
2624 if( inError == kDNSServiceErr_NoSuchRecord )
2625 {
2626 ASPrintF( &rdataStr, "No Such Record" );
2627 }
2628 else
2629 {
2630 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
2631 if( !rdataStr )
2632 {
2633 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, INT_MAX );
2634 require_action( rdataStr, exit, err = kNoMemoryErr );
2635 }
2636 }
2637
2638 if( !context->printedHeader )
2639 {
2640 FPrintF( stdout, "%-26s A/R Flags IF %-32s %-5s %-5s %6s RData\n", "Timestamp", "Name", "Type", "Class", "TTL" );
2641 context->printedHeader = true;
2642 }
2643 FPrintF( stdout, "%{du:time} %-3s %5X %2d %-32s %-5s %?-5s%?5u %6u %s\n",
2644 &now, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
2645 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL, rdataStr );
2646
2647 if( context->oneShotMode )
2648 {
2649 if( ( inFlags & kDNSServiceFlagsAdd ) &&
2650 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
2651 {
2652 context->gotRecord = true;
2653 }
2654 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
2655 }
2656
2657 exit:
2658 FreeNullSafe( rdataStr );
2659 if( err ) exit( 1 );
2660 }
2661
2662 //===========================================================================================================================
2663 // RegisterCmd
2664 //===========================================================================================================================
2665
2666 typedef struct
2667 {
2668 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord().
2669 uint8_t * dataPtr; // Record data.
2670 size_t dataLen; // Record data length.
2671 uint32_t ttl; // Record TTL value.
2672 uint16_t type; // Record type.
2673
2674 } ExtraRecord;
2675
2676 typedef struct
2677 {
2678 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation.
2679 const char * name; // Service name argument for DNSServiceRegister().
2680 const char * type; // Service type argument for DNSServiceRegister().
2681 const char * domain; // Domain in which advertise the service.
2682 uint8_t * txtPtr; // Service TXT record data. (malloc'd)
2683 size_t txtLen; // Service TXT record data len.
2684 ExtraRecord * extraRecords; // Array of extra records to add to registered service.
2685 size_t extraRecordsCount; // Number of extra records.
2686 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd)
2687 size_t updateTXTLen; // Length of record data for TXT record update.
2688 uint32_t updateTTL; // TTL of updated TXT record.
2689 int updateDelayMs; // Post-registration TXT record update delay in milliseconds.
2690 DNSServiceFlags flags; // Flags argument for DNSServiceRegister().
2691 uint32_t ifIndex; // Interface index argument for DNSServiceRegister().
2692 int lifetimeMs; // Lifetime of the record registration in milliseconds.
2693 uint16_t port; // Service instance's port number.
2694 Boolean printedHeader; // True if results header was printed.
2695 Boolean didRegister; // True if service was registered.
2696
2697 } RegisterContext;
2698
2699 static void RegisterPrintPrologue( const RegisterContext *inContext );
2700 static void RegisterContextFree( RegisterContext *inContext );
2701 static void DNSSD_API
2702 RegisterCallback(
2703 DNSServiceRef inSDRef,
2704 DNSServiceFlags inFlags,
2705 DNSServiceErrorType inError,
2706 const char * inName,
2707 const char * inType,
2708 const char * inDomain,
2709 void * inContext );
2710 static void RegisterUpdate( void *inContext );
2711
2712 static void RegisterCmd( void )
2713 {
2714 OSStatus err;
2715 RegisterContext * context = NULL;
2716 dispatch_source_t signalSource = NULL;
2717
2718 // Set up SIGINT handler.
2719
2720 signal( SIGINT, SIG_IGN );
2721 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2722 require_noerr( err, exit );
2723 dispatch_resume( signalSource );
2724
2725 // Create context.
2726
2727 context = (RegisterContext *) calloc( 1, sizeof( *context ) );
2728 require_action( context, exit, err = kNoMemoryErr );
2729
2730 // Check command parameters.
2731
2732 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
2733 {
2734 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
2735 err = kParamErr;
2736 goto exit;
2737 }
2738
2739 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
2740 {
2741 FPrintF( stderr, "There are missing additional record parameters.\n" );
2742 err = kParamErr;
2743 goto exit;
2744 }
2745
2746 // Get flags.
2747
2748 context->flags = GetDNSSDFlagsFromOpts();
2749
2750 // Get interface index.
2751
2752 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2753 require_noerr_quiet( err, exit );
2754
2755 // Get TXT record data.
2756
2757 if( gRegister_TXT )
2758 {
2759 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
2760 require_noerr_quiet( err, exit );
2761 }
2762
2763 // Set remaining parameters.
2764
2765 context->name = gRegister_Name;
2766 context->type = gRegister_Type;
2767 context->domain = gRegister_Domain;
2768 context->port = (uint16_t) gRegister_Port;
2769 context->lifetimeMs = gRegister_LifetimeMs;
2770
2771 if( gAddRecord_TypesCount > 0 )
2772 {
2773 size_t i;
2774
2775 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
2776 require_action( context, exit, err = kNoMemoryErr );
2777 context->extraRecordsCount = gAddRecord_TypesCount;
2778
2779 for( i = 0; i < gAddRecord_TypesCount; ++i )
2780 {
2781 ExtraRecord * const extraRecord = &context->extraRecords[ i ];
2782
2783 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
2784 require_noerr( err, exit );
2785
2786 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
2787 if( err )
2788 {
2789 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
2790 err = kParamErr;
2791 goto exit;
2792 }
2793
2794 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
2795 require_noerr_quiet( err, exit );
2796 }
2797 }
2798
2799 if( gUpdateRecord_Data )
2800 {
2801 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
2802 require_noerr_quiet( err, exit );
2803
2804 context->updateTTL = (uint32_t) gUpdateRecord_TTL;
2805 context->updateDelayMs = gUpdateRecord_DelayMs;
2806 }
2807
2808 // Print prologue.
2809
2810 RegisterPrintPrologue( context );
2811
2812 // Start operation.
2813
2814 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
2815 context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr,
2816 RegisterCallback, context );
2817 ForgetMem( &context->txtPtr );
2818 require_noerr( err, exit );
2819
2820 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
2821 require_noerr( err, exit );
2822
2823 dispatch_main();
2824
2825 exit:
2826 dispatch_source_forget( &signalSource );
2827 if( context ) RegisterContextFree( context );
2828 if( err ) exit( 1 );
2829 }
2830
2831 //===========================================================================================================================
2832 // RegisterPrintPrologue
2833 //===========================================================================================================================
2834
2835 static void RegisterPrintPrologue( const RegisterContext *inContext )
2836 {
2837 size_t i;
2838 int infinite;
2839 char ifName[ kInterfaceNameBufLen ];
2840
2841 InterfaceIndexToName( inContext->ifIndex, ifName );
2842
2843 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2844 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2845 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" );
2846 FPrintF( stdout, "Type: %s\n", inContext->type );
2847 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
2848 FPrintF( stdout, "Port: %u\n", inContext->port );
2849 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen );
2850 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
2851 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
2852 if( inContext->updateTXTPtr )
2853 {
2854 FPrintF( stdout, "\nUpdate record:\n" );
2855 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
2856 FPrintF( stdout, " TTL: %u%?s\n",
2857 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
2858 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen );
2859 }
2860 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
2861 for( i = 0; i < inContext->extraRecordsCount; ++i )
2862 {
2863 const ExtraRecord * record = &inContext->extraRecords[ i ];
2864
2865 FPrintF( stdout, "Extra record %zu:\n", i + 1 );
2866 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type );
2867 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" );
2868 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX );
2869 }
2870 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
2871 FPrintF( stdout, "---\n" );
2872 }
2873
2874 //===========================================================================================================================
2875 // RegisterContextFree
2876 //===========================================================================================================================
2877
2878 static void RegisterContextFree( RegisterContext *inContext )
2879 {
2880 ExtraRecord * record;
2881 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount;
2882
2883 DNSServiceForget( &inContext->opRef );
2884 ForgetMem( &inContext->txtPtr );
2885 for( record = inContext->extraRecords; record < end; ++record )
2886 {
2887 check( !record->recordRef );
2888 ForgetMem( &record->dataPtr );
2889 }
2890 ForgetMem( &inContext->extraRecords );
2891 ForgetMem( &inContext->updateTXTPtr );
2892 free( inContext );
2893 }
2894
2895 //===========================================================================================================================
2896 // RegisterCallback
2897 //===========================================================================================================================
2898
2899 static void DNSSD_API
2900 RegisterCallback(
2901 DNSServiceRef inSDRef,
2902 DNSServiceFlags inFlags,
2903 DNSServiceErrorType inError,
2904 const char * inName,
2905 const char * inType,
2906 const char * inDomain,
2907 void * inContext )
2908 {
2909 RegisterContext * const context = (RegisterContext *) inContext;
2910 OSStatus err;
2911 struct timeval now;
2912
2913 Unused( inSDRef );
2914
2915 gettimeofday( &now, NULL );
2916
2917 if( !context->printedHeader )
2918 {
2919 FPrintF( stdout, "%-26s A/R Flags Service\n", "Timestamp" );
2920 context->printedHeader = true;
2921 }
2922 FPrintF( stdout, "%{du:time} %-3s %5X %s.%s%s %?#m\n",
2923 &now, AddRmvString( inFlags ), inFlags, inName, inType, inDomain, inError, inError );
2924
2925 require_noerr_action_quiet( inError, exit, err = inError );
2926
2927 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
2928 {
2929 context->didRegister = true;
2930 if( context->updateTXTPtr )
2931 {
2932 if( context->updateDelayMs > 0 )
2933 {
2934 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
2935 context, RegisterUpdate );
2936 }
2937 else
2938 {
2939 RegisterUpdate( context );
2940 }
2941 }
2942 if( context->extraRecordsCount > 0 )
2943 {
2944 ExtraRecord * record;
2945 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount;
2946
2947 for( record = context->extraRecords; record < end; ++record )
2948 {
2949 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
2950 (uint16_t) record->dataLen, record->dataPtr, record->ttl );
2951 require_noerr( err, exit );
2952 }
2953 }
2954 if( context->lifetimeMs == 0 )
2955 {
2956 Exit( kExitReason_TimeLimit );
2957 }
2958 else if( context->lifetimeMs > 0 )
2959 {
2960 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
2961 kExitReason_TimeLimit, Exit );
2962 }
2963 }
2964 err = kNoErr;
2965
2966 exit:
2967 if( err ) exit( 1 );
2968 }
2969
2970 //===========================================================================================================================
2971 // RegisterUpdate
2972 //===========================================================================================================================
2973
2974 static void RegisterUpdate( void *inContext )
2975 {
2976 OSStatus err;
2977 RegisterContext * const context = (RegisterContext *) inContext;
2978
2979 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
2980 context->updateTTL );
2981 require_noerr( err, exit );
2982
2983 exit:
2984 if( err ) exit( 1 );
2985 }
2986
2987 //===========================================================================================================================
2988 // RegisterRecordCmd
2989 //===========================================================================================================================
2990
2991 typedef struct
2992 {
2993 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection().
2994 DNSRecordRef recordRef; // Registered record reference.
2995 const char * recordName; // Name of resource record.
2996 uint8_t * dataPtr; // Pointer to resource record data.
2997 size_t dataLen; // Length of resource record data.
2998 uint32_t ttl; // TTL value of resource record in seconds.
2999 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord().
3000 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord().
3001 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3002 uint16_t recordType; // Resource record type.
3003 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd)
3004 size_t updateDataLen; // Length of data for record update.
3005 uint32_t updateTTL; // TTL for updated record.
3006 int updateDelayMs; // Post-registration record update delay in milliseconds.
3007 Boolean didRegister; // True if the record was registered.
3008
3009 } RegisterRecordContext;
3010
3011 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
3012 static void RegisterRecordContextFree( RegisterRecordContext *inContext );
3013 static void DNSSD_API
3014 RegisterRecordCallback(
3015 DNSServiceRef inSDRef,
3016 DNSRecordRef inRecordRef,
3017 DNSServiceFlags inFlags,
3018 DNSServiceErrorType inError,
3019 void * inContext );
3020 static void RegisterRecordUpdate( void *inContext );
3021
3022 static void RegisterRecordCmd( void )
3023 {
3024 OSStatus err;
3025 RegisterRecordContext * context = NULL;
3026 dispatch_source_t signalSource = NULL;
3027
3028 // Set up SIGINT handler.
3029
3030 signal( SIGINT, SIG_IGN );
3031 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3032 require_noerr( err, exit );
3033 dispatch_resume( signalSource );
3034
3035 // Create context.
3036
3037 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
3038 require_action( context, exit, err = kNoMemoryErr );
3039
3040 // Create connection.
3041
3042 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
3043 require_noerr_quiet( err, exit );
3044
3045 // Get flags.
3046
3047 context->flags = GetDNSSDFlagsFromOpts();
3048
3049 // Get interface.
3050
3051 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3052 require_noerr_quiet( err, exit );
3053
3054 // Get record type.
3055
3056 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
3057 require_noerr( err, exit );
3058
3059 // Get record data.
3060
3061 if( gRegisterRecord_Data )
3062 {
3063 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
3064 require_noerr_quiet( err, exit );
3065 }
3066
3067 // Set remaining parameters.
3068
3069 context->recordName = gRegisterRecord_Name;
3070 context->ttl = (uint32_t) gRegisterRecord_TTL;
3071 context->lifetimeMs = gRegisterRecord_LifetimeMs;
3072
3073 // Get update data.
3074
3075 if( gRegisterRecord_UpdateData )
3076 {
3077 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
3078 require_noerr_quiet( err, exit );
3079
3080 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL;
3081 context->updateDelayMs = gRegisterRecord_UpdateDelayMs;
3082 }
3083
3084 // Print prologue.
3085
3086 RegisterRecordPrintPrologue( context );
3087
3088 // Start operation.
3089
3090 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
3091 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
3092 context->ttl, RegisterRecordCallback, context );
3093 if( err )
3094 {
3095 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
3096 goto exit;
3097 }
3098
3099 dispatch_main();
3100
3101 exit:
3102 dispatch_source_forget( &signalSource );
3103 if( context ) RegisterRecordContextFree( context );
3104 if( err ) exit( 1 );
3105 }
3106
3107 //===========================================================================================================================
3108 // RegisterRecordPrintPrologue
3109 //===========================================================================================================================
3110
3111 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
3112 {
3113 int infinite;
3114 char ifName[ kInterfaceNameBufLen ];
3115
3116 InterfaceIndexToName( inContext->ifIndex, ifName );
3117
3118 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3119 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3120 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3121 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3122 FPrintF( stdout, "TTL: %u\n", inContext->ttl );
3123 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
3124 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3125 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3126 if( inContext->updateDataPtr )
3127 {
3128 FPrintF( stdout, "\nUpdate record:\n" );
3129 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
3130 FPrintF( stdout, " TTL: %u%?s\n",
3131 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
3132 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
3133 }
3134 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3135 FPrintF( stdout, "---\n" );
3136 }
3137
3138 //===========================================================================================================================
3139 // RegisterRecordContextFree
3140 //===========================================================================================================================
3141
3142 static void RegisterRecordContextFree( RegisterRecordContext *inContext )
3143 {
3144 DNSServiceForget( &inContext->conRef );
3145 ForgetMem( &inContext->dataPtr );
3146 ForgetMem( &inContext->updateDataPtr );
3147 free( inContext );
3148 }
3149
3150 //===========================================================================================================================
3151 // RegisterRecordCallback
3152 //===========================================================================================================================
3153
3154 static void
3155 RegisterRecordCallback(
3156 DNSServiceRef inSDRef,
3157 DNSRecordRef inRecordRef,
3158 DNSServiceFlags inFlags,
3159 DNSServiceErrorType inError,
3160 void * inContext )
3161 {
3162 RegisterRecordContext * context = (RegisterRecordContext *) inContext;
3163 struct timeval now;
3164
3165 Unused( inSDRef );
3166 Unused( inRecordRef );
3167 Unused( inFlags );
3168 Unused( context );
3169
3170 gettimeofday( &now, NULL );
3171 FPrintF( stdout, "%{du:time} Record registration result (error %#m)\n", &now, inError );
3172
3173 if( !context->didRegister && !inError )
3174 {
3175 context->didRegister = true;
3176 if( context->updateDataPtr )
3177 {
3178 if( context->updateDelayMs > 0 )
3179 {
3180 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
3181 context, RegisterRecordUpdate );
3182 }
3183 else
3184 {
3185 RegisterRecordUpdate( context );
3186 }
3187 }
3188 if( context->lifetimeMs == 0 )
3189 {
3190 Exit( kExitReason_TimeLimit );
3191 }
3192 else if( context->lifetimeMs > 0 )
3193 {
3194 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
3195 kExitReason_TimeLimit, Exit );
3196 }
3197 }
3198 }
3199
3200 //===========================================================================================================================
3201 // RegisterRecordUpdate
3202 //===========================================================================================================================
3203
3204 static void RegisterRecordUpdate( void *inContext )
3205 {
3206 OSStatus err;
3207 RegisterRecordContext * const context = (RegisterRecordContext *) inContext;
3208
3209 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
3210 context->updateDataPtr, context->updateTTL );
3211 require_noerr( err, exit );
3212
3213 exit:
3214 if( err ) exit( 1 );
3215 }
3216
3217 //===========================================================================================================================
3218 // ResolveCmd
3219 //===========================================================================================================================
3220
3221 typedef struct
3222 {
3223 DNSServiceRef mainRef; // Main sdRef for shared connections.
3224 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation.
3225 DNSServiceFlags flags; // Flags argument for DNSServiceResolve().
3226 const char * name; // Service name argument for DNSServiceResolve().
3227 const char * type; // Service type argument for DNSServiceResolve().
3228 const char * domain; // Domain argument for DNSServiceResolve().
3229 uint32_t ifIndex; // Interface index argument for DNSServiceResolve().
3230 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds.
3231
3232 } ResolveContext;
3233
3234 static void ResolvePrintPrologue( const ResolveContext *inContext );
3235 static void ResolveContextFree( ResolveContext *inContext );
3236 static void DNSSD_API
3237 ResolveCallback(
3238 DNSServiceRef inSDRef,
3239 DNSServiceFlags inFlags,
3240 uint32_t inInterfaceIndex,
3241 DNSServiceErrorType inError,
3242 const char * inFullName,
3243 const char * inHostname,
3244 uint16_t inPort,
3245 uint16_t inTXTLen,
3246 const unsigned char * inTXTPtr,
3247 void * inContext );
3248
3249 static void ResolveCmd( void )
3250 {
3251 OSStatus err;
3252 DNSServiceRef sdRef;
3253 ResolveContext * context = NULL;
3254 dispatch_source_t signalSource = NULL;
3255 int useMainConnection;
3256
3257 // Set up SIGINT handler.
3258
3259 signal( SIGINT, SIG_IGN );
3260 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3261 require_noerr( err, exit );
3262 dispatch_resume( signalSource );
3263
3264 // Create context.
3265
3266 context = (ResolveContext *) calloc( 1, sizeof( *context ) );
3267 require_action( context, exit, err = kNoMemoryErr );
3268
3269 // Check command parameters.
3270
3271 if( gResolve_TimeLimitSecs < 0 )
3272 {
3273 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
3274 err = kParamErr;
3275 goto exit;
3276 }
3277
3278 // Create main connection.
3279
3280 if( gConnectionOpt )
3281 {
3282 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3283 require_noerr_quiet( err, exit );
3284 useMainConnection = true;
3285 }
3286 else
3287 {
3288 useMainConnection = false;
3289 }
3290
3291 // Get flags.
3292
3293 context->flags = GetDNSSDFlagsFromOpts();
3294 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3295
3296 // Get interface index.
3297
3298 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3299 require_noerr_quiet( err, exit );
3300
3301 // Set remaining parameters.
3302
3303 context->name = gResolve_Name;
3304 context->type = gResolve_Type;
3305 context->domain = gResolve_Domain;
3306 context->timeLimitSecs = gResolve_TimeLimitSecs;
3307
3308 // Print prologue.
3309
3310 ResolvePrintPrologue( context );
3311
3312 // Start operation.
3313
3314 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3315 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
3316 ResolveCallback, NULL );
3317 require_noerr( err, exit );
3318
3319 context->opRef = sdRef;
3320 if( !useMainConnection )
3321 {
3322 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3323 require_noerr( err, exit );
3324 }
3325
3326 // Set time limit.
3327
3328 if( context->timeLimitSecs > 0 )
3329 {
3330 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3331 kExitReason_TimeLimit, Exit );
3332 }
3333 dispatch_main();
3334
3335 exit:
3336 dispatch_source_forget( &signalSource );
3337 if( context ) ResolveContextFree( context );
3338 if( err ) exit( 1 );
3339 }
3340
3341 //===========================================================================================================================
3342 // ReconfirmCmd
3343 //===========================================================================================================================
3344
3345 static void ReconfirmCmd( void )
3346 {
3347 OSStatus err;
3348 uint8_t * rdataPtr = NULL;
3349 size_t rdataLen = 0;
3350 DNSServiceFlags flags;
3351 uint32_t ifIndex;
3352 uint16_t type, class;
3353 char ifName[ kInterfaceNameBufLen ];
3354
3355 // Get flags.
3356
3357 flags = GetDNSSDFlagsFromOpts();
3358
3359 // Get interface index.
3360
3361 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
3362 require_noerr_quiet( err, exit );
3363
3364 // Get record type.
3365
3366 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
3367 require_noerr( err, exit );
3368
3369 // Get record data.
3370
3371 if( gReconfirmRecord_Data )
3372 {
3373 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
3374 require_noerr_quiet( err, exit );
3375 }
3376
3377 // Get record class.
3378
3379 if( gReconfirmRecord_Class )
3380 {
3381 err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
3382 require_noerr( err, exit );
3383 }
3384 else
3385 {
3386 class = kDNSServiceClass_IN;
3387 }
3388
3389 // Print prologue.
3390
3391 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
3392 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
3393 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name );
3394 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
3395 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
3396 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX );
3397 FPrintF( stdout, "---\n" );
3398
3399 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
3400 FPrintF( stdout, "Error: %#m\n", err );
3401
3402 exit:
3403 FreeNullSafe( rdataPtr );
3404 if( err ) exit( 1 );
3405 }
3406
3407 //===========================================================================================================================
3408 // ResolvePrintPrologue
3409 //===========================================================================================================================
3410
3411 static void ResolvePrintPrologue( const ResolveContext *inContext )
3412 {
3413 const int timeLimitSecs = inContext->timeLimitSecs;
3414 char ifName[ kInterfaceNameBufLen ];
3415
3416 InterfaceIndexToName( inContext->ifIndex, ifName );
3417
3418 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3419 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3420 FPrintF( stdout, "Name: %s\n", inContext->name );
3421 FPrintF( stdout, "Type: %s\n", inContext->type );
3422 FPrintF( stdout, "Domain: %s\n", inContext->domain );
3423 FPrintF( stdout, "Time limit: " );
3424 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3425 else FPrintF( stdout, "∞\n" );
3426 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3427 FPrintF( stdout, "---\n" );
3428 }
3429
3430 //===========================================================================================================================
3431 // ResolveContextFree
3432 //===========================================================================================================================
3433
3434 static void ResolveContextFree( ResolveContext *inContext )
3435 {
3436 DNSServiceForget( &inContext->opRef );
3437 DNSServiceForget( &inContext->mainRef );
3438 free( inContext );
3439 }
3440
3441 //===========================================================================================================================
3442 // ResolveCallback
3443 //===========================================================================================================================
3444
3445 static void DNSSD_API
3446 ResolveCallback(
3447 DNSServiceRef inSDRef,
3448 DNSServiceFlags inFlags,
3449 uint32_t inInterfaceIndex,
3450 DNSServiceErrorType inError,
3451 const char * inFullName,
3452 const char * inHostname,
3453 uint16_t inPort,
3454 uint16_t inTXTLen,
3455 const unsigned char * inTXTPtr,
3456 void * inContext )
3457 {
3458 struct timeval now;
3459 char errorStr[ 64 ];
3460
3461 Unused( inSDRef );
3462 Unused( inFlags );
3463 Unused( inContext );
3464
3465 gettimeofday( &now, NULL );
3466
3467 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
3468
3469 FPrintF( stdout, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
3470 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
3471 if( inTXTLen == 1 )
3472 {
3473 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
3474 }
3475 else
3476 {
3477 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
3478 }
3479 }
3480
3481 //===========================================================================================================================
3482 // GetAddrInfoPOSIXCmd
3483 //===========================================================================================================================
3484
3485 #define AddressFamilyStr( X ) ( \
3486 ( (X) == AF_INET ) ? "inet" : \
3487 ( (X) == AF_INET6 ) ? "inet6" : \
3488 ( (X) == AF_UNSPEC ) ? "unspec" : \
3489 "???" )
3490
3491 typedef struct
3492 {
3493 unsigned int flag;
3494 const char * str;
3495
3496 } FlagStringPair;
3497
3498 #define CaseFlagStringify( X ) { (X), # X }
3499
3500 const FlagStringPair kGAIPOSIXFlagStringPairs[] =
3501 {
3502 #if( defined( AI_UNUSABLE ) )
3503 CaseFlagStringify( AI_UNUSABLE ),
3504 #endif
3505 CaseFlagStringify( AI_NUMERICSERV ),
3506 CaseFlagStringify( AI_V4MAPPED ),
3507 CaseFlagStringify( AI_ADDRCONFIG ),
3508 #if( defined( AI_V4MAPPED_CFG ) )
3509 CaseFlagStringify( AI_V4MAPPED_CFG ),
3510 #endif
3511 CaseFlagStringify( AI_ALL ),
3512 CaseFlagStringify( AI_NUMERICHOST ),
3513 CaseFlagStringify( AI_CANONNAME ),
3514 CaseFlagStringify( AI_PASSIVE ),
3515 { 0, NULL }
3516 };
3517
3518 static void GetAddrInfoPOSIXCmd( void )
3519 {
3520 OSStatus err;
3521 struct addrinfo hints;
3522 struct timeval now;
3523 const struct addrinfo * addrInfo;
3524 struct addrinfo * addrInfoList = NULL;
3525 const FlagStringPair * pair;
3526
3527 memset( &hints, 0, sizeof( hints ) );
3528 hints.ai_socktype = SOCK_STREAM;
3529
3530 // Set hints address family.
3531
3532 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC;
3533 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET;
3534 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6;
3535 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC;
3536 else
3537 {
3538 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
3539 err = kParamErr;
3540 goto exit;
3541 }
3542
3543 // Set hints flags.
3544
3545 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG;
3546 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL;
3547 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME;
3548 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST;
3549 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV;
3550 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE;
3551 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED;
3552 #if( defined( AI_V4MAPPED_CFG ) )
3553 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG;
3554 #endif
3555 #if( defined( AI_DEFAULT ) )
3556 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT;
3557 #endif
3558 #if( defined( AI_UNUSABLE ) )
3559 if( gGAIPOSIXFlag_Unusable ) hints.ai_flags |= AI_UNUSABLE;
3560 #endif
3561
3562 // Print prologue.
3563
3564 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName );
3565 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName );
3566 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) );
3567 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags );
3568 for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair )
3569 {
3570 if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str );
3571 }
3572 FPrintF( stdout, ">\n" );
3573 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3574 FPrintF( stdout, "---\n" );
3575
3576 // Call getaddrinfo().
3577
3578 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
3579 gettimeofday( &now, NULL );
3580 if( err )
3581 {
3582 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
3583 }
3584 else
3585 {
3586 int addrCount = 0;
3587
3588 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
3589
3590 FPrintF( stdout, "Addresses (%d total):\n", addrCount );
3591 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
3592 {
3593 FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
3594 }
3595 }
3596 FPrintF( stdout, "---\n" );
3597 FPrintF( stdout, "End time: %{du:time}\n", &now );
3598
3599 exit:
3600 if( addrInfoList ) freeaddrinfo( addrInfoList );
3601 if( err ) exit( 1 );
3602 }
3603
3604 //===========================================================================================================================
3605 // ReverseLookupCmd
3606 //===========================================================================================================================
3607
3608 static void ReverseLookupCmd( void )
3609 {
3610 OSStatus err;
3611 QueryRecordContext * context = NULL;
3612 DNSServiceRef sdRef;
3613 dispatch_source_t signalSource = NULL;
3614 uint32_t ipv4Addr;
3615 uint8_t ipv6Addr[ 16 ];
3616 char recordName[ ( 16 * 4 ) + 9 + 1 ];
3617 int useMainConnection;
3618 const char * endPtr;
3619
3620 // Set up SIGINT handler.
3621
3622 signal( SIGINT, SIG_IGN );
3623 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3624 require_noerr( err, exit );
3625 dispatch_resume( signalSource );
3626
3627 // Create context.
3628
3629 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
3630 require_action( context, exit, err = kNoMemoryErr );
3631
3632 // Check command parameters.
3633
3634 if( gReverseLookup_TimeLimitSecs < 0 )
3635 {
3636 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
3637 err = kParamErr;
3638 goto exit;
3639 }
3640
3641 // Create main connection.
3642
3643 if( gConnectionOpt )
3644 {
3645 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3646 require_noerr_quiet( err, exit );
3647 useMainConnection = true;
3648 }
3649 else
3650 {
3651 useMainConnection = false;
3652 }
3653
3654 // Get flags.
3655
3656 context->flags = GetDNSSDFlagsFromOpts();
3657 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3658
3659 // Get interface index.
3660
3661 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3662 require_noerr_quiet( err, exit );
3663
3664 // Create reverse lookup record name.
3665
3666 err = StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
3667 &ipv4Addr, NULL, NULL, NULL, &endPtr );
3668 if( err || ( *endPtr != '\0' ) )
3669 {
3670 char * dst;
3671 int i;
3672
3673 err = StringToIPv6Address( gReverseLookup_IPAddr,
3674 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
3675 ipv6Addr, NULL, NULL, NULL, &endPtr );
3676 if( err || ( *endPtr != '\0' ) )
3677 {
3678 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
3679 err = kParamErr;
3680 goto exit;
3681 }
3682 dst = recordName;
3683 for( i = 15; i >= 0; --i )
3684 {
3685 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] & 0x0F ];
3686 *dst++ = '.';
3687 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] >> 4 ];
3688 *dst++ = '.';
3689 }
3690 strcpy( dst, "ip6.arpa." );
3691 check( ( strlen( recordName ) + 1 ) <= sizeof( recordName ) );
3692 }
3693 else
3694 {
3695 SNPrintF( recordName, sizeof( recordName ), "%u.%u.%u.%u.in-addr.arpa.",
3696 ipv4Addr & 0xFF,
3697 ( ipv4Addr >> 8 ) & 0xFF,
3698 ( ipv4Addr >> 16 ) & 0xFF,
3699 ( ipv4Addr >> 24 ) & 0xFF );
3700 }
3701
3702 // Set remaining parameters.
3703
3704 context->recordName = recordName;
3705 context->recordType = kDNSServiceType_PTR;
3706 context->timeLimitSecs = gReverseLookup_TimeLimitSecs;
3707 context->oneShotMode = gReverseLookup_OneShot ? true : false;
3708
3709 // Print prologue.
3710
3711 QueryRecordPrintPrologue( context );
3712
3713 // Start operation.
3714
3715 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3716 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
3717 kDNSServiceClass_IN, QueryRecordCallback, context );
3718 require_noerr( err, exit );
3719
3720 context->opRef = sdRef;
3721 if( !useMainConnection )
3722 {
3723 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3724 require_noerr( err, exit );
3725 }
3726
3727 // Set time limit.
3728
3729 if( context->timeLimitSecs > 0 )
3730 {
3731 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3732 kExitReason_TimeLimit, Exit );
3733 }
3734 dispatch_main();
3735
3736 exit:
3737 dispatch_source_forget( &signalSource );
3738 if( context ) QueryRecordContextFree( context );
3739 if( err ) exit( 1 );
3740 }
3741
3742 //===========================================================================================================================
3743 // PortMappingCmd
3744 //===========================================================================================================================
3745
3746 typedef struct
3747 {
3748 DNSServiceRef mainRef; // Main sdRef for shared connection.
3749 DNSServiceRef opRef; // sdRef for the DNSServiceNATPortMappingCreate operation.
3750 DNSServiceFlags flags; // Flags for DNSServiceNATPortMappingCreate operation.
3751 uint32_t ifIndex; // Interface index argument for DNSServiceNATPortMappingCreate operation.
3752 DNSServiceProtocol protocols; // Protocols argument for DNSServiceNATPortMappingCreate operation.
3753 uint32_t ttl; // TTL argument for DNSServiceNATPortMappingCreate operation.
3754 uint16_t internalPort; // Internal port argument for DNSServiceNATPortMappingCreate operation.
3755 uint16_t externalPort; // External port argument for DNSServiceNATPortMappingCreate operation.
3756 Boolean printedHeader; // True if results header was printed.
3757
3758 } PortMappingContext;
3759
3760 static void PortMappingPrintPrologue( const PortMappingContext *inContext );
3761 static void PortMappingContextFree( PortMappingContext *inContext );
3762 static void DNSSD_API
3763 PortMappingCallback(
3764 DNSServiceRef inSDRef,
3765 DNSServiceFlags inFlags,
3766 uint32_t inInterfaceIndex,
3767 DNSServiceErrorType inError,
3768 uint32_t inExternalIPv4Address,
3769 DNSServiceProtocol inProtocol,
3770 uint16_t inInternalPort,
3771 uint16_t inExternalPort,
3772 uint32_t inTTL,
3773 void * inContext );
3774
3775 static void PortMappingCmd( void )
3776 {
3777 OSStatus err;
3778 PortMappingContext * context = NULL;
3779 DNSServiceRef sdRef;
3780 dispatch_source_t signalSource = NULL;
3781 int useMainConnection;
3782
3783 // Set up SIGINT handler.
3784
3785 signal( SIGINT, SIG_IGN );
3786 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3787 require_noerr( err, exit );
3788 dispatch_resume( signalSource );
3789
3790 // Create context.
3791
3792 context = (PortMappingContext *) calloc( 1, sizeof( *context ) );
3793 require_action( context, exit, err = kNoMemoryErr );
3794
3795 // Check command parameters.
3796
3797 if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) )
3798 {
3799 FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort );
3800 err = kParamErr;
3801 goto exit;
3802 }
3803
3804 if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) )
3805 {
3806 FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort );
3807 err = kParamErr;
3808 goto exit;
3809 }
3810
3811 // Create main connection.
3812
3813 if( gConnectionOpt )
3814 {
3815 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3816 require_noerr_quiet( err, exit );
3817 useMainConnection = true;
3818 }
3819 else
3820 {
3821 useMainConnection = false;
3822 }
3823
3824 // Get flags.
3825
3826 context->flags = GetDNSSDFlagsFromOpts();
3827 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3828
3829 // Get interface index.
3830
3831 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3832 require_noerr_quiet( err, exit );
3833
3834 // Set remaining parameters.
3835
3836 if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP;
3837 if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP;
3838 context->ttl = (uint32_t) gPortMapping_TTL;
3839 context->internalPort = (uint16_t) gPortMapping_InternalPort;
3840 context->externalPort = (uint16_t) gPortMapping_ExternalPort;
3841
3842 // Print prologue.
3843
3844 PortMappingPrintPrologue( context );
3845
3846 // Start operation.
3847
3848 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3849 err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols,
3850 htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context );
3851 require_noerr( err, exit );
3852
3853 context->opRef = sdRef;
3854 if( !useMainConnection )
3855 {
3856 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3857 require_noerr( err, exit );
3858 }
3859
3860 dispatch_main();
3861
3862 exit:
3863 dispatch_source_forget( &signalSource );
3864 if( context ) PortMappingContextFree( context );
3865 if( err ) exit( 1 );
3866 }
3867
3868 //===========================================================================================================================
3869 // PortMappingPrintPrologue
3870 //===========================================================================================================================
3871
3872 static void PortMappingPrintPrologue( const PortMappingContext *inContext )
3873 {
3874 char ifName[ kInterfaceNameBufLen ];
3875
3876 InterfaceIndexToName( inContext->ifIndex, ifName );
3877
3878 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3879 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3880 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
3881 FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort );
3882 FPrintF( stdout, "External Port: %u\n", inContext->externalPort );
3883 FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl,
3884 " (system will use a default value.)" );
3885 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3886 FPrintF( stdout, "---\n" );
3887
3888 }
3889
3890 //===========================================================================================================================
3891 // PortMappingContextFree
3892 //===========================================================================================================================
3893
3894 static void PortMappingContextFree( PortMappingContext *inContext )
3895 {
3896 DNSServiceForget( &inContext->opRef );
3897 DNSServiceForget( &inContext->mainRef );
3898 free( inContext );
3899 }
3900
3901 //===========================================================================================================================
3902 // PortMappingCallback
3903 //===========================================================================================================================
3904
3905 static void DNSSD_API
3906 PortMappingCallback(
3907 DNSServiceRef inSDRef,
3908 DNSServiceFlags inFlags,
3909 uint32_t inInterfaceIndex,
3910 DNSServiceErrorType inError,
3911 uint32_t inExternalIPv4Address,
3912 DNSServiceProtocol inProtocol,
3913 uint16_t inInternalPort,
3914 uint16_t inExternalPort,
3915 uint32_t inTTL,
3916 void * inContext )
3917 {
3918 PortMappingContext * const context = (PortMappingContext *) inContext;
3919 struct timeval now;
3920 char errorStr[ 128 ];
3921
3922 Unused( inSDRef );
3923 Unused( inFlags );
3924
3925 gettimeofday( &now, NULL );
3926
3927 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError );
3928 if( !context->printedHeader )
3929 {
3930 FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
3931 context->printedHeader = true;
3932 }
3933 FPrintF( stdout, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
3934 &now, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL,
3935 inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr );
3936 }
3937
3938 //===========================================================================================================================
3939 // BrowseAllCmd
3940 //===========================================================================================================================
3941
3942 typedef struct BrowseDomain BrowseDomain;
3943 typedef struct BrowseType BrowseType;
3944 typedef struct BrowseOp BrowseOp;
3945 typedef struct BrowseInstance BrowseInstance;
3946 typedef struct BrowseIPAddr BrowseIPAddr;
3947
3948 typedef struct
3949 {
3950 int refCount;
3951 DNSServiceRef mainRef;
3952 DNSServiceRef domainsQuery;
3953 const char * domain;
3954 BrowseDomain * domainList;
3955 char ** serviceTypes;
3956 size_t serviceTypesCount;
3957 dispatch_source_t exitTimer;
3958 uint32_t ifIndex;
3959 int pendingConnectCount;
3960 int browseTimeSecs;
3961 int maxConnectTimeSecs;
3962 Boolean includeAWDL;
3963 Boolean useColoredText;
3964
3965 } BrowseAllContext;
3966
3967 struct BrowseDomain
3968 {
3969 BrowseDomain * next;
3970 char * name;
3971 DNSServiceRef servicesQuery;
3972 BrowseAllContext * context;
3973 BrowseType * typeList;
3974 };
3975
3976 struct BrowseType
3977 {
3978 BrowseType * next;
3979 char * name;
3980 BrowseOp * browseList;
3981 };
3982
3983 struct BrowseOp
3984 {
3985 BrowseOp * next;
3986 BrowseAllContext * context;
3987 DNSServiceRef browse;
3988 uint64_t startTicks;
3989 BrowseInstance * instanceList;
3990 uint32_t ifIndex;
3991 Boolean isTCP;
3992 };
3993
3994 struct BrowseInstance
3995 {
3996 BrowseInstance * next;
3997 BrowseAllContext * context;
3998 char * name;
3999 uint64_t foundTicks;
4000 DNSServiceRef resolve;
4001 uint64_t resolveStartTicks;
4002 uint64_t resolveDoneTicks;
4003 DNSServiceRef getAddr;
4004 uint64_t getAddrStartTicks;
4005 BrowseIPAddr * addrList;
4006 uint8_t * txtPtr;
4007 size_t txtLen;
4008 char * hostname;
4009 uint32_t ifIndex;
4010 uint16_t port;
4011 Boolean isTCP;
4012 };
4013
4014 typedef enum
4015 {
4016 kConnectStatus_None = 0,
4017 kConnectStatus_Pending = 1,
4018 kConnectStatus_Succeeded = 2,
4019 kConnectStatus_Failed = 3
4020
4021 } ConnectStatus;
4022
4023 struct BrowseIPAddr
4024 {
4025 BrowseIPAddr * next;
4026 sockaddr_ip sip;
4027 int refCount;
4028 BrowseAllContext * context;
4029 uint64_t foundTicks;
4030 AsyncConnectionRef connection;
4031 ConnectStatus connectStatus;
4032 CFTimeInterval connectTimeSecs;
4033 OSStatus connectError;
4034 };
4035
4036 static void BrowseAllPrintPrologue( const BrowseAllContext *inContext );
4037 static void DNSSD_API
4038 BrowseAllQueryDomainsCallback(
4039 DNSServiceRef inSDRef,
4040 DNSServiceFlags inFlags,
4041 uint32_t inInterfaceIndex,
4042 DNSServiceErrorType inError,
4043 const char * inFullName,
4044 uint16_t inType,
4045 uint16_t inClass,
4046 uint16_t inRDataLen,
4047 const void * inRDataPtr,
4048 uint32_t inTTL,
4049 void * inContext );
4050 static void DNSSD_API
4051 BrowseAllQueryCallback(
4052 DNSServiceRef inSDRef,
4053 DNSServiceFlags inFlags,
4054 uint32_t inInterfaceIndex,
4055 DNSServiceErrorType inError,
4056 const char * inFullName,
4057 uint16_t inType,
4058 uint16_t inClass,
4059 uint16_t inRDataLen,
4060 const void * inRDataPtr,
4061 uint32_t inTTL,
4062 void * inContext );
4063 static void DNSSD_API
4064 BrowseAllBrowseCallback(
4065 DNSServiceRef inSDRef,
4066 DNSServiceFlags inFlags,
4067 uint32_t inInterfaceIndex,
4068 DNSServiceErrorType inError,
4069 const char * inName,
4070 const char * inRegType,
4071 const char * inDomain,
4072 void * inContext );
4073 static void DNSSD_API
4074 BrowseAllResolveCallback(
4075 DNSServiceRef inSDRef,
4076 DNSServiceFlags inFlags,
4077 uint32_t inInterfaceIndex,
4078 DNSServiceErrorType inError,
4079 const char * inFullName,
4080 const char * inHostname,
4081 uint16_t inPort,
4082 uint16_t inTXTLen,
4083 const unsigned char * inTXTPtr,
4084 void * inContext );
4085 static void DNSSD_API
4086 BrowseAllGAICallback(
4087 DNSServiceRef inSDRef,
4088 DNSServiceFlags inFlags,
4089 uint32_t inInterfaceIndex,
4090 DNSServiceErrorType inError,
4091 const char * inHostname,
4092 const struct sockaddr * inSockAddr,
4093 uint32_t inTTL,
4094 void * inContext );
4095 static void BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
4096 static void BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
4097 static void BrowseAllStop( void *inContext );
4098 static void BrowseAllExit( void *inContext );
4099 static OSStatus BrowseAllAddDomain( BrowseAllContext *inContext, const char *inName );
4100 static OSStatus BrowseAllRemoveDomain( BrowseAllContext *inContext, const char *inName );
4101 static void BrowseAllContextRelease( BrowseAllContext *inContext );
4102 static OSStatus
4103 BrowseAllAddServiceType(
4104 BrowseAllContext * inContext,
4105 BrowseDomain * inDomain,
4106 const char * inName,
4107 uint32_t inIfIndex,
4108 Boolean inIncludeAWDL );
4109 static OSStatus
4110 BrowseAllRemoveServiceType(
4111 BrowseAllContext * inContext,
4112 BrowseDomain * inDomain,
4113 const char * inName,
4114 uint32_t inIfIndex );
4115 static OSStatus
4116 BrowseAllAddServiceInstance(
4117 BrowseAllContext * inContext,
4118 BrowseOp * inBrowse,
4119 const char * inName,
4120 const char * inRegType,
4121 const char * inDomain,
4122 uint32_t inIfIndex );
4123 static OSStatus
4124 BrowseAllRemoveServiceInstance(
4125 BrowseAllContext * inContext,
4126 BrowseOp * inBrowse,
4127 const char * inName,
4128 uint32_t inIfIndex );
4129 static OSStatus
4130 BrowseAllAddIPAddress(
4131 BrowseAllContext * inContext,
4132 BrowseInstance * inInstance,
4133 const struct sockaddr * inSockAddr );
4134 static OSStatus
4135 BrowseAllRemoveIPAddress(
4136 BrowseAllContext * inContext,
4137 BrowseInstance * inInstance,
4138 const struct sockaddr * inSockAddr );
4139 static void BrowseDomainFree( BrowseDomain *inDomain );
4140 static void BrowseTypeFree( BrowseType *inType );
4141 static void BrowseOpFree( BrowseOp *inBrowse );
4142 static void BrowseInstanceFree( BrowseInstance *inInstance );
4143 static void BrowseIPAddrRelease( BrowseIPAddr *inAddr );
4144 static void BrowseIPAddrReleaseList( BrowseIPAddr *inList );
4145
4146 #define ForgetIPAddressList( X ) ForgetCustom( X, BrowseIPAddrReleaseList )
4147 #define ForgetBrowseAllContext( X ) ForgetCustom( X, BrowseAllContextRelease )
4148
4149 #define kBrowseAllOpenFileMin 4096
4150
4151 static void BrowseAllCmd( void )
4152 {
4153 OSStatus err;
4154 BrowseAllContext * context = NULL;
4155
4156 // Check command parameters.
4157
4158 if( gBrowseAll_BrowseTimeSecs <= 0 )
4159 {
4160 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
4161 err = kParamErr;
4162 goto exit;
4163 }
4164
4165 #if( TARGET_OS_POSIX )
4166 // Set open file minimum.
4167
4168 {
4169 struct rlimit fdLimits;
4170
4171 err = getrlimit( RLIMIT_NOFILE, &fdLimits );
4172 err = map_global_noerr_errno( err );
4173 require_noerr( err, exit );
4174
4175 if( fdLimits.rlim_cur < kBrowseAllOpenFileMin )
4176 {
4177 fdLimits.rlim_cur = kBrowseAllOpenFileMin;
4178 err = setrlimit( RLIMIT_NOFILE, &fdLimits );
4179 err = map_global_noerr_errno( err );
4180 require_noerr( err, exit );
4181 }
4182 }
4183 #endif
4184
4185 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
4186 require_action( context, exit, err = kNoMemoryErr );
4187
4188 context->refCount = 1;
4189 context->domain = gBrowseAll_Domain;
4190 context->serviceTypes = gBrowseAll_ServiceTypes;
4191 context->serviceTypesCount = gBrowseAll_ServiceTypesCount;
4192 gBrowseAll_ServiceTypes = NULL;
4193 gBrowseAll_ServiceTypesCount = 0;
4194 context->browseTimeSecs = gBrowseAll_BrowseTimeSecs;
4195 context->maxConnectTimeSecs = gBrowseAll_MaxConnectTimeSecs;
4196 context->includeAWDL = gDNSSDFlag_IncludeAWDL ? true : false;
4197 #if( TARGET_OS_POSIX )
4198 context->useColoredText = isatty( STDOUT_FILENO ) ? true : false;
4199 #endif
4200
4201 err = DNSServiceCreateConnection( &context->mainRef );
4202 require_noerr( err, exit );
4203
4204 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
4205 require_noerr( err, exit );
4206
4207 // Set interface index.
4208
4209 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4210 require_noerr_quiet( err, exit );
4211
4212 BrowseAllPrintPrologue( context );
4213
4214 if( context->domain )
4215 {
4216 err = BrowseAllAddDomain( context, context->domain );
4217 require_noerr( err, exit );
4218 }
4219 else
4220 {
4221 DNSServiceRef sdRef;
4222
4223 sdRef = context->mainRef;
4224 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly,
4225 "b._dns-sd._udp.local.", kDNSServiceType_PTR, kDNSServiceClass_IN, BrowseAllQueryDomainsCallback, context );
4226 require_noerr( err, exit );
4227
4228 context->domainsQuery = sdRef;
4229 }
4230
4231 dispatch_after_f( dispatch_time_seconds( context->browseTimeSecs ), dispatch_get_main_queue(), context, BrowseAllStop );
4232 dispatch_main();
4233
4234 exit:
4235 if( context ) BrowseAllContextRelease( context );
4236 if( err ) exit( 1 );
4237 }
4238
4239 //===========================================================================================================================
4240 // BrowseAllPrintPrologue
4241 //===========================================================================================================================
4242
4243 static void BrowseAllPrintPrologue( const BrowseAllContext *inContext )
4244 {
4245 size_t i;
4246 char ifName[ kInterfaceNameBufLen ];
4247
4248 InterfaceIndexToName( inContext->ifIndex, ifName );
4249
4250 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4251 FPrintF( stdout, "Service types: ");
4252 if( inContext->serviceTypesCount > 0 )
4253 {
4254 FPrintF( stdout, "%s", inContext->serviceTypes[ 0 ] );
4255 for( i = 1; i < inContext->serviceTypesCount; ++i ) FPrintF( stdout, ", %s", inContext->serviceTypes[ i ] );
4256 FPrintF( stdout, "\n" );
4257 }
4258 else
4259 {
4260 FPrintF( stdout, "all services\n" );
4261 }
4262 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "default domains" );
4263 FPrintF( stdout, "Browse time: %d second%?c\n", inContext->browseTimeSecs, inContext->browseTimeSecs != 1, 's' );
4264 FPrintF( stdout, "Max connect time: %d second%?c\n",
4265 inContext->maxConnectTimeSecs, inContext->maxConnectTimeSecs != 1, 's' );
4266 FPrintF( stdout, "IncludeAWDL: %s\n", inContext->includeAWDL ? "YES" : "NO" );
4267 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4268 FPrintF( stdout, "---\n" );
4269 }
4270
4271 //===========================================================================================================================
4272 // BrowseAllQueryDomainsCallback
4273 //===========================================================================================================================
4274
4275 static void DNSSD_API
4276 BrowseAllQueryDomainsCallback(
4277 DNSServiceRef inSDRef,
4278 DNSServiceFlags inFlags,
4279 uint32_t inInterfaceIndex,
4280 DNSServiceErrorType inError,
4281 const char * inFullName,
4282 uint16_t inType,
4283 uint16_t inClass,
4284 uint16_t inRDataLen,
4285 const void * inRDataPtr,
4286 uint32_t inTTL,
4287 void * inContext )
4288 {
4289 OSStatus err;
4290 BrowseAllContext * const context = (BrowseAllContext *) inContext;
4291 char domainStr[ kDNSServiceMaxDomainName ];
4292
4293 Unused( inSDRef );
4294 Unused( inInterfaceIndex );
4295 Unused( inFullName );
4296 Unused( inType );
4297 Unused( inClass );
4298 Unused( inTTL );
4299
4300 err = inError;
4301 require_noerr( err, exit );
4302
4303 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
4304 require_noerr( err, exit );
4305
4306 if( inFlags & kDNSServiceFlagsAdd )
4307 {
4308 err = BrowseAllAddDomain( context, domainStr );
4309 if( err == kDuplicateErr ) err = kNoErr;
4310 require_noerr( err, exit );
4311 }
4312 else
4313 {
4314 err = BrowseAllRemoveDomain( context, domainStr );
4315 if( err == kNotFoundErr ) err = kNoErr;
4316 require_noerr( err, exit );
4317 }
4318
4319 exit:
4320 if( err ) exit( 1 );
4321 }
4322
4323 //===========================================================================================================================
4324 // BrowseAllQueryCallback
4325 //===========================================================================================================================
4326
4327 static void DNSSD_API
4328 BrowseAllQueryCallback(
4329 DNSServiceRef inSDRef,
4330 DNSServiceFlags inFlags,
4331 uint32_t inInterfaceIndex,
4332 DNSServiceErrorType inError,
4333 const char * inFullName,
4334 uint16_t inType,
4335 uint16_t inClass,
4336 uint16_t inRDataLen,
4337 const void * inRDataPtr,
4338 uint32_t inTTL,
4339 void * inContext )
4340 {
4341 OSStatus err;
4342 BrowseDomain * const domain = (BrowseDomain *) inContext;
4343 const uint8_t * firstLabel;
4344 const uint8_t * secondLabel;
4345 char * serviceTypeStr = NULL;
4346 const uint8_t * const end = ( (uint8_t * ) inRDataPtr ) + inRDataLen;
4347
4348 Unused( inSDRef );
4349 Unused( inFullName );
4350 Unused( inTTL );
4351 Unused( inType );
4352 Unused( inClass );
4353
4354 err = inError;
4355 require_noerr( err, exit );
4356
4357 check( inType == kDNSServiceType_PTR );
4358 check( inClass == kDNSServiceClass_IN );
4359 require_action( inRDataLen > 0, exit, err = kSizeErr );
4360
4361 firstLabel = inRDataPtr;
4362 require_action_quiet( ( firstLabel + 1 + firstLabel[ 0 ] ) < end , exit, err = kUnderrunErr );
4363
4364 secondLabel = firstLabel + 1 + firstLabel[ 0 ];
4365 require_action_quiet( ( secondLabel + 1 + secondLabel[ 0 ] ) < end , exit, err = kUnderrunErr );
4366
4367 ASPrintF( &serviceTypeStr, "%#s.%#s", firstLabel, secondLabel );
4368 require_action( serviceTypeStr, exit, err = kNoMemoryErr );
4369
4370 if( inFlags & kDNSServiceFlagsAdd )
4371 {
4372 err = BrowseAllAddServiceType( domain->context, domain, serviceTypeStr, inInterfaceIndex, false );
4373 if( err == kDuplicateErr ) err = kNoErr;
4374 require_noerr( err, exit );
4375 }
4376 else
4377 {
4378 err = BrowseAllRemoveServiceType( domain->context, domain, serviceTypeStr, inInterfaceIndex );
4379 if( err == kNotFoundErr ) err = kNoErr;
4380 require_noerr( err, exit );
4381 }
4382
4383 exit:
4384 FreeNullSafe( serviceTypeStr );
4385 }
4386
4387 //===========================================================================================================================
4388 // BrowseAllBrowseCallback
4389 //===========================================================================================================================
4390
4391 static void DNSSD_API
4392 BrowseAllBrowseCallback(
4393 DNSServiceRef inSDRef,
4394 DNSServiceFlags inFlags,
4395 uint32_t inInterfaceIndex,
4396 DNSServiceErrorType inError,
4397 const char * inName,
4398 const char * inRegType,
4399 const char * inDomain,
4400 void * inContext )
4401 {
4402 OSStatus err;
4403 BrowseOp * const browse = (BrowseOp *) inContext;
4404
4405 Unused( inSDRef );
4406
4407 err = inError;
4408 require_noerr( err, exit );
4409
4410 if( inFlags & kDNSServiceFlagsAdd )
4411 {
4412 err = BrowseAllAddServiceInstance( browse->context, browse, inName, inRegType, inDomain, inInterfaceIndex );
4413 if( err == kDuplicateErr ) err = kNoErr;
4414 require_noerr( err, exit );
4415 }
4416 else
4417 {
4418 err = BrowseAllRemoveServiceInstance( browse->context, browse, inName, inInterfaceIndex );
4419 if( err == kNotFoundErr ) err = kNoErr;
4420 require_noerr( err, exit );
4421 }
4422
4423 exit:
4424 return;
4425 }
4426
4427 //===========================================================================================================================
4428 // BrowseAllResolveCallback
4429 //===========================================================================================================================
4430
4431 static void DNSSD_API
4432 BrowseAllResolveCallback(
4433 DNSServiceRef inSDRef,
4434 DNSServiceFlags inFlags,
4435 uint32_t inInterfaceIndex,
4436 DNSServiceErrorType inError,
4437 const char * inFullName,
4438 const char * inHostname,
4439 uint16_t inPort,
4440 uint16_t inTXTLen,
4441 const unsigned char * inTXTPtr,
4442 void * inContext )
4443 {
4444 OSStatus err;
4445 const uint64_t nowTicks = UpTicks();
4446 BrowseInstance * const instance = (BrowseInstance *) inContext;
4447
4448 Unused( inSDRef );
4449 Unused( inFlags );
4450 Unused( inInterfaceIndex );
4451 Unused( inFullName );
4452
4453 err = inError;
4454 require_noerr( err, exit );
4455
4456 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
4457 {
4458 FreeNullSafe( instance->txtPtr );
4459 instance->txtPtr = malloc( inTXTLen );
4460 require_action( instance->txtPtr, exit, err = kNoMemoryErr );
4461
4462 memcpy( instance->txtPtr, inTXTPtr, inTXTLen );
4463 instance->txtLen = inTXTLen;
4464 }
4465
4466 instance->port = ntohs( inPort );
4467
4468 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
4469 {
4470 DNSServiceRef sdRef;
4471
4472 if( !instance->hostname ) instance->resolveDoneTicks = nowTicks;
4473 FreeNullSafe( instance->hostname );
4474 instance->hostname = strdup( inHostname );
4475 require_action( instance->hostname, exit, err = kNoMemoryErr );
4476
4477 DNSServiceForget( &instance->getAddr );
4478 ForgetIPAddressList( &instance->addrList );
4479
4480 sdRef = instance->context->mainRef;
4481 instance->getAddrStartTicks = UpTicks();
4482 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
4483 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, BrowseAllGAICallback, instance );
4484 require_noerr( err, exit );
4485
4486 instance->getAddr = sdRef;
4487 }
4488
4489 exit:
4490 if( err ) exit( 1 );
4491 }
4492
4493 //===========================================================================================================================
4494 // BrowseAllGAICallback
4495 //===========================================================================================================================
4496
4497 static void DNSSD_API
4498 BrowseAllGAICallback(
4499 DNSServiceRef inSDRef,
4500 DNSServiceFlags inFlags,
4501 uint32_t inInterfaceIndex,
4502 DNSServiceErrorType inError,
4503 const char * inHostname,
4504 const struct sockaddr * inSockAddr,
4505 uint32_t inTTL,
4506 void * inContext )
4507 {
4508 OSStatus err;
4509 BrowseInstance * const instance = (BrowseInstance *) inContext;
4510
4511 Unused( inSDRef );
4512 Unused( inInterfaceIndex );
4513 Unused( inHostname );
4514 Unused( inTTL );
4515
4516 err = inError;
4517 require_noerr( err, exit );
4518
4519 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
4520 {
4521 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
4522 goto exit;
4523 }
4524
4525 if( inFlags & kDNSServiceFlagsAdd )
4526 {
4527 err = BrowseAllAddIPAddress( instance->context, instance, inSockAddr );
4528 if( err == kDuplicateErr ) err = kNoErr;
4529 require_noerr( err, exit );
4530 }
4531 else
4532 {
4533 err = BrowseAllRemoveIPAddress( instance->context, instance, inSockAddr );
4534 if( err == kNotFoundErr ) err = kNoErr;
4535 require_noerr( err, exit );
4536 }
4537
4538 exit:
4539 return;
4540 }
4541
4542 //===========================================================================================================================
4543 // BrowseAllConnectionProgress
4544 //===========================================================================================================================
4545
4546 static void BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
4547 {
4548 BrowseIPAddr * const addr = (BrowseIPAddr *) inArg;
4549
4550 if( inPhase == kAsyncConnectionPhase_Connected )
4551 {
4552 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails;
4553
4554 addr->connectTimeSecs = info->connectSecs;
4555 }
4556 }
4557
4558 //===========================================================================================================================
4559 // BrowseAllConnectionHandler
4560 //===========================================================================================================================
4561
4562 static void BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
4563 {
4564 BrowseIPAddr * const addr = (BrowseIPAddr *) inArg;
4565 BrowseAllContext * const context = addr->context;
4566
4567 if( inError )
4568 {
4569 addr->connectStatus = kConnectStatus_Failed;
4570 addr->connectError = inError;
4571 }
4572 else
4573 {
4574 addr->connectStatus = kConnectStatus_Succeeded;
4575 }
4576
4577 check( context->pendingConnectCount > 0 );
4578 if( --context->pendingConnectCount == 0 )
4579 {
4580 if( context->exitTimer )
4581 {
4582 dispatch_source_forget( &context->exitTimer );
4583 dispatch_async_f( dispatch_get_main_queue(), context, BrowseAllExit );
4584 }
4585 }
4586
4587 ForgetSocket( &inSock );
4588 BrowseIPAddrRelease( addr );
4589 }
4590
4591 //===========================================================================================================================
4592 // BrowseAllStop
4593 //===========================================================================================================================
4594
4595 static void BrowseAllStop( void *inContext )
4596 {
4597 OSStatus err;
4598 BrowseAllContext * const context = (BrowseAllContext *) inContext;
4599 BrowseDomain * domain;
4600 BrowseType * type;
4601 BrowseOp * browse;
4602 BrowseInstance * instance;
4603
4604 DNSServiceForget( &context->domainsQuery );
4605 for( domain = context->domainList; domain; domain = domain->next )
4606 {
4607 DNSServiceForget( &domain->servicesQuery );
4608 for( type = domain->typeList; type; type = type->next )
4609 {
4610 for( browse = type->browseList; browse; browse = browse->next )
4611 {
4612 DNSServiceForget( &browse->browse );
4613 for( instance = browse->instanceList; instance; instance = instance->next )
4614 {
4615 DNSServiceForget( &instance->resolve );
4616 DNSServiceForget( &instance->getAddr );
4617 }
4618 }
4619 }
4620 }
4621 DNSServiceForget( &context->mainRef );
4622
4623 if( ( context->pendingConnectCount > 0 ) && ( context->maxConnectTimeSecs > 0 ) )
4624 {
4625 check( !context->exitTimer );
4626 err = DispatchTimerCreate( dispatch_time_seconds( context->maxConnectTimeSecs ), DISPATCH_TIME_FOREVER,
4627 100 * kNanosecondsPerMillisecond, NULL, BrowseAllExit, NULL, context, &context->exitTimer );
4628 require_noerr( err, exit );
4629 dispatch_resume( context->exitTimer );
4630 }
4631 else
4632 {
4633 dispatch_async_f( dispatch_get_main_queue(), context, BrowseAllExit );
4634 }
4635 err = kNoErr;
4636
4637 exit:
4638 if( err ) exit( 1 );
4639 }
4640
4641 //===========================================================================================================================
4642 // BrowseAllExit
4643 //===========================================================================================================================
4644
4645 #define kStatusStr_CouldConnect "connected"
4646 #define kStatusStr_CouldConnectColored kANSIGreen kStatusStr_CouldConnect kANSINormal
4647 #define kStatusStr_CouldNotConnect "could not connect"
4648 #define kStatusStr_CouldNotConnectColored kANSIRed kStatusStr_CouldNotConnect kANSINormal
4649 #define kStatusStr_NoConnectionAttempted "no connection attempted"
4650 #define kStatusStr_Unknown "unknown"
4651
4652 #define Indent( X ) ( (X) * 4 ), ""
4653
4654 static void BrowseAllExit( void *inContext )
4655 {
4656 BrowseAllContext * const context = (BrowseAllContext *) inContext;
4657 BrowseDomain * domain;
4658 BrowseType * type;
4659 BrowseOp * browse;
4660 BrowseInstance * instance;
4661 BrowseIPAddr * addr;
4662
4663 dispatch_source_forget( &context->exitTimer );
4664
4665 for( domain = context->domainList; domain; domain = domain->next )
4666 {
4667 FPrintF( stdout, "%s\n\n", domain->name );
4668
4669 for( type = domain->typeList; type; type = type->next )
4670 {
4671 const char * desc;
4672
4673 desc = ServiceTypeDescription( type->name );
4674 if( desc ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), desc, type->name );
4675 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name );
4676
4677 for( browse = type->browseList; browse; browse = browse->next )
4678 {
4679 for( instance = browse->instanceList; instance; instance = instance->next )
4680 {
4681 char ifname[ IF_NAMESIZE + 1 ];
4682
4683 FPrintF( stdout, "%*s" "%s via ", Indent( 2 ), instance->name );
4684 if( instance->ifIndex == 0 )
4685 {
4686 FPrintF( stdout, "the Internet" );
4687 }
4688 else if( if_indextoname( instance->ifIndex, ifname ) )
4689 {
4690 NetTransportType netType;
4691
4692 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4693 &netType );
4694 FPrintF( stdout, "%s (%s)",
4695 ( netType == kNetTransportType_Ethernet ) ? "ethernet" : NetTransportTypeToString( netType ),
4696 ifname );
4697 }
4698 else
4699 {
4700 FPrintF( stdout, "interface index %u", instance->ifIndex );
4701 }
4702 FPrintF( stdout, "\n\n" );
4703
4704 if( instance->hostname )
4705 {
4706 char buffer[ 256 ];
4707
4708 SNPrintF( buffer, sizeof( buffer ), "%s:%u", instance->hostname, instance->port );
4709 FPrintF( stdout, "%*s" "%-51s %4llu ms\n", Indent( 3 ), buffer,
4710 UpTicksToMilliseconds( instance->resolveDoneTicks - instance->resolveStartTicks ) );
4711 }
4712 else
4713 {
4714 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
4715 }
4716
4717 for( addr = instance->addrList; addr; addr = addr->next )
4718 {
4719 AsyncConnection_Forget( &addr->connection );
4720
4721 if( addr->connectStatus == kConnectStatus_Pending )
4722 {
4723 addr->connectStatus = kConnectStatus_Failed;
4724 addr->connectError = kTimeoutErr;
4725 }
4726
4727 FPrintF( stdout, "%*s" "%-##47a %4llu ms", Indent( 4 ),
4728 &addr->sip.sa, UpTicksToMilliseconds( addr->foundTicks - instance->getAddrStartTicks ) );
4729 if( context->maxConnectTimeSecs <= 0 )
4730 {
4731 FPrintF( stdout, "\n" );
4732 continue;
4733 }
4734 switch( addr->connectStatus )
4735 {
4736 case kConnectStatus_None:
4737 FPrintF( stdout, " (%s)\n", kStatusStr_NoConnectionAttempted );
4738 break;
4739
4740 case kConnectStatus_Succeeded:
4741 FPrintF( stdout, " (%s in %.2f ms)\n",
4742 context->useColoredText ? kStatusStr_CouldConnectColored : kStatusStr_CouldConnect,
4743 addr->connectTimeSecs * 1000 );
4744 break;
4745
4746 case kConnectStatus_Failed:
4747 FPrintF( stdout, " (%s: %m)\n",
4748 context->useColoredText ? kStatusStr_CouldNotConnectColored : kStatusStr_CouldNotConnect,
4749 addr->connectError );
4750 break;
4751
4752 default:
4753 FPrintF( stdout, " (%s)\n", kStatusStr_Unknown );
4754 break;
4755 }
4756 }
4757
4758 FPrintF( stdout, "\n" );
4759 if( instance->txtLen == 0 ) continue;
4760
4761 FPrintF( stdout, "%*s" "TXT record:\n", Indent( 3 ) );
4762 if( instance->txtLen > 1 )
4763 {
4764 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
4765 }
4766 else
4767 {
4768 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
4769 }
4770 FPrintF( stdout, "\n" );
4771 }
4772 }
4773 FPrintF( stdout, "\n" );
4774 }
4775 }
4776
4777 while( ( domain = context->domainList ) != NULL )
4778 {
4779 context->domainList = domain->next;
4780 BrowseDomainFree( domain );
4781 }
4782
4783 BrowseAllContextRelease( context );
4784 Exit( NULL );
4785 }
4786
4787 //===========================================================================================================================
4788 // BrowseAllAddDomain
4789 //===========================================================================================================================
4790
4791 static OSStatus BrowseAllAddDomain( BrowseAllContext *inContext, const char *inName )
4792 {
4793 OSStatus err;
4794 BrowseDomain * domain;
4795 BrowseDomain ** p;
4796 BrowseDomain * newDomain = NULL;
4797
4798 for( p = &inContext->domainList; ( domain = *p ) != NULL; p = &domain->next )
4799 {
4800 if( strcasecmp( domain->name, inName ) == 0 ) break;
4801 }
4802 require_action_quiet( !domain, exit, err = kDuplicateErr );
4803
4804 newDomain = (BrowseDomain *) calloc( 1, sizeof( *newDomain ) );
4805 require_action( newDomain, exit, err = kNoMemoryErr );
4806
4807 ++inContext->refCount;
4808 newDomain->context = inContext;
4809
4810 newDomain->name = strdup( inName );
4811 require_action( newDomain->name, exit, err = kNoMemoryErr );
4812
4813 if( inContext->serviceTypesCount > 0 )
4814 {
4815 size_t i;
4816
4817 for( i = 0; i < inContext->serviceTypesCount; ++i )
4818 {
4819 err = BrowseAllAddServiceType( inContext, newDomain, inContext->serviceTypes[ i ], inContext->ifIndex,
4820 inContext->includeAWDL );
4821 if( err == kDuplicateErr ) err = kNoErr;
4822 require_noerr( err, exit );
4823 }
4824 }
4825 else
4826 {
4827 char * recordName;
4828 DNSServiceFlags flags;
4829 DNSServiceRef sdRef;
4830
4831 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
4832 require_action( recordName, exit, err = kNoMemoryErr );
4833
4834 flags = kDNSServiceFlagsShareConnection;
4835 if( inContext->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
4836
4837 sdRef = newDomain->context->mainRef;
4838 err = DNSServiceQueryRecord( &sdRef, flags, inContext->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
4839 BrowseAllQueryCallback, newDomain );
4840 free( recordName );
4841 require_noerr( err, exit );
4842
4843 newDomain->servicesQuery = sdRef;
4844 }
4845
4846 *p = newDomain;
4847 newDomain = NULL;
4848 err = kNoErr;
4849
4850 exit:
4851 if( newDomain ) BrowseDomainFree( newDomain );
4852 return( err );
4853 }
4854
4855 //===========================================================================================================================
4856 // BrowseAllRemoveDomain
4857 //===========================================================================================================================
4858
4859 static OSStatus BrowseAllRemoveDomain( BrowseAllContext *inContext, const char *inName )
4860 {
4861 OSStatus err;
4862 BrowseDomain * domain;
4863 BrowseDomain ** p;
4864
4865 for( p = &inContext->domainList; ( domain = *p ) != NULL; p = &domain->next )
4866 {
4867 if( strcasecmp( domain->name, inName ) == 0 ) break;
4868 }
4869
4870 if( domain )
4871 {
4872 *p = domain->next;
4873 BrowseDomainFree( domain );
4874 err = kNoErr;
4875 }
4876 else
4877 {
4878 err = kNotFoundErr;
4879 }
4880
4881 return( err );
4882 }
4883
4884 //===========================================================================================================================
4885 // BrowseAllContextRelease
4886 //===========================================================================================================================
4887
4888 static void BrowseAllContextRelease( BrowseAllContext *inContext )
4889 {
4890 if( --inContext->refCount == 0 )
4891 {
4892 check( !inContext->domainsQuery );
4893 check( !inContext->domainList );
4894 check( !inContext->exitTimer );
4895 check( !inContext->pendingConnectCount );
4896 DNSServiceForget( &inContext->mainRef );
4897 if( inContext->serviceTypes )
4898 {
4899 StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
4900 inContext->serviceTypes = NULL;
4901 inContext->serviceTypesCount = 0;
4902 }
4903 free( inContext );
4904 }
4905 }
4906
4907 //===========================================================================================================================
4908 // BrowseAllAddServiceType
4909 //===========================================================================================================================
4910
4911 static OSStatus
4912 BrowseAllAddServiceType(
4913 BrowseAllContext * inContext,
4914 BrowseDomain * inDomain,
4915 const char * inName,
4916 uint32_t inIfIndex,
4917 Boolean inIncludeAWDL )
4918 {
4919 OSStatus err;
4920 DNSServiceRef sdRef;
4921 DNSServiceFlags flags;
4922 BrowseType * type;
4923 BrowseType ** typePtr;
4924 BrowseType * newType = NULL;
4925 BrowseOp * browse;
4926 BrowseOp ** browsePtr;
4927 BrowseOp * newBrowse = NULL;
4928
4929 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
4930 {
4931 if( strcasecmp( type->name, inName ) == 0 ) break;
4932 }
4933 if( !type )
4934 {
4935 newType = (BrowseType *) calloc( 1, sizeof( *newType ) );
4936 require_action( newType, exit, err = kNoMemoryErr );
4937
4938 newType->name = strdup( inName );
4939 require_action( newType->name, exit, err = kNoMemoryErr );
4940
4941 type = newType;
4942 }
4943
4944 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
4945 {
4946 if( browse->ifIndex == inIfIndex ) break;
4947 }
4948 require_action_quiet( !browse, exit, err = kDuplicateErr );
4949
4950 newBrowse = (BrowseOp *) calloc( 1, sizeof( *newBrowse ) );
4951 require_action( newBrowse, exit, err = kNoMemoryErr );
4952
4953 ++inContext->refCount;
4954 newBrowse->context = inContext;
4955 newBrowse->ifIndex = inIfIndex;
4956 if( stricmp_suffix( inName, "._tcp" ) == 0 ) newBrowse->isTCP = true;
4957
4958 flags = kDNSServiceFlagsShareConnection;
4959 if( inIncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
4960
4961 newBrowse->startTicks = UpTicks();
4962
4963 sdRef = inContext->mainRef;
4964 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, BrowseAllBrowseCallback,
4965 newBrowse );
4966 require_noerr( err, exit );
4967
4968 newBrowse->browse = sdRef;
4969 *browsePtr = newBrowse;
4970 newBrowse = NULL;
4971
4972 if( newType )
4973 {
4974 *typePtr = newType;
4975 newType = NULL;
4976 }
4977
4978 exit:
4979 if( newBrowse ) BrowseOpFree( newBrowse );
4980 if( newType ) BrowseTypeFree( newType );
4981 return( err );
4982 }
4983
4984 //===========================================================================================================================
4985 // BrowseAllRemoveServiceType
4986 //===========================================================================================================================
4987
4988 static OSStatus
4989 BrowseAllRemoveServiceType(
4990 BrowseAllContext * inContext,
4991 BrowseDomain * inDomain,
4992 const char * inName,
4993 uint32_t inIfIndex )
4994 {
4995 OSStatus err;
4996 BrowseType * type;
4997 BrowseType ** typePtr;
4998 BrowseOp * browse;
4999 BrowseOp ** browsePtr;
5000
5001 Unused( inContext );
5002
5003 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
5004 {
5005 if( strcasecmp( type->name, inName ) == 0 ) break;
5006 }
5007 require_action_quiet( type, exit, err = kNotFoundErr );
5008
5009 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
5010 {
5011 if( browse->ifIndex == inIfIndex ) break;
5012 }
5013 require_action_quiet( browse, exit, err = kNotFoundErr );
5014
5015 *browsePtr = browse->next;
5016 BrowseOpFree( browse );
5017 if( !type->browseList )
5018 {
5019 *typePtr = type->next;
5020 BrowseTypeFree( type );
5021 }
5022 err = kNoErr;
5023
5024 exit:
5025 return( err );
5026 }
5027
5028 //===========================================================================================================================
5029 // BrowseAllAddServiceInstance
5030 //===========================================================================================================================
5031
5032 static OSStatus
5033 BrowseAllAddServiceInstance(
5034 BrowseAllContext * inContext,
5035 BrowseOp * inBrowse,
5036 const char * inName,
5037 const char * inRegType,
5038 const char * inDomain,
5039 uint32_t inIfIndex )
5040 {
5041 OSStatus err;
5042 DNSServiceRef sdRef;
5043 BrowseInstance * instance;
5044 BrowseInstance ** p;
5045 const uint64_t nowTicks = UpTicks();
5046 BrowseInstance * newInstance = NULL;
5047
5048 for( p = &inBrowse->instanceList; ( instance = *p ) != NULL; p = &instance->next )
5049 {
5050 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
5051 }
5052 require_action_quiet( !instance, exit, err = kDuplicateErr );
5053
5054 newInstance = (BrowseInstance *) calloc( 1, sizeof( *newInstance ) );
5055 require_action( newInstance, exit, err = kNoMemoryErr );
5056
5057 ++inContext->refCount;
5058 newInstance->context = inContext;
5059 newInstance->foundTicks = nowTicks;
5060 newInstance->ifIndex = inIfIndex;
5061 newInstance->isTCP = inBrowse->isTCP;
5062
5063 newInstance->name = strdup( inName );
5064 require_action( newInstance->name, exit, err = kNoMemoryErr );
5065
5066 sdRef = inContext->mainRef;
5067 newInstance->resolveStartTicks = UpTicks();
5068 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
5069 BrowseAllResolveCallback, newInstance );
5070 require_noerr( err, exit );
5071
5072 newInstance->resolve = sdRef;
5073 *p = newInstance;
5074 newInstance = NULL;
5075
5076 exit:
5077 if( newInstance ) BrowseInstanceFree( newInstance );
5078 return( err );
5079 }
5080
5081 //===========================================================================================================================
5082 // BrowseAllRemoveServiceInstance
5083 //===========================================================================================================================
5084
5085 static OSStatus
5086 BrowseAllRemoveServiceInstance(
5087 BrowseAllContext * inContext,
5088 BrowseOp * inBrowse,
5089 const char * inName,
5090 uint32_t inIfIndex )
5091 {
5092 OSStatus err;
5093 BrowseInstance * instance;
5094 BrowseInstance ** p;
5095
5096 Unused( inContext );
5097
5098 for( p = &inBrowse->instanceList; ( instance = *p ) != NULL; p = &instance->next )
5099 {
5100 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
5101 }
5102 require_action_quiet( instance, exit, err = kNotFoundErr );
5103
5104 *p = instance->next;
5105 BrowseInstanceFree( instance );
5106 err = kNoErr;
5107
5108 exit:
5109 return( err );
5110 }
5111
5112 //===========================================================================================================================
5113 // BrowseAllAddIPAddress
5114 //===========================================================================================================================
5115
5116 #define kDiscardProtocolPort 9
5117
5118 static OSStatus
5119 BrowseAllAddIPAddress(
5120 BrowseAllContext * inContext,
5121 BrowseInstance * inInstance,
5122 const struct sockaddr * inSockAddr )
5123 {
5124 OSStatus err;
5125 BrowseIPAddr * addr;
5126 BrowseIPAddr ** p;
5127 const uint64_t nowTicks = UpTicks();
5128 BrowseIPAddr * newAddr = NULL;
5129
5130 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
5131 {
5132 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
5133 err = kTypeErr;
5134 goto exit;
5135 }
5136
5137 for( p = &inInstance->addrList; ( addr = *p ) != NULL; p = &addr->next )
5138 {
5139 if( SockAddrCompareAddr( &addr->sip, inSockAddr ) == 0 ) break;
5140 }
5141 require_action_quiet( !addr, exit, err = kDuplicateErr );
5142
5143 newAddr = (BrowseIPAddr *) calloc( 1, sizeof( *newAddr ) );
5144 require_action( newAddr, exit, err = kNoMemoryErr );
5145
5146 ++inContext->refCount;
5147 newAddr->refCount = 1;
5148 newAddr->context = inContext;
5149 newAddr->foundTicks = nowTicks;
5150 SockAddrCopy( inSockAddr, &newAddr->sip.sa );
5151
5152 if( ( inContext->maxConnectTimeSecs > 0 ) && inInstance->isTCP && ( inInstance->port != kDiscardProtocolPort ) )
5153 {
5154 char destination[ kSockAddrStringMaxSize ];
5155
5156 err = SockAddrToString( &newAddr->sip, kSockAddrStringFlagsNoPort, destination );
5157 require_noerr( err, exit );
5158
5159 err = AsyncConnection_Connect( &newAddr->connection, destination, -inInstance->port, kAsyncConnectionFlag_P2P,
5160 kAsyncConnectionNoTimeout, kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
5161 BrowseAllConnectionProgress, newAddr, BrowseAllConnectionHandler, newAddr, dispatch_get_main_queue() );
5162 require_noerr( err, exit );
5163
5164 ++newAddr->refCount;
5165 newAddr->connectStatus = kConnectStatus_Pending;
5166 ++inContext->pendingConnectCount;
5167 }
5168
5169 *p = newAddr;
5170 newAddr = NULL;
5171 err = kNoErr;
5172
5173 exit:
5174 if( newAddr ) BrowseIPAddrRelease( newAddr );
5175 return( err );
5176 }
5177
5178 //===========================================================================================================================
5179 // BrowseAllRemoveIPAddress
5180 //===========================================================================================================================
5181
5182 static OSStatus
5183 BrowseAllRemoveIPAddress(
5184 BrowseAllContext * inContext,
5185 BrowseInstance * inInstance,
5186 const struct sockaddr * inSockAddr )
5187 {
5188 OSStatus err;
5189 BrowseIPAddr * addr;
5190 BrowseIPAddr ** p;
5191
5192 Unused( inContext );
5193
5194 for( p = &inInstance->addrList; ( addr = *p ) != NULL; p = &addr->next )
5195 {
5196 if( SockAddrCompareAddr( &addr->sip.sa, inSockAddr ) == 0 ) break;
5197 }
5198 require_action_quiet( addr, exit, err = kNotFoundErr );
5199
5200 *p = addr->next;
5201 BrowseIPAddrRelease( addr );
5202 err = kNoErr;
5203
5204 exit:
5205 return( err );
5206 }
5207
5208 //===========================================================================================================================
5209 // BrowseDomainFree
5210 //===========================================================================================================================
5211
5212 static void BrowseDomainFree( BrowseDomain *inDomain )
5213 {
5214 BrowseType * type;
5215
5216 ForgetBrowseAllContext( &inDomain->context );
5217 ForgetMem( &inDomain->name );
5218 DNSServiceForget( &inDomain->servicesQuery );
5219 while( ( type = inDomain->typeList ) != NULL )
5220 {
5221 inDomain->typeList = type->next;
5222 BrowseTypeFree( type );
5223 }
5224 free( inDomain );
5225 }
5226
5227 //===========================================================================================================================
5228 // BrowseTypeFree
5229 //===========================================================================================================================
5230
5231 static void BrowseTypeFree( BrowseType *inType )
5232 {
5233 BrowseOp * browse;
5234
5235 ForgetMem( &inType->name );
5236 while( ( browse = inType->browseList ) != NULL )
5237 {
5238 inType->browseList = browse->next;
5239 BrowseOpFree( browse );
5240 }
5241 free( inType );
5242 }
5243
5244 //===========================================================================================================================
5245 // BrowseOpFree
5246 //===========================================================================================================================
5247
5248 static void BrowseOpFree( BrowseOp *inBrowse )
5249 {
5250 BrowseInstance * instance;
5251
5252 ForgetBrowseAllContext( &inBrowse->context );
5253 DNSServiceForget( &inBrowse->browse );
5254 while( ( instance = inBrowse->instanceList ) != NULL )
5255 {
5256 inBrowse->instanceList = instance->next;
5257 BrowseInstanceFree( instance );
5258 }
5259 free( inBrowse );
5260 }
5261
5262 //===========================================================================================================================
5263 // BrowseInstanceFree
5264 //===========================================================================================================================
5265
5266 static void BrowseInstanceFree( BrowseInstance *inInstance )
5267 {
5268 ForgetBrowseAllContext( &inInstance->context );
5269 ForgetMem( &inInstance->name );
5270 DNSServiceForget( &inInstance->resolve );
5271 DNSServiceForget( &inInstance->getAddr );
5272 ForgetMem( &inInstance->txtPtr );
5273 ForgetMem( &inInstance->hostname );
5274 ForgetIPAddressList( &inInstance->addrList );
5275 free( inInstance );
5276 }
5277
5278 //===========================================================================================================================
5279 // BrowseIPAddrRelease
5280 //===========================================================================================================================
5281
5282 static void BrowseIPAddrRelease( BrowseIPAddr *inAddr )
5283 {
5284 AsyncConnection_Forget( &inAddr->connection );
5285 if( --inAddr->refCount == 0 )
5286 {
5287 ForgetBrowseAllContext( &inAddr->context );
5288 free( inAddr );
5289 }
5290 }
5291
5292 //===========================================================================================================================
5293 // BrowseIPAddrReleaseList
5294 //===========================================================================================================================
5295
5296 static void BrowseIPAddrReleaseList( BrowseIPAddr *inList )
5297 {
5298 BrowseIPAddr * addr;
5299
5300 while( ( addr = inList ) != NULL )
5301 {
5302 inList = addr->next;
5303 BrowseIPAddrRelease( addr );
5304 }
5305 }
5306
5307 //===========================================================================================================================
5308 // GetNameInfoCmd
5309 //===========================================================================================================================
5310
5311 const FlagStringPair kGetNameInfoFlagStringPairs[] =
5312 {
5313 CaseFlagStringify( NI_NUMERICSCOPE ),
5314 CaseFlagStringify( NI_DGRAM ),
5315 CaseFlagStringify( NI_NUMERICSERV ),
5316 CaseFlagStringify( NI_NAMEREQD ),
5317 CaseFlagStringify( NI_NUMERICHOST ),
5318 CaseFlagStringify( NI_NOFQDN ),
5319 { 0, NULL }
5320 };
5321
5322 static void GetNameInfoCmd( void )
5323 {
5324 OSStatus err;
5325 sockaddr_ip sip;
5326 size_t sockAddrLen;
5327 unsigned int flags;
5328 const FlagStringPair * pair;
5329 struct timeval now;
5330 char host[ NI_MAXHOST ];
5331 char serv[ NI_MAXSERV ];
5332
5333 err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen );
5334 check_noerr( err );
5335 if( err )
5336 {
5337 FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress );
5338 goto exit;
5339 }
5340
5341 flags = 0;
5342 if( gGetNameInfoFlag_DGram ) flags |= NI_DGRAM;
5343 if( gGetNameInfoFlag_NameReqd ) flags |= NI_NAMEREQD;
5344 if( gGetNameInfoFlag_NoFQDN ) flags |= NI_NOFQDN;
5345 if( gGetNameInfoFlag_NumericHost ) flags |= NI_NUMERICHOST;
5346 if( gGetNameInfoFlag_NumericScope ) flags |= NI_NUMERICSCOPE;
5347 if( gGetNameInfoFlag_NumericServ ) flags |= NI_NUMERICSERV;
5348
5349 // Print prologue.
5350
5351 FPrintF( stdout, "SockAddr: %##a\n", &sip.sa );
5352 FPrintF( stdout, "Flags: 0x%X < ", flags );
5353 for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair )
5354 {
5355 if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str );
5356 }
5357 FPrintF( stdout, ">\n" );
5358 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5359 FPrintF( stdout, "---\n" );
5360
5361 // Call getnameinfo().
5362
5363 err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ),
5364 (int) flags );
5365 gettimeofday( &now, NULL );
5366 if( err )
5367 {
5368 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
5369 }
5370 else
5371 {
5372 FPrintF( stdout, "host: %s\n", host );
5373 FPrintF( stdout, "serv: %s\n", serv );
5374 }
5375 FPrintF( stdout, "---\n" );
5376 FPrintF( stdout, "End time: %{du:time}\n", &now );
5377
5378 exit:
5379 gExitCode = err ? 1 : 0;
5380 }
5381
5382 //===========================================================================================================================
5383 // GetAddrInfoStressCmd
5384 //===========================================================================================================================
5385
5386 typedef struct
5387 {
5388 DNSServiceRef mainRef;
5389 DNSServiceRef sdRef;
5390 DNSServiceFlags flags;
5391 unsigned int interfaceIndex;
5392 unsigned int connectionNumber;
5393 unsigned int requestCount;
5394 unsigned int requestCountMax;
5395 unsigned int requestCountLimit;
5396 unsigned int durationMinMs;
5397 unsigned int durationMaxMs;
5398
5399 } GAIStressContext;
5400
5401 static void GetAddrInfoStressEvent( void *inContext );
5402 static void DNSSD_API
5403 GetAddrInfoStressCallback(
5404 DNSServiceRef inSDRef,
5405 DNSServiceFlags inFlags,
5406 uint32_t inInterfaceIndex,
5407 DNSServiceErrorType inError,
5408 const char * inHostname,
5409 const struct sockaddr * inSockAddr,
5410 uint32_t inTTL,
5411 void * inContext );
5412
5413 static void GetAddrInfoStressCmd( void )
5414 {
5415 OSStatus err;
5416 GAIStressContext * context = NULL;
5417 int i;
5418 DNSServiceFlags flags;
5419 uint32_t ifIndex;
5420 char ifName[ kInterfaceNameBufLen ];
5421
5422 if( gGAIStress_TestDurationSecs < 0 )
5423 {
5424 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
5425 err = kParamErr;
5426 goto exit;
5427 }
5428 if( gGAIStress_ConnectionCount <= 0 )
5429 {
5430 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
5431 err = kParamErr;
5432 goto exit;
5433 }
5434 if( gGAIStress_DurationMinMs <= 0 )
5435 {
5436 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
5437 err = kParamErr;
5438 goto exit;
5439 }
5440 if( gGAIStress_DurationMaxMs <= 0 )
5441 {
5442 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
5443 err = kParamErr;
5444 goto exit;
5445 }
5446 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
5447 {
5448 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5449 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
5450 err = kParamErr;
5451 goto exit;
5452 }
5453 if( gGAIStress_RequestCountMax <= 0 )
5454 {
5455 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
5456 err = kParamErr;
5457 goto exit;
5458 }
5459
5460 // Set flags.
5461
5462 flags = GetDNSSDFlagsFromOpts();
5463
5464 // Set interface index.
5465
5466 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
5467 require_noerr_quiet( err, exit );
5468
5469 for( i = 0; i < gGAIStress_ConnectionCount; ++i )
5470 {
5471 context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
5472 require_action( context, exit, err = kNoMemoryErr );
5473
5474 context->flags = flags;
5475 context->interfaceIndex = ifIndex;
5476 context->connectionNumber = (unsigned int)( i + 1 );
5477 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax;
5478 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs;
5479 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs;
5480
5481 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5482 context = NULL;
5483 }
5484
5485 if( gGAIStress_TestDurationSecs > 0 )
5486 {
5487 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
5488 }
5489
5490 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
5491 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
5492 FPrintF( stdout, "Test duration: " );
5493 if( gGAIStress_TestDurationSecs == 0 )
5494 {
5495 FPrintF( stdout, "∞\n" );
5496 }
5497 else
5498 {
5499 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
5500 }
5501 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount );
5502 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs );
5503 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs );
5504 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax );
5505 FPrintF( stdout, "Start time: %{du:time}\n", NULL);
5506 FPrintF( stdout, "---\n" );
5507
5508 dispatch_main();
5509
5510 exit:
5511 FreeNullSafe( context );
5512 if( err ) exit( 1 );
5513 }
5514
5515 //===========================================================================================================================
5516 // GetAddrInfoStressEvent
5517 //===========================================================================================================================
5518
5519 #define kStressRandStrLen 5
5520
5521 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5522
5523 static void GetAddrInfoStressEvent( void *inContext )
5524 {
5525 GAIStressContext * const context = (GAIStressContext *) inContext;
5526 OSStatus err;
5527 DNSServiceRef sdRef;
5528 unsigned int nextMs;
5529 char randomStr[ kStressRandStrLen + 1 ];
5530 char hostname[ kStressRandStrLen + 4 + 1 ];
5531 Boolean isConnectionNew = false;
5532 static Boolean printedHeader = false;
5533
5534 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
5535 {
5536 DNSServiceForget( &context->mainRef );
5537 context->sdRef = NULL;
5538 context->requestCount = 0;
5539 context->requestCountLimit = RandomRange( 1, context->requestCountMax );
5540
5541 err = DNSServiceCreateConnection( &context->mainRef );
5542 require_noerr( err, exit );
5543
5544 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
5545 require_noerr( err, exit );
5546
5547 isConnectionNew = true;
5548 }
5549
5550 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
5551 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
5552
5553 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
5554
5555 if( !printedHeader )
5556 {
5557 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5558 printedHeader = true;
5559 }
5560 FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n",
5561 NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
5562
5563 DNSServiceForget( &context->sdRef );
5564 sdRef = context->mainRef;
5565 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
5566 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
5567 require_noerr( err, exit );
5568 context->sdRef = sdRef;
5569
5570 context->requestCount++;
5571
5572 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5573
5574 exit:
5575 if( err ) exit( 1 );
5576 }
5577
5578 //===========================================================================================================================
5579 // GetAddrInfoStressCallback
5580 //===========================================================================================================================
5581
5582 static void DNSSD_API
5583 GetAddrInfoStressCallback(
5584 DNSServiceRef inSDRef,
5585 DNSServiceFlags inFlags,
5586 uint32_t inInterfaceIndex,
5587 DNSServiceErrorType inError,
5588 const char * inHostname,
5589 const struct sockaddr * inSockAddr,
5590 uint32_t inTTL,
5591 void * inContext )
5592 {
5593 Unused( inSDRef );
5594 Unused( inFlags );
5595 Unused( inInterfaceIndex );
5596 Unused( inError );
5597 Unused( inHostname );
5598 Unused( inSockAddr );
5599 Unused( inTTL );
5600 Unused( inContext );
5601 }
5602
5603 //===========================================================================================================================
5604 // DNSQueryCmd
5605 //===========================================================================================================================
5606
5607 typedef struct
5608 {
5609 sockaddr_ip serverAddr;
5610 uint64_t sendTicks;
5611 uint8_t * msgPtr;
5612 size_t msgLen;
5613 size_t msgOffset;
5614 const char * name;
5615 dispatch_source_t readSource;
5616 SocketRef sock;
5617 int timeLimitSecs;
5618 uint16_t queryID;
5619 uint16_t type;
5620 Boolean haveTCPLen;
5621 Boolean useTCP;
5622 Boolean printRawRData; // True if RDATA results are not to be formatted.
5623 uint8_t msgBuf[ 512 ];
5624
5625 } DNSQueryContext;
5626
5627 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext );
5628 static void DNSQueryReadHandler( void *inContext );
5629 static void DNSQueryCancelHandler( void *inContext );
5630
5631 static void DNSQueryCmd( void )
5632 {
5633 OSStatus err;
5634 DNSQueryContext * context = NULL;
5635 uint8_t * msgPtr;
5636 size_t msgLen, sendLen;
5637
5638 // Check command parameters.
5639
5640 if( gDNSQuery_TimeLimitSecs < -1 )
5641 {
5642 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
5643 err = kParamErr;
5644 goto exit;
5645 }
5646 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
5647 {
5648 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
5649 err = kParamErr;
5650 goto exit;
5651 }
5652
5653 // Create context.
5654
5655 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
5656 require_action( context, exit, err = kNoMemoryErr );
5657
5658 context->name = gDNSQuery_Name;
5659 context->sock = kInvalidSocketRef;
5660 context->timeLimitSecs = gDNSQuery_TimeLimitSecs;
5661 context->queryID = (uint16_t) Random32();
5662 context->useTCP = gDNSQuery_UseTCP ? true : false;
5663 context->printRawRData = gDNSQuery_RawRData ? true : false;
5664
5665 #if( TARGET_OS_DARWIN )
5666 if( gDNSQuery_Server )
5667 #endif
5668 {
5669 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5670 require_noerr( err, exit );
5671 }
5672 #if( TARGET_OS_DARWIN )
5673 else
5674 {
5675 err = GetDefaultDNSServer( &context->serverAddr );
5676 require_noerr( err, exit );
5677 }
5678 #endif
5679 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
5680
5681 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
5682 require_noerr( err, exit );
5683
5684 // Write query message.
5685
5686 check_compile_time_code( sizeof( context->msgBuf ) >= ( kDNSQueryMessageMaxLen + 2 ) );
5687
5688 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
5689 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
5690 kDNSServiceClass_IN, &msgLen );
5691 require_noerr( err, exit );
5692 check( msgLen <= UINT16_MAX );
5693
5694 if( context->useTCP )
5695 {
5696 WriteBig16( context->msgBuf, msgLen );
5697 sendLen = 2 + msgLen;
5698 }
5699 else
5700 {
5701 sendLen = msgLen;
5702 }
5703
5704 DNSQueryPrintPrologue( context );
5705
5706 if( gDNSQuery_Verbose )
5707 {
5708 FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr, msgLen );
5709 FPrintF( stdout, "---\n" );
5710 }
5711
5712 if( context->useTCP )
5713 {
5714 // Create TCP socket.
5715
5716 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
5717 err = map_socket_creation_errno( context->sock );
5718 require_noerr( err, exit );
5719
5720 err = SocketConnect( context->sock, &context->serverAddr, 5 );
5721 require_noerr( err, exit );
5722 }
5723 else
5724 {
5725 // Create UDP socket.
5726
5727 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
5728 require_noerr( err, exit );
5729 }
5730
5731 context->sendTicks = UpTicks();
5732 err = SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
5733 require_noerr( err, exit );
5734
5735 if( context->timeLimitSecs == 0 ) goto exit;
5736
5737 err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context,
5738 &context->readSource );
5739 require_noerr( err, exit );
5740 dispatch_resume( context->readSource );
5741
5742 if( context->timeLimitSecs > 0 )
5743 {
5744 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5745 Exit );
5746 }
5747 dispatch_main();
5748
5749 exit:
5750 if( context )
5751 {
5752 dispatch_source_forget( &context->readSource );
5753 ForgetSocket( &context->sock );
5754 free( context );
5755 }
5756 if( err ) exit( 1 );
5757 }
5758
5759 //===========================================================================================================================
5760 // DNSQueryPrintPrologue
5761 //===========================================================================================================================
5762
5763 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext )
5764 {
5765 const int timeLimitSecs = inContext->timeLimitSecs;
5766
5767 FPrintF( stdout, "Name: %s\n", inContext->name );
5768 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type );
5769 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr );
5770 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" );
5771 FPrintF( stdout, "Time limit: " );
5772 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
5773 else FPrintF( stdout, "∞\n" );
5774 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5775 FPrintF( stdout, "---\n" );
5776 }
5777
5778 //===========================================================================================================================
5779 // DNSQueryReadHandler
5780 //===========================================================================================================================
5781
5782 static void DNSQueryReadHandler( void *inContext )
5783 {
5784 OSStatus err;
5785 struct timeval now;
5786 const uint64_t nowTicks = UpTicks();
5787 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5788
5789 gettimeofday( &now, NULL );
5790
5791 if( context->useTCP )
5792 {
5793 if( !context->haveTCPLen )
5794 {
5795 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
5796 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5797 require_noerr( err, exit );
5798
5799 context->msgOffset = 0;
5800 context->msgLen = ReadBig16( context->msgBuf );
5801 context->haveTCPLen = true;
5802 if( context->msgLen <= sizeof( context->msgBuf ) )
5803 {
5804 context->msgPtr = context->msgBuf;
5805 }
5806 else
5807 {
5808 context->msgPtr = (uint8_t *) malloc( context->msgLen );
5809 require_action( context->msgPtr, exit, err = kNoMemoryErr );
5810 }
5811 }
5812
5813 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
5814 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5815 require_noerr( err, exit );
5816 context->msgOffset = 0;
5817 context->haveTCPLen = false;
5818 }
5819 else
5820 {
5821 sockaddr_ip fromAddr;
5822
5823 context->msgPtr = context->msgBuf;
5824 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
5825 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5826 require_noerr( err, exit );
5827
5828 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5829 }
5830
5831 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
5832 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5833 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5834 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5835 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgPtr, context->msgLen );
5836
5837 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
5838 {
5839 Exit( kExitReason_ReceivedResponse );
5840 }
5841
5842 exit:
5843 if( err ) dispatch_source_forget( &context->readSource );
5844 }
5845
5846 //===========================================================================================================================
5847 // DNSQueryCancelHandler
5848 //===========================================================================================================================
5849
5850 static void DNSQueryCancelHandler( void *inContext )
5851 {
5852 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5853
5854 check( !context->readSource );
5855 ForgetSocket( &context->sock );
5856 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
5857 free( context );
5858 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
5859 }
5860
5861 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5862 //===========================================================================================================================
5863 // DNSCryptCmd
5864 //===========================================================================================================================
5865
5866 #define kDNSCryptPort 443
5867
5868 #define kDNSCryptMinPadLength 8
5869 #define kDNSCryptMaxPadLength 256
5870 #define kDNSCryptBlockSize 64
5871 #define kDNSCryptCertMinimumLength 124
5872 #define kDNSCryptClientMagicLength 8
5873 #define kDNSCryptResolverMagicLength 8
5874 #define kDNSCryptHalfNonceLength 12
5875 #define kDNSCryptCertMagicLength 4
5876
5877 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
5878
5879 static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
5880 static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
5881 {
5882 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5883 };
5884
5885 typedef struct
5886 {
5887 uint8_t certMagic[ kDNSCryptCertMagicLength ];
5888 uint8_t esVersion[ 2 ];
5889 uint8_t minorVersion[ 2 ];
5890 uint8_t signature[ crypto_sign_BYTES ];
5891 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ];
5892 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5893 uint8_t serial[ 4 ];
5894 uint8_t startTime[ 4 ];
5895 uint8_t endTime[ 4 ];
5896 uint8_t extensions[ 1 ]; // Variably-sized extension data.
5897
5898 } DNSCryptCert;
5899
5900 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
5901
5902 typedef struct
5903 {
5904 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5905 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5906 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5907 uint8_t poly1305MAC[ 16 ];
5908
5909 } DNSCryptQueryHeader;
5910
5911 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
5912 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
5913 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
5914 offsetof( DNSCryptQueryHeader, poly1305MAC ) );
5915
5916 typedef struct
5917 {
5918 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ];
5919 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5920 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ];
5921 uint8_t poly1305MAC[ 16 ];
5922
5923 } DNSCryptResponseHeader;
5924
5925 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
5926 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
5927 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
5928 sizeof( DNSCryptResponseHeader ) );
5929
5930 typedef struct
5931 {
5932 sockaddr_ip serverAddr;
5933 uint64_t sendTicks;
5934 const char * providerName;
5935 const char * qname;
5936 const uint8_t * certPtr;
5937 size_t certLen;
5938 dispatch_source_t readSource;
5939 size_t msgLen;
5940 int timeLimitSecs;
5941 uint16_t queryID;
5942 uint16_t qtype;
5943 Boolean printRawRData;
5944 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
5945 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
5946 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5947 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ];
5948 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5949 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5950 uint8_t nmKey[ crypto_box_BEFORENMBYTES ];
5951 uint8_t msgBuf[ 512 ];
5952
5953 } DNSCryptContext;
5954
5955 static void DNSCryptReceiveCertHandler( void *inContext );
5956 static void DNSCryptReceiveResponseHandler( void *inContext );
5957 static void DNSCryptProceed( void *inContext );
5958 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext );
5959 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext );
5960 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext );
5961 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
5962
5963 static void DNSCryptCmd( void )
5964 {
5965 OSStatus err;
5966 DNSCryptContext * context = NULL;
5967 size_t writtenBytes;
5968 size_t totalBytes;
5969 SocketContext * sockCtx;
5970 SocketRef sock = kInvalidSocketRef;
5971 const char * ptr;
5972
5973 // Check command parameters.
5974
5975 if( gDNSCrypt_TimeLimitSecs < -1 )
5976 {
5977 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
5978 err = kParamErr;
5979 goto exit;
5980 }
5981
5982 // Create context.
5983
5984 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
5985 require_action( context, exit, err = kNoMemoryErr );
5986
5987 context->providerName = gDNSCrypt_ProviderName;
5988 context->qname = gDNSCrypt_Name;
5989 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs;
5990 context->printRawRData = gDNSCrypt_RawRData ? true : false;
5991
5992 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
5993 require_noerr( err, exit );
5994
5995 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
5996 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
5997 if( err || ( *ptr != '\0' ) )
5998 {
5999 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
6000 goto exit;
6001 }
6002 else if( totalBytes != sizeof( context->serverPublicSignKey ) )
6003 {
6004 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
6005 totalBytes, sizeof( context->serverPublicSignKey ) );
6006 err = kSizeErr;
6007 goto exit;
6008 }
6009 check( writtenBytes == totalBytes );
6010
6011 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
6012 require_noerr( err, exit );
6013 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
6014
6015 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
6016 require_noerr( err, exit );
6017
6018 // Write query message.
6019
6020 context->queryID = (uint16_t) Random32();
6021 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
6022 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
6023 require_noerr( err, exit );
6024
6025 // Create UDP socket.
6026
6027 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
6028 require_noerr( err, exit );
6029
6030 // Send DNS query.
6031
6032 context->sendTicks = UpTicks();
6033 err = SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
6034 require_noerr( err, exit );
6035
6036 err = SocketContextCreate( sock, context, &sockCtx );
6037 require_noerr( err, exit );
6038 sock = kInvalidSocketRef;
6039
6040 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx,
6041 &context->readSource );
6042 if( err ) ForgetSocketContext( &sockCtx );
6043 require_noerr( err, exit );
6044
6045 dispatch_resume( context->readSource );
6046
6047 if( context->timeLimitSecs > 0 )
6048 {
6049 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6050 Exit );
6051 }
6052 dispatch_main();
6053
6054 exit:
6055 if( context ) free( context );
6056 ForgetSocket( &sock );
6057 if( err ) exit( 1 );
6058 }
6059
6060 //===========================================================================================================================
6061 // DNSCryptReceiveCertHandler
6062 //===========================================================================================================================
6063
6064 static void DNSCryptReceiveCertHandler( void *inContext )
6065 {
6066 OSStatus err;
6067 struct timeval now;
6068 const uint64_t nowTicks = UpTicks();
6069 SocketContext * const sockCtx = (SocketContext *) inContext;
6070 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6071 const DNSHeader * hdr;
6072 sockaddr_ip fromAddr;
6073 const uint8_t * ptr;
6074 const uint8_t * txtPtr;
6075 size_t txtLen;
6076 unsigned int answerCount, i;
6077 uint8_t targetName[ kDomainNameLengthMax ];
6078
6079 gettimeofday( &now, NULL );
6080
6081 dispatch_source_forget( &context->readSource );
6082
6083 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6084 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6085 require_noerr( err, exit );
6086 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6087
6088 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6089 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6090 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6091 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6092 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgBuf, context->msgLen );
6093
6094 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6095
6096 hdr = (DNSHeader *) context->msgBuf;
6097 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
6098
6099 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
6100 require_noerr( err, exit );
6101
6102 err = DomainNameFromString( targetName, context->providerName, NULL );
6103 require_noerr( err, exit );
6104
6105 answerCount = DNSHeaderGetAnswerCount( hdr );
6106 for( i = 0; i < answerCount; ++i )
6107 {
6108 uint16_t type;
6109 uint16_t class;
6110 uint8_t name[ kDomainNameLengthMax ];
6111
6112 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
6113 &ptr );
6114 require_noerr( err, exit );
6115
6116 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
6117 {
6118 break;
6119 }
6120 }
6121
6122 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
6123 {
6124 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
6125 err = kSizeErr;
6126 goto exit;
6127 }
6128 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
6129 {
6130 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
6131 err = kSizeErr;
6132 goto exit;
6133 }
6134
6135 context->certLen = txtPtr[ 0 ];
6136 context->certPtr = &txtPtr[ 1 ];
6137
6138 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
6139
6140 exit:
6141 if( err ) Exit( NULL );
6142 }
6143
6144 //===========================================================================================================================
6145 // DNSCryptReceiveResponseHandler
6146 //===========================================================================================================================
6147
6148 static void DNSCryptReceiveResponseHandler( void *inContext )
6149 {
6150 OSStatus err;
6151 struct timeval now;
6152 const uint64_t nowTicks = UpTicks();
6153 SocketContext * const sockCtx = (SocketContext *) inContext;
6154 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6155 sockaddr_ip fromAddr;
6156 DNSCryptResponseHeader * hdr;
6157 const uint8_t * end;
6158 uint8_t * ciphertext;
6159 uint8_t * plaintext;
6160 const uint8_t * response;
6161 uint8_t nonce[ crypto_box_NONCEBYTES ];
6162
6163 gettimeofday( &now, NULL );
6164
6165 dispatch_source_forget( &context->readSource );
6166
6167 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6168 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6169 require_noerr( err, exit );
6170 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6171
6172 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6173 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6174 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6175 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6176
6177 if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
6178 {
6179 FPrintF( stderr, "DNSCrypt response is too short.\n" );
6180 err = kSizeErr;
6181 goto exit;
6182 }
6183
6184 hdr = (DNSCryptResponseHeader *) context->msgBuf;
6185
6186 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
6187 {
6188 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
6189 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX,
6190 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
6191 err = kValueErr;
6192 goto exit;
6193 }
6194
6195 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
6196 {
6197 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
6198 err = kValueErr;
6199 goto exit;
6200 }
6201
6202 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
6203
6204 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
6205 memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
6206
6207 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
6208 check( plaintext == ciphertext );
6209
6210 end = context->msgBuf + context->msgLen;
6211
6212 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
6213 require_noerr( err, exit );
6214
6215 response = plaintext + crypto_box_ZEROBYTES;
6216 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, response, (size_t)( end - response ) );
6217 Exit( kExitReason_ReceivedResponse );
6218
6219 exit:
6220 if( err ) Exit( NULL );
6221 }
6222
6223 //===========================================================================================================================
6224 // DNSCryptProceed
6225 //===========================================================================================================================
6226
6227 static void DNSCryptProceed( void *inContext )
6228 {
6229 OSStatus err;
6230 DNSCryptContext * const context = (DNSCryptContext *) inContext;
6231
6232 err = DNSCryptProcessCert( context );
6233 require_noerr_quiet( err, exit );
6234
6235 err = DNSCryptBuildQuery( context );
6236 require_noerr_quiet( err, exit );
6237
6238 err = DNSCryptSendQuery( context );
6239 require_noerr_quiet( err, exit );
6240
6241 exit:
6242 if( err ) Exit( NULL );
6243 }
6244
6245 //===========================================================================================================================
6246 // DNSCryptProcessCert
6247 //===========================================================================================================================
6248
6249 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext )
6250 {
6251 OSStatus err;
6252 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr;
6253 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen;
6254 struct timeval now;
6255 time_t startTimeSecs, endTimeSecs;
6256 size_t signedLen;
6257 uint8_t * tempBuf;
6258 unsigned long long tempLen;
6259
6260 DNSCryptPrintCertificate( cert, inContext->certLen );
6261
6262 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
6263 {
6264 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
6265 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX,
6266 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
6267 err = kValueErr;
6268 goto exit;
6269 }
6270
6271 startTimeSecs = (time_t) ReadBig32( cert->startTime );
6272 endTimeSecs = (time_t) ReadBig32( cert->endTime );
6273
6274 gettimeofday( &now, NULL );
6275 if( now.tv_sec < startTimeSecs )
6276 {
6277 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
6278 err = kDateErr;
6279 goto exit;
6280 }
6281 if( now.tv_sec >= endTimeSecs )
6282 {
6283 FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
6284 err = kDateErr;
6285 goto exit;
6286 }
6287
6288 signedLen = (size_t)( certEnd - cert->signature );
6289 tempBuf = (uint8_t *) malloc( signedLen );
6290 require_action( tempBuf, exit, err = kNoMemoryErr );
6291 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
6292 free( tempBuf );
6293 if( err )
6294 {
6295 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
6296 err = kAuthenticationErr;
6297 goto exit;
6298 }
6299
6300 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES );
6301 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength );
6302
6303 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
6304 require_noerr( err, exit );
6305
6306 inContext->certPtr = NULL;
6307 inContext->certLen = 0;
6308 inContext->msgLen = 0;
6309
6310 exit:
6311 return( err );
6312 }
6313
6314 //===========================================================================================================================
6315 // DNSCryptBuildQuery
6316 //===========================================================================================================================
6317
6318 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
6319
6320 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext )
6321 {
6322 OSStatus err;
6323 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf;
6324 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 );
6325 size_t queryLen;
6326 size_t paddedQueryLen;
6327 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf );
6328 const uint8_t * padLimit;
6329 uint8_t nonce[ crypto_box_NONCEBYTES ];
6330
6331 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
6332
6333 inContext->queryID = (uint16_t) Random32();
6334 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
6335 inContext->qtype, kDNSServiceClass_IN, &queryLen );
6336 require_noerr( err, exit );
6337
6338 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
6339 if( padLimit > msgLimit ) padLimit = msgLimit;
6340
6341 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
6342 require_noerr( err, exit );
6343
6344 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
6345 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
6346 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
6347 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
6348
6349 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
6350 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
6351 require_noerr( err, exit );
6352
6353 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength );
6354 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES );
6355 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength );
6356
6357 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
6358
6359 exit:
6360 return( err );
6361 }
6362
6363 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
6364 {
6365 OSStatus err;
6366 size_t paddedLen;
6367
6368 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
6369
6370 paddedLen = inMsgLen + kDNSCryptMinPadLength +
6371 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
6372 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
6373 if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
6374
6375 inMsgPtr[ inMsgLen ] = 0x80;
6376 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
6377
6378 if( outPaddedLen ) *outPaddedLen = paddedLen;
6379 err = kNoErr;
6380
6381 exit:
6382 return( err );
6383 }
6384
6385 //===========================================================================================================================
6386 // DNSCryptSendQuery
6387 //===========================================================================================================================
6388
6389 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
6390 {
6391 OSStatus err;
6392 SocketContext * sockCtx;
6393 SocketRef sock = kInvalidSocketRef;
6394
6395 check( inContext->msgLen > 0 );
6396 check( !inContext->readSource );
6397
6398 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
6399 require_noerr( err, exit );
6400
6401 inContext->sendTicks = UpTicks();
6402 err = SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
6403 require_noerr( err, exit );
6404
6405 err = SocketContextCreate( sock, inContext, &sockCtx );
6406 require_noerr( err, exit );
6407 sock = kInvalidSocketRef;
6408
6409 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx,
6410 &inContext->readSource );
6411 if( err ) ForgetSocketContext( &sockCtx );
6412 require_noerr( err, exit );
6413
6414 dispatch_resume( inContext->readSource );
6415
6416 exit:
6417 ForgetSocket( &sock );
6418 return( err );
6419 }
6420
6421 //===========================================================================================================================
6422 // DNSCryptPrintCertificate
6423 //===========================================================================================================================
6424
6425 #define kCertTimeStrBufLen 32
6426
6427 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
6428
6429 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
6430 {
6431 time_t startTime, endTime;
6432 int extLen;
6433 char timeBuf[ kCertTimeStrBufLen ];
6434
6435 check( inLen >= kDNSCryptCertMinimumLength );
6436
6437 startTime = (time_t) ReadBig32( inCert->startTime );
6438 endTime = (time_t) ReadBig32( inCert->endTime );
6439
6440 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
6441 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
6442 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) );
6443 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) );
6444 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
6445 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
6446 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
6447 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
6448 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) );
6449 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
6450 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
6451
6452 if( inLen > kDNSCryptCertMinimumLength )
6453 {
6454 extLen = (int)( inLen - kDNSCryptCertMinimumLength );
6455 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen );
6456 }
6457 FPrintF( stdout, "\n" );
6458 }
6459
6460 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
6461 {
6462 struct tm * tm;
6463
6464 tm = localtime( &inTime );
6465 if( !tm )
6466 {
6467 dlogassert( "localtime() returned a NULL pointer.\n" );
6468 *inBuffer = '\0';
6469 }
6470 else
6471 {
6472 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
6473 }
6474
6475 return( inBuffer );
6476 }
6477
6478 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6479
6480 //===========================================================================================================================
6481 // MDNSQueryCmd
6482 //===========================================================================================================================
6483
6484 typedef struct
6485 {
6486 const char * qnameStr; // Name (QNAME) of the record being queried as a C string.
6487 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
6488 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
6489 int localPort; // The port number to which the sockets are bound.
6490 int receiveSecs; // After send, the amount of time to spend receiving.
6491 uint32_t ifIndex; // Index of the interface over which to send the query.
6492 uint16_t qtype; // The type (QTYPE) of the record being queried.
6493 Boolean isQU; // True if the query is QU, i.e., requests unicast responses.
6494 Boolean allResponses; // True if all mDNS messages received should be printed.
6495 Boolean printRawRData; // True if RDATA should be printed as hexdumps.
6496 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
6497 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
6498 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query.
6499 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in DNS label format.
6500 uint8_t msgBuf[ 8940 ]; // Message buffer. 8940 is max size used by mDNSResponder.
6501
6502 } MDNSQueryContext;
6503
6504 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
6505 static void MDNSQueryReadHandler( void *inContext );
6506
6507 static void MDNSQueryCmd( void )
6508 {
6509 OSStatus err;
6510 MDNSQueryContext * context;
6511 struct sockaddr_in mcastAddr4;
6512 struct sockaddr_in6 mcastAddr6;
6513 SocketRef sockV4 = kInvalidSocketRef;
6514 SocketRef sockV6 = kInvalidSocketRef;
6515 ssize_t n;
6516 const char * ifNamePtr;
6517 size_t msgLen;
6518 unsigned int sendCount;
6519
6520 // Check command parameters.
6521
6522 if( gMDNSQuery_ReceiveSecs < -1 )
6523 {
6524 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
6525 err = kParamErr;
6526 goto exit;
6527 }
6528
6529 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
6530 require_action( context, exit, err = kNoMemoryErr );
6531
6532 context->qnameStr = gMDNSQuery_Name;
6533 context->receiveSecs = gMDNSQuery_ReceiveSecs;
6534 context->isQU = gMDNSQuery_IsQU ? true : false;
6535 context->allResponses = gMDNSQuery_AllResponses ? true : false;
6536 context->printRawRData = gMDNSQuery_RawRData ? true : false;
6537 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
6538 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
6539
6540 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
6541 require_noerr_quiet( err, exit );
6542
6543 ifNamePtr = if_indextoname( context->ifIndex, context->ifName );
6544 require_action( ifNamePtr, exit, err = kNameErr );
6545
6546 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
6547 require_noerr( err, exit );
6548
6549 // Set up IPv4 socket.
6550
6551 if( context->useIPv4 )
6552 {
6553 err = ServerSocketOpen( AF_INET, SOCK_DGRAM, IPPROTO_UDP,
6554 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6555 &context->localPort, kSocketBufferSize_DontSet, &sockV4 );
6556 require_noerr( err, exit );
6557
6558 err = SocketSetMulticastInterface( sockV4, ifNamePtr, context->ifIndex );
6559 require_noerr( err, exit );
6560
6561 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
6562 err = map_socket_noerr_errno( sockV4, err );
6563 require_noerr( err, exit );
6564
6565 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
6566 SIN_LEN_SET( &mcastAddr4 );
6567 mcastAddr4.sin_family = AF_INET;
6568 mcastAddr4.sin_port = htons( kMDNSPort );
6569 mcastAddr4.sin_addr.s_addr = htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
6570
6571 if( !context->isQU && ( context->localPort == kMDNSPort ) )
6572 {
6573 err = SocketJoinMulticast( sockV4, &mcastAddr4, ifNamePtr, context->ifIndex );
6574 require_noerr( err, exit );
6575 }
6576 }
6577
6578 // Set up IPv6 socket.
6579
6580 if( context->useIPv6 )
6581 {
6582 err = ServerSocketOpen( AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
6583 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6584 &context->localPort, kSocketBufferSize_DontSet, &sockV6 );
6585 require_noerr( err, exit );
6586
6587 err = SocketSetMulticastInterface( sockV6, ifNamePtr, context->ifIndex );
6588 require_noerr( err, exit );
6589
6590 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
6591 err = map_socket_noerr_errno( sockV6, err );
6592 require_noerr( err, exit );
6593
6594 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
6595 SIN6_LEN_SET( &mcastAddr6 );
6596 mcastAddr6.sin6_family = AF_INET6;
6597 mcastAddr6.sin6_port = htons( kMDNSPort );
6598 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // mDNS IPv6 multicast address FF02::FB
6599 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
6600 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0xFB;
6601
6602 if( !context->isQU && ( context->localPort == kMDNSPort ) )
6603 {
6604 err = SocketJoinMulticast( sockV6, &mcastAddr6, ifNamePtr, context->ifIndex );
6605 require_noerr( err, exit );
6606 }
6607 }
6608
6609 // Craft mDNS query message.
6610
6611 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
6612 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
6613 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kQClassUnicastResponseBit ) : kDNSServiceClass_IN, &msgLen );
6614 require_noerr( err, exit );
6615
6616 // Print prologue.
6617
6618 MDNSQueryPrintPrologue( context );
6619
6620 // Send mDNS query message.
6621
6622 sendCount = 0;
6623 if( IsValidSocket( sockV4 ) )
6624 {
6625 n = sendto( sockV4, context->msgBuf, msgLen, 0, (struct sockaddr *) &mcastAddr4, (socklen_t) sizeof( mcastAddr4 ) );
6626 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
6627 if( err )
6628 {
6629 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
6630 ForgetSocket( &sockV4 );
6631 }
6632 else
6633 {
6634 ++sendCount;
6635 }
6636 }
6637 if( IsValidSocket( sockV6 ) )
6638 {
6639 n = sendto( sockV6, context->msgBuf, msgLen, 0, (struct sockaddr *) &mcastAddr6, (socklen_t) sizeof( mcastAddr6 ) );
6640 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
6641 if( err )
6642 {
6643 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
6644 ForgetSocket( &sockV6 );
6645 }
6646 else
6647 {
6648 ++sendCount;
6649 }
6650 }
6651 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
6652
6653 // If there's no wait period after the send, then exit.
6654
6655 if( context->receiveSecs == 0 ) goto exit;
6656
6657 // Create dispatch read sources for socket(s).
6658
6659 if( IsValidSocket( sockV4 ) )
6660 {
6661 SocketContext * sockCtx;
6662
6663 err = SocketContextCreate( sockV4, context, &sockCtx );
6664 require_noerr( err, exit );
6665 sockV4 = kInvalidSocketRef;
6666
6667 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6668 &context->readSourceV4 );
6669 if( err ) ForgetSocketContext( &sockCtx );
6670 require_noerr( err, exit );
6671
6672 dispatch_resume( context->readSourceV4 );
6673 }
6674
6675 if( IsValidSocket( sockV6 ) )
6676 {
6677 SocketContext * sockCtx;
6678
6679 err = SocketContextCreate( sockV6, context, &sockCtx );
6680 require_noerr( err, exit );
6681 sockV6 = kInvalidSocketRef;
6682
6683 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6684 &context->readSourceV6 );
6685 if( err ) ForgetSocketContext( &sockCtx );
6686 require_noerr( err, exit );
6687
6688 dispatch_resume( context->readSourceV6 );
6689 }
6690
6691 if( context->receiveSecs > 0 )
6692 {
6693 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6694 Exit );
6695 }
6696 dispatch_main();
6697
6698 exit:
6699 ForgetSocket( &sockV4 );
6700 ForgetSocket( &sockV6 );
6701 if( err ) exit( 1 );
6702 }
6703
6704 //===========================================================================================================================
6705 // MDNSQueryPrintPrologue
6706 //===========================================================================================================================
6707
6708 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
6709 {
6710 const int receiveSecs = inContext->receiveSecs;
6711
6712 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName );
6713 FPrintF( stdout, "Name: %s\n", inContext->qnameStr );
6714 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype );
6715 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" );
6716 FPrintF( stdout, "Local port: %d\n", inContext->localPort );
6717 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
6718 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
6719 FPrintF( stdout, "Receive duration: " );
6720 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
6721 else FPrintF( stdout, "∞\n" );
6722 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
6723 }
6724
6725 //===========================================================================================================================
6726 // MDNSQueryReadHandler
6727 //===========================================================================================================================
6728
6729 static void MDNSQueryReadHandler( void *inContext )
6730 {
6731 OSStatus err;
6732 struct timeval now;
6733 SocketContext * const sockCtx = (SocketContext *) inContext;
6734 MDNSQueryContext * const context = (MDNSQueryContext *) sockCtx->userContext;
6735 size_t msgLen;
6736 sockaddr_ip fromAddr;
6737 Boolean foundAnswer = false;
6738
6739 gettimeofday( &now, NULL );
6740
6741 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
6742 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6743 require_noerr( err, exit );
6744
6745 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
6746 {
6747 const uint8_t * ptr;
6748 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf;
6749 unsigned int rrCount, i;
6750 uint16_t type, class;
6751 uint8_t name[ kDomainNameLengthMax ];
6752
6753 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
6754 require_noerr( err, exit );
6755
6756 if( context->qname[ 0 ] == 0 )
6757 {
6758 err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
6759 require_noerr( err, exit );
6760 }
6761
6762 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
6763 for( i = 0; i < rrCount; ++i )
6764 {
6765 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
6766 require_noerr( err, exit );
6767
6768 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
6769 DomainNameEqual( name, context->qname ) )
6770 {
6771 foundAnswer = true;
6772 break;
6773 }
6774 }
6775 }
6776 if( context->allResponses || foundAnswer )
6777 {
6778 FPrintF( stdout, "---\n" );
6779 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6780 FPrintF( stdout, "Source: %##a\n", &fromAddr );
6781 FPrintF( stdout, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6782 msgLen, context->printRawRData ? 1 : 0, context->msgBuf, msgLen );
6783 }
6784
6785 exit:
6786 if( err ) exit( 1 );
6787 }
6788
6789 //===========================================================================================================================
6790 // PIDToUUIDCmd
6791 //===========================================================================================================================
6792
6793 static void PIDToUUIDCmd( void )
6794 {
6795 OSStatus err;
6796 int n;
6797 struct proc_uniqidentifierinfo info;
6798
6799 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
6800 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
6801
6802 FPrintF( stdout, "%#U\n", info.p_uuid );
6803 err = kNoErr;
6804
6805 exit:
6806 if( err ) exit( 1 );
6807 }
6808
6809 //===========================================================================================================================
6810 // DNSServerCmd
6811 //===========================================================================================================================
6812
6813 typedef uint32_t DNSServerEventType;
6814 #define kDNSServerEvent_Started 1
6815 #define kDNSServerEvent_Stopped 2
6816
6817 typedef struct DNSServerPrivate * DNSServerRef;
6818
6819 typedef struct
6820 {
6821 DNSServerRef server; // Reference to the DNS server.
6822 dispatch_source_t sigIntSource; // Dispatch SIGINT source.
6823 dispatch_source_t sigTermSource; // Dispatch SIGTERM source.
6824 #if( TARGET_OS_DARWIN )
6825 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
6826 pid_t followPID; // PID of process being followed (we exit when they exit), if any.
6827 Boolean resolverRegistered; // True if system DNS settings contains a resolver entry for server.
6828 #endif
6829 Boolean loopbackOnly; // True if the server should be bound to the loopback interface.
6830 Boolean serverStarted; // True if the server was successfully started.
6831 Boolean calledStop; // True if the server was explicitly stopped.
6832
6833 } DNSServerCmdContext;
6834
6835 typedef void ( *DNSServerEventHandler_f )( DNSServerEventType inType, void *inContext );
6836
6837 CFTypeID DNSServerGetTypeID( void );
6838 static OSStatus
6839 DNSServerCreate(
6840 dispatch_queue_t inQueue,
6841 DNSServerEventHandler_f inEventHandler,
6842 void * inEventContext,
6843 int inResponseDelayMs,
6844 Boolean inLoopbackOnly,
6845 DNSServerRef * outServer );
6846 static void DNSServerStart( DNSServerRef inServer );
6847 static void DNSServerStop( DNSServerRef inServer );
6848
6849 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext );
6850 static void DNSServerCmdEventHandler( DNSServerEventType inType, void *inContext );
6851 static void DNSServerCmdSigIntHandler( void *inContext );
6852 static void DNSServerCmdSigTermHandler( void *inContext );
6853 #if( TARGET_OS_DARWIN )
6854 static void DNSServerCmdFollowedProcessHandler( void *inContext );
6855 #endif
6856
6857 ulog_define_ex( "com.apple.dnssdutil", DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL );
6858 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6859
6860 static void DNSServerCmd( void )
6861 {
6862 OSStatus err;
6863 DNSServerCmdContext * context;
6864
6865 context = (DNSServerCmdContext *) calloc( 1, sizeof( *context ) );
6866 require_action( context, exit, err = kNoMemoryErr );
6867
6868 context->loopbackOnly = gDNSServer_LoopbackOnly ? true : false;
6869
6870 #if( TARGET_OS_DARWIN )
6871 if( gDNSServer_FollowPID )
6872 {
6873 long long value;
6874
6875 err = StringToLongLong( gDNSServer_FollowPID, &value );
6876 if( !err && ( value < 0 ) ) err = kValueErr;
6877 if( err )
6878 {
6879 FPrintF( stderr, "Invalid followPID argument \"%s\".\n", gDNSServer_FollowPID );
6880 goto exit;
6881 }
6882 context->followPID = (pid_t) value;
6883
6884 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
6885 DNSServerCmdFollowedProcessHandler, NULL, context, &context->processMonitor );
6886 require_noerr( err, exit );
6887 dispatch_resume( context->processMonitor );
6888 }
6889 else
6890 {
6891 context->followPID = -1;
6892 }
6893 #endif
6894
6895 signal( SIGINT, SIG_IGN );
6896 err = DispatchSignalSourceCreate( SIGINT, DNSServerCmdSigIntHandler, context, &context->sigIntSource );
6897 require_noerr( err, exit );
6898 dispatch_resume( context->sigIntSource );
6899
6900 signal( SIGTERM, SIG_IGN );
6901 err = DispatchSignalSourceCreate( SIGTERM, DNSServerCmdSigTermHandler, context, &context->sigTermSource );
6902 require_noerr( err, exit );
6903 dispatch_resume( context->sigTermSource );
6904
6905 if( gDNSServer_Foreground )
6906 {
6907 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6908 }
6909
6910 if( ( gDNSServer_DefaultTTL < 0 ) || ( gDNSServer_DefaultTTL > INT32_MAX ) )
6911 {
6912 ds_ulog( kLogLevelError, "The default TTL %d provided by user is out-of-range. Will use %d instead.\n",
6913 gDNSServer_DefaultTTL, kDNSServerDefaultTTL );
6914 gDNSServer_DefaultTTL = kDNSServerDefaultTTL;
6915 }
6916
6917 err = DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler, context, gDNSServer_ResponseDelayMs,
6918 context->loopbackOnly, &context->server );
6919 require_noerr( err, exit );
6920
6921 DNSServerStart( context->server );
6922 dispatch_main();
6923
6924 exit:
6925 ds_ulog( kLogLevelError, "Failed to start DNS server: %#m\n", err );
6926 if( context ) DNSServerCmdContextFree( context );
6927 if( err ) exit( 1 );
6928 }
6929
6930 //===========================================================================================================================
6931 // DNSServerCmdContextFree
6932 //===========================================================================================================================
6933
6934 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext )
6935 {
6936 ForgetCF( &inContext->server );
6937 dispatch_source_forget( &inContext->sigIntSource );
6938 dispatch_source_forget( &inContext->sigTermSource );
6939 dispatch_source_forget( &inContext->processMonitor );
6940 free( inContext );
6941 }
6942
6943 //===========================================================================================================================
6944 // DNSServerCmdEventHandler
6945 //===========================================================================================================================
6946
6947 #if( TARGET_OS_DARWIN )
6948 static OSStatus _DNSServerCmdRegisterResolver( void );
6949 static OSStatus _DNSServerCmdUnregisterResolver( void );
6950 #endif
6951
6952 static void DNSServerCmdEventHandler( DNSServerEventType inType, void *inContext )
6953 {
6954 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
6955 #if( TARGET_OS_DARWIN )
6956 OSStatus err;
6957 #endif
6958
6959 if( inType == kDNSServerEvent_Started )
6960 {
6961 context->serverStarted = true;
6962 #if( TARGET_OS_DARWIN )
6963 err = _DNSServerCmdRegisterResolver();
6964 if( err )
6965 {
6966 ds_ulog( kLogLevelError, "Failed to add resolver to DNS configuration for \"d.test.\" domain: %#m\n", err );
6967 if( context->loopbackOnly ) exit( 1 );
6968 }
6969 else
6970 {
6971 context->resolverRegistered = true;
6972 }
6973 #endif
6974 }
6975 else if( inType == kDNSServerEvent_Stopped )
6976 {
6977 #if( TARGET_OS_DARWIN )
6978 if( context->resolverRegistered )
6979 {
6980 err = _DNSServerCmdUnregisterResolver();
6981 if( err )
6982 {
6983 ds_ulog( kLogLevelError, "Failed to remove resolver from DNS configuration: %#m\n", err );
6984 }
6985 else
6986 {
6987 context->resolverRegistered = false;
6988 }
6989 }
6990
6991 if( !context->calledStop )
6992 {
6993 ds_ulog( kLogLevelError, "The server stopped unexpectedly.\n" );
6994 exit( 1 );
6995 }
6996 #endif
6997 DNSServerCmdContextFree( context );
6998 }
6999 }
7000
7001 #if( TARGET_OS_DARWIN )
7002 //===========================================================================================================================
7003 // _DNSServerCmdRegisterResolver
7004 //===========================================================================================================================
7005
7006 static OSStatus _DNSServerCmdRegisterResolver( void )
7007 {
7008 OSStatus err;
7009 SCDynamicStoreRef store;
7010 CFPropertyListRef plist = NULL;
7011 CFStringRef key = NULL;
7012 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7013 Boolean success;
7014
7015 store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
7016 err = map_scerror( store );
7017 require_noerr( err, exit );
7018
7019 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
7020 "{"
7021 "%kO="
7022 "["
7023 "%s"
7024 "]"
7025 "%kO="
7026 "["
7027 "%.4a"
7028 "%.16a"
7029 "]"
7030 "}",
7031 kSCPropNetDNSSupplementalMatchDomains, "d.test.",
7032 kSCPropNetDNSServerAddresses, &loopbackV4, in6addr_loopback.s6_addr );
7033 require_noerr( err, exit );
7034
7035 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7036 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7037 require_action( key, exit, err = kUnknownErr );
7038
7039 success = SCDynamicStoreSetValue( store, key, plist );
7040 require_action( success, exit, err = kUnknownErr );
7041
7042 exit:
7043 CFReleaseNullSafe( store );
7044 CFReleaseNullSafe( plist );
7045 CFReleaseNullSafe( key );
7046 return( err );
7047 }
7048
7049 //===========================================================================================================================
7050 // _DNSServerCmdUnregisterResolver
7051 //===========================================================================================================================
7052
7053 static OSStatus _DNSServerCmdUnregisterResolver( void )
7054 {
7055 OSStatus err;
7056 SCDynamicStoreRef store;
7057 CFStringRef key = NULL;
7058 Boolean success;
7059
7060 store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
7061 err = map_scerror( store );
7062 require_noerr( err, exit );
7063
7064 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7065 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7066 require_action( key, exit, err = kUnknownErr );
7067
7068 success = SCDynamicStoreRemoveValue( store, key );
7069 require_action( success, exit, err = kUnknownErr );
7070
7071 exit:
7072 CFReleaseNullSafe( store );
7073 CFReleaseNullSafe( key );
7074 return( err );
7075 }
7076 #endif
7077
7078 //===========================================================================================================================
7079 // DNSServerCmdSigIntHandler
7080 //===========================================================================================================================
7081
7082 static void _DNSServerCmdExternalExit( DNSServerCmdContext *inContext, int inSignal );
7083
7084 static void DNSServerCmdSigIntHandler( void *inContext )
7085 {
7086 _DNSServerCmdExternalExit( (DNSServerCmdContext *) inContext, SIGINT );
7087 }
7088
7089 //===========================================================================================================================
7090 // DNSServerCmdSigTermHandler
7091 //===========================================================================================================================
7092
7093 static void DNSServerCmdSigTermHandler( void *inContext )
7094 {
7095 _DNSServerCmdExternalExit( (DNSServerCmdContext *) inContext, SIGTERM );
7096 }
7097
7098 #if( TARGET_OS_DARWIN )
7099 //===========================================================================================================================
7100 // DNSServerCmdFollowedProcessHandler
7101 //===========================================================================================================================
7102
7103 static void DNSServerCmdFollowedProcessHandler( void *inContext )
7104 {
7105 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
7106
7107 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT )
7108 {
7109 _DNSServerCmdExternalExit( context, 0 );
7110 }
7111 }
7112 #endif
7113
7114 //===========================================================================================================================
7115 // _DNSServerCmdExternalExit
7116 //===========================================================================================================================
7117
7118 #define SignalNumberToString( X ) ( \
7119 ( (X) == SIGINT ) ? "SIGINT" : \
7120 ( (X) == SIGTERM ) ? "SIGTERM" : \
7121 "???" )
7122
7123 static void _DNSServerCmdExternalExit( DNSServerCmdContext *inContext, int inSignal )
7124 {
7125 OSStatus err;
7126
7127 #if( TARGET_OS_DARWIN )
7128 if( inSignal == 0 )
7129 {
7130 ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inContext->followPID );
7131 }
7132 else
7133 #endif
7134 {
7135 ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, SignalNumberToString( inSignal ) );
7136 }
7137
7138 #if( TARGET_OS_DARWIN )
7139 if( inContext->resolverRegistered )
7140 {
7141 err = _DNSServerCmdUnregisterResolver();
7142 if( err )
7143 {
7144 ds_ulog( kLogLevelError, "Failed to remove resolver from DNS configuration: %#m\n", err );
7145 goto exit;
7146 }
7147 inContext->resolverRegistered = false;
7148 }
7149 #endif
7150 if( inContext->serverStarted )
7151 {
7152 DNSServerStop( inContext->server );
7153 inContext->calledStop = true;
7154 }
7155 err = kNoErr;
7156
7157 exit:
7158 exit( err ? 1 : 0 );
7159 }
7160
7161 //===========================================================================================================================
7162 // DNSServerCreate
7163 //===========================================================================================================================
7164
7165 typedef struct DNSDelayedResponse DNSDelayedResponse;
7166 struct DNSDelayedResponse
7167 {
7168 DNSDelayedResponse * next;
7169 sockaddr_ip clientAddr;
7170 uint64_t targetTicks;
7171 uint8_t * msgPtr;
7172 size_t msgLen;
7173 };
7174
7175 #define DNSScheduledResponseFree( X ) do { ForgetMem( &(X)->msgPtr ) ; free( X ); } while( 0 )
7176
7177 struct DNSServerPrivate
7178 {
7179 CFRuntimeBase base; // CF object base.
7180 dispatch_queue_t queue; // Queue for DNS server's events.
7181 dispatch_source_t readSourceUDPv4; // Read source for IPv4 UDP socket.
7182 dispatch_source_t readSourceUDPv6; // Read source for IPv6 UDP socket.
7183 dispatch_source_t readSourceTCPv4; // Read source for IPv4 TCP socket.
7184 dispatch_source_t readSourceTCPv6; // Read source for IPv6 TCP socket.
7185 DNSServerEventHandler_f eventHandler;
7186 void * eventContext;
7187 DNSDelayedResponse * responseList;
7188 int responseDelayMs;
7189 dispatch_source_t responseTimer;
7190 Boolean loopbackOnly;
7191 Boolean stopped;
7192 };
7193
7194 CF_CLASS_DEFINE( DNSServer );
7195
7196 static OSStatus
7197 DNSServerCreate(
7198 dispatch_queue_t inQueue,
7199 DNSServerEventHandler_f inEventHandler,
7200 void * inEventContext,
7201 int inResponseDelayMs,
7202 Boolean inLoopbackOnly,
7203 DNSServerRef * outServer )
7204 {
7205 OSStatus err;
7206 DNSServerRef obj = NULL;
7207
7208 CF_OBJECT_CREATE( DNSServer, obj, err, exit );
7209
7210 ReplaceDispatchQueue( &obj->queue, inQueue );
7211 obj->eventHandler = inEventHandler;
7212 obj->eventContext = inEventContext;
7213 obj->responseDelayMs = inResponseDelayMs;
7214 if( inLoopbackOnly ) obj->loopbackOnly = true;
7215
7216 *outServer = obj;
7217 obj = NULL;
7218 err = kNoErr;
7219
7220 exit:
7221 CFReleaseNullSafe( obj );
7222 return( err );
7223 }
7224
7225 //===========================================================================================================================
7226 // _DNSServerFinalize
7227 //===========================================================================================================================
7228
7229 static void _DNSServerFinalize( CFTypeRef inObj )
7230 {
7231 DNSServerRef const me = (DNSServerRef) inObj;
7232
7233 check( !me->readSourceUDPv4 );
7234 check( !me->readSourceUDPv6 );
7235 check( !me->readSourceTCPv4 );
7236 check( !me->readSourceTCPv6 );
7237 check( !me->responseTimer );
7238 dispatch_forget( &me->queue );
7239 }
7240
7241 //===========================================================================================================================
7242 // DNSServerStart
7243 //===========================================================================================================================
7244
7245 static void _DNSServerStart( void *inContext );
7246 static void _DNSServerUDPReadHandler( void *inContext );
7247 static void _DNSServerTCPReadHandler( void *inContext );
7248
7249 static void DNSServerStart( DNSServerRef me )
7250 {
7251 CFRetain( me );
7252 dispatch_async_f( me->queue, me, _DNSServerStart );
7253 }
7254
7255 static void _DNSServerStart( void *inContext )
7256 {
7257 OSStatus err;
7258 DNSServerRef const me = (DNSServerRef) inContext;
7259 SocketRef sock = kInvalidSocketRef;
7260 SocketContext * sockCtx = NULL;
7261 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7262
7263 // Create IPv4 UDP socket.
7264
7265 err = _ServerSocketOpenEx2( AF_INET, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &loopbackV4 : NULL,
7266 kDNSPort, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7267 require_noerr( err, exit );
7268
7269 // Create read source for IPv4 UDP socket.
7270
7271 err = SocketContextCreate( sock, me, &sockCtx );
7272 require_noerr( err, exit );
7273 sock = kInvalidSocketRef;
7274
7275 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7276 &me->readSourceUDPv4 );
7277 require_noerr( err, exit );
7278 dispatch_resume( me->readSourceUDPv4 );
7279 sockCtx = NULL;
7280
7281 // Create IPv6 UDP socket.
7282
7283 err = _ServerSocketOpenEx2( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &in6addr_loopback : NULL,
7284 kDNSPort, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7285 require_noerr( err, exit );
7286
7287 // Create read source for IPv6 UDP socket.
7288
7289 err = SocketContextCreate( sock, me, &sockCtx );
7290 require_noerr( err, exit );
7291 sock = kInvalidSocketRef;
7292
7293 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7294 &me->readSourceUDPv6 );
7295 require_noerr( err, exit );
7296 dispatch_resume( me->readSourceUDPv6 );
7297 sockCtx = NULL;
7298
7299 // Create IPv4 TCP socket.
7300
7301 err = _ServerSocketOpenEx2( AF_INET, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &loopbackV4 : NULL,
7302 kDNSPort, NULL, kSocketBufferSize_DontSet, false, &sock );
7303 require_noerr( err, exit );
7304
7305 // Create read source for IPv4 TCP socket.
7306
7307 err = SocketContextCreate( sock, me, &sockCtx );
7308 require_noerr( err, exit );
7309 sock = kInvalidSocketRef;
7310
7311 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7312 &me->readSourceTCPv4 );
7313 require_noerr( err, exit );
7314 dispatch_resume( me->readSourceTCPv4 );
7315 sockCtx = NULL;
7316
7317 // Create IPv6 TCP socket.
7318
7319 err = _ServerSocketOpenEx2( AF_INET6, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &in6addr_loopback : NULL,
7320 kDNSPort, NULL, kSocketBufferSize_DontSet, false, &sock );
7321 require_noerr( err, exit );
7322
7323 // Create read source for IPv6 TCP socket.
7324
7325 err = SocketContextCreate( sock, me, &sockCtx );
7326 require_noerr( err, exit );
7327 sock = kInvalidSocketRef;
7328
7329 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7330 &me->readSourceTCPv6 );
7331 require_noerr( err, exit );
7332 dispatch_resume( me->readSourceTCPv6 );
7333 sockCtx = NULL;
7334
7335 CFRetain( me );
7336 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Started, me->eventContext );
7337
7338 exit:
7339 ForgetSocket( &sock );
7340 if( sockCtx ) SocketContextRelease( sockCtx );
7341 if( err ) DNSServerStop( me );
7342 CFRelease( me );
7343 }
7344
7345 //===========================================================================================================================
7346 // DNSServerStop
7347 //===========================================================================================================================
7348
7349 static void _DNSServerStop( void *inContext );
7350 static void _DNSServerStop2( void *inContext );
7351
7352 static void DNSServerStop( DNSServerRef me )
7353 {
7354 CFRetain( me );
7355 dispatch_async_f( me->queue, me, _DNSServerStop );
7356 }
7357
7358 static void _DNSServerStop( void *inContext )
7359 {
7360 DNSServerRef const me = (DNSServerRef) inContext;
7361 DNSDelayedResponse * resp;
7362
7363 dispatch_source_forget( &me->readSourceUDPv4 );
7364 dispatch_source_forget( &me->readSourceUDPv6 );
7365 dispatch_source_forget( &me->readSourceTCPv4 );
7366 dispatch_source_forget( &me->readSourceTCPv6 );
7367 dispatch_source_forget( &me->responseTimer );
7368
7369 while( ( resp = me->responseList ) != NULL )
7370 {
7371 me->responseList = resp->next;
7372 DNSScheduledResponseFree( resp );
7373 }
7374
7375 dispatch_async_f( me->queue, me, _DNSServerStop2 );
7376 }
7377
7378 static void _DNSServerStop2( void *inContext )
7379 {
7380 DNSServerRef const me = (DNSServerRef) inContext;
7381
7382 if( !me->stopped )
7383 {
7384 me->stopped = true;
7385 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Stopped, me->eventContext );
7386 CFRelease( me );
7387 }
7388 CFRelease( me );
7389 }
7390
7391 //===========================================================================================================================
7392 // _DNSServerUDPReadHandler
7393 //===========================================================================================================================
7394
7395 static OSStatus
7396 _DNSServerAnswerQuery(
7397 const uint8_t * inQueryPtr,
7398 size_t inQueryLen,
7399 Boolean inForTCP,
7400 uint8_t ** outResponsePtr,
7401 size_t * outResponseLen );
7402
7403 #define _DNSServerAnswerQueryForUDP( IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7404 _DNSServerAnswerQuery( IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7405
7406 #define _DNSServerAnswerQueryForTCP( IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7407 _DNSServerAnswerQuery( IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7408
7409 static void _DNSServerUDPDelayedSend( void *inContext );
7410
7411 static void _DNSServerUDPReadHandler( void *inContext )
7412 {
7413 OSStatus err;
7414 SocketContext * const sockCtx = (SocketContext *) inContext;
7415 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
7416 struct timeval now;
7417 ssize_t n;
7418 sockaddr_ip clientAddr;
7419 socklen_t clientAddrLen;
7420 uint8_t * responsePtr = NULL; // malloc'd
7421 size_t responseLen;
7422 uint8_t msg[ 512 ];
7423
7424 gettimeofday( &now, NULL );
7425
7426 // Receive message.
7427
7428 clientAddrLen = (socklen_t) sizeof( clientAddr );
7429 n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &clientAddr.sa, &clientAddrLen );
7430 err = map_socket_value_errno( sockCtx->sock, n >= 0, n );
7431 require_noerr( err, exit );
7432
7433 ds_ulog( kLogLevelInfo, "UDP server received %zd bytes from %##a at %{du:time}.\n", n, &clientAddr, &now );
7434
7435 if( n < kDNSHeaderLength )
7436 {
7437 ds_ulog( kLogLevelInfo, "UDP DNS message is too small (%zd < %d).\n", n, kDNSHeaderLength );
7438 goto exit;
7439 }
7440
7441 ds_ulog( kLogLevelInfo, "UDP received message:\n\n%1{du:dnsmsg}", msg, (size_t) n );
7442
7443 // Create response.
7444
7445 err = _DNSServerAnswerQueryForUDP( msg, (size_t) n, &responsePtr, &responseLen );
7446 require_noerr_quiet( err, exit );
7447
7448 // Schedule response.
7449
7450 if( me->responseDelayMs > 0 )
7451 {
7452 DNSDelayedResponse * resp;
7453 DNSDelayedResponse ** ptr;
7454 DNSDelayedResponse * newResp;
7455
7456 newResp = (DNSDelayedResponse *) calloc( 1, sizeof( *newResp ) );
7457 require_action( newResp, exit, err = kNoMemoryErr );
7458
7459 SockAddrCopy( &clientAddr, &newResp->clientAddr );
7460 newResp->targetTicks = UpTicks() + MillisecondsToUpTicks( (uint64_t) me->responseDelayMs );
7461 newResp->msgLen = responseLen;
7462 newResp->msgPtr = responsePtr;
7463 responsePtr = NULL;
7464
7465 for( ptr = &me->responseList; ( resp = *ptr ) != NULL; ptr = &resp->next )
7466 {
7467 if( newResp->targetTicks < resp->targetTicks ) break;
7468 }
7469
7470 newResp->next = resp;
7471 *ptr = newResp;
7472
7473 if( me->responseList == newResp )
7474 {
7475 dispatch_source_forget( &me->responseTimer );
7476
7477 err = DispatchTimerCreate( dispatch_time_milliseconds( me->responseDelayMs ), DISPATCH_TIME_FOREVER,
7478 ( (uint64_t) me->responseDelayMs ) * kNanosecondsPerMillisecond / 10, me->queue,
7479 _DNSServerUDPDelayedSend, NULL, sockCtx, &me->responseTimer );
7480 require_noerr( err, exit );
7481 dispatch_resume( me->responseTimer );
7482 }
7483 }
7484 else
7485 {
7486 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
7487
7488 n = sendto( sockCtx->sock, (char *) responsePtr, responseLen, 0, &clientAddr.sa, clientAddrLen );
7489 err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) responseLen, n );
7490 require_noerr( err, exit );
7491 }
7492
7493 exit:
7494 FreeNullSafe( responsePtr );
7495 return;
7496 }
7497
7498 static void _DNSServerUDPDelayedSend( void *inContext )
7499 {
7500 OSStatus err;
7501 SocketContext * const sockCtx = (SocketContext *) inContext;
7502 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
7503 DNSDelayedResponse * resp;
7504 ssize_t n;
7505 uint64_t nowTicks;
7506 DNSDelayedResponse * freeList = NULL;
7507
7508 dispatch_source_forget( &me->responseTimer );
7509
7510 nowTicks = UpTicks();
7511 while( ( resp = me->responseList ) != NULL )
7512 {
7513 if( resp->targetTicks > nowTicks ) break;
7514 me->responseList = resp->next;
7515
7516 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7517 resp->msgLen, resp->msgPtr, resp->msgLen );
7518
7519 n = sendto( sockCtx->sock, (char *) resp->msgPtr, resp->msgLen, 0, &resp->clientAddr.sa,
7520 SockAddrGetSize( &resp->clientAddr ) );
7521 err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) resp->msgLen, n );
7522 check_noerr( err );
7523
7524 resp->next = freeList;
7525 freeList = resp;
7526 nowTicks = UpTicks();
7527 }
7528
7529 if( ( resp = me->responseList ) != NULL )
7530 {
7531 uint64_t remainingNs;
7532
7533 remainingNs = UpTicksToNanoseconds( resp->targetTicks - nowTicks );
7534 if( remainingNs > INT64_MAX ) remainingNs = INT64_MAX;
7535
7536 err = DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) remainingNs ), DISPATCH_TIME_FOREVER, 0,
7537 me->queue, _DNSServerUDPDelayedSend, NULL, sockCtx, &me->responseTimer );
7538 require_noerr( err, exit );
7539 dispatch_resume( me->responseTimer );
7540 }
7541
7542 exit:
7543 while( ( resp = freeList ) != NULL )
7544 {
7545 freeList = resp->next;
7546 DNSScheduledResponseFree( resp );
7547 }
7548 }
7549
7550 //===========================================================================================================================
7551 // _DNSServerAnswerQuery
7552 //===========================================================================================================================
7553
7554 #define kLabelPrefix_Alias "alias"
7555 #define kLabelPrefix_AliasTTL "alias-ttl"
7556 #define kLabelPrefix_Count "count"
7557 #define kLabelPrefix_TTL "ttl"
7558 #define kLabel_IPv4 "ipv4"
7559 #define kLabel_IPv6 "ipv6"
7560
7561 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7562
7563 static OSStatus
7564 _DNSServerInitializeResponseMessage(
7565 DataBuffer * inDB,
7566 unsigned int inID,
7567 unsigned int inFlags,
7568 const uint8_t * inQName,
7569 unsigned int inQType,
7570 unsigned int inQClass );
7571 static OSStatus
7572 _DNSServerAnswerQueryDynamically(
7573 const uint8_t * inQName,
7574 unsigned int inQType,
7575 unsigned int inQClass,
7576 Boolean inForTCP,
7577 DataBuffer * inDB );
7578
7579 static OSStatus
7580 _DNSServerAnswerQuery(
7581 const uint8_t * const inQueryPtr,
7582 const size_t inQueryLen,
7583 Boolean inForTCP,
7584 uint8_t ** outResponsePtr,
7585 size_t * outResponseLen )
7586 {
7587 OSStatus err;
7588 DataBuffer dataBuf;
7589 const uint8_t * ptr;
7590 const uint8_t * const queryEnd = &inQueryPtr[ inQueryLen ];
7591 const DNSHeader * qhdr;
7592 unsigned int msgID, qflags, qtype, qclass, rflags;
7593 uint8_t qname[ kDomainNameLengthMax ];
7594
7595 DataBuffer_Init( &dataBuf, NULL, 0, kDNSMaxTCPMessageSize );
7596
7597 require_action_quiet( inQueryLen >= kDNSHeaderLength, exit, err = kUnderrunErr );
7598
7599 qhdr = (const DNSHeader *) inQueryPtr;
7600 msgID = DNSHeaderGetID( qhdr );
7601 qflags = DNSHeaderGetFlags( qhdr );
7602
7603 // Minimal checking of the query message's header.
7604
7605 if( ( qflags & kDNSHeaderFlag_Response ) || // The message must be a query, not a response.
7606 ( DNSFlagsGetOpCode( qflags ) != kDNSOpCode_Query ) || // OPCODE must be QUERY (standard query).
7607 ( DNSHeaderGetQuestionCount( qhdr ) != 1 ) ) // There should be a single question.
7608 {
7609 err = kRequestErr;
7610 goto exit;
7611 }
7612
7613 // Get QNAME.
7614
7615 ptr = (const uint8_t *) &qhdr[ 1 ];
7616 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, ptr, qname, &ptr );
7617 require_noerr( err, exit );
7618
7619 // Get QTYPE and QCLASS.
7620
7621 require_action_quiet( ( queryEnd - ptr ) >= 4, exit, err = kUnderrunErr );
7622 qtype = DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields *) ptr );
7623 qclass = DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields *) ptr );
7624 ptr += 4;
7625
7626 // Create a tentative response message.
7627
7628 rflags = kDNSHeaderFlag_Response;
7629 if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired;
7630 DNSFlagsSetOpCode( rflags, kDNSOpCode_Query );
7631
7632 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7633 require_noerr( err, exit );
7634
7635 err = _DNSServerAnswerQueryDynamically( qname, qtype, qclass, inForTCP, &dataBuf );
7636 if( err )
7637 {
7638 DNSFlagsSetRCode( rflags, kDNSRCode_ServerFailure );
7639 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7640 require_noerr( err, exit );
7641 }
7642
7643 err = DataBuffer_Detach( &dataBuf, outResponsePtr, outResponseLen );
7644 require_noerr( err, exit );
7645
7646 exit:
7647 DataBuffer_Free( &dataBuf );
7648 return( err );
7649 }
7650
7651 static OSStatus
7652 _DNSServerInitializeResponseMessage(
7653 DataBuffer * inDB,
7654 unsigned int inID,
7655 unsigned int inFlags,
7656 const uint8_t * inQName,
7657 unsigned int inQType,
7658 unsigned int inQClass )
7659 {
7660 OSStatus err;
7661 DNSHeader header;
7662 DNSQuestionFixedFields fields;
7663
7664 DataBuffer_Reset( inDB );
7665
7666 memset( &header, 0, sizeof( header ) );
7667 DNSHeaderSetID( &header, inID );
7668 DNSHeaderSetFlags( &header, inFlags );
7669 DNSHeaderSetQuestionCount( &header, 1 );
7670
7671 err = DataBuffer_Append( inDB, &header, sizeof( header ) );
7672 require_noerr( err, exit );
7673
7674 err = DataBuffer_Append( inDB, inQName, DomainNameLength( inQName ) );
7675 require_noerr( err, exit );
7676
7677 DNSQuestionFixedFieldsInit( &fields, inQType, inQClass );
7678 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
7679 require_noerr( err, exit );
7680
7681 exit:
7682 return( err );
7683 }
7684
7685 static OSStatus
7686 _DNSServerAnswerQueryDynamically(
7687 const uint8_t * const inQName,
7688 const unsigned int inQType,
7689 const unsigned int inQClass,
7690 const Boolean inForTCP,
7691 DataBuffer * const inDB )
7692 {
7693 OSStatus err; // General-purpose error variable.
7694 const uint8_t * labelPtr; // QNAME label pointer.
7695 size_t labelLen; // QNAME label length.
7696 DNSHeader * hdr; // Response header pointer.
7697 unsigned int flags; // Response header flags.
7698 unsigned int rcode; // Response header response code.
7699 unsigned int answerCount = 0; // Number of answers contained in response.
7700 int32_t aliasCount = -1; // Arg from "alias" label. Valid values are in [2 .. 2^31 - 1].
7701 int count = -1; // First arg from "count" label. Valid values are in [1 .. 255].
7702 int randCount = -1; // Second arg from "count" label. Valid values are in [1 .. 255].
7703 int32_t ttl = -1; // Arg from "ttl" label. Valid values are in [0 .. 2^31 - 1].
7704 uint32_t aliasTTLs[ kMaxAliasTTLCount ]; // Args from "alias-ttl" label. Valid values are in [0 .. 2^31 - 1].
7705 int i; // General-purpose array index.
7706 Boolean useAliasTTLs = false; // True if QNAME contained a valid "alias-ttl" label.
7707 Boolean nameExists = false; // True if name specified by QNAME exists.
7708 Boolean nameHasA = false; // True if name specified by QNAME has an A record.
7709 Boolean nameHasAAAA = false; // True if name specified by QNAME has a AAAA record.
7710 Boolean notImplemented = false; // True if the kind of the query is not supported.
7711 Boolean truncated = false; // True if the response message is truncated.
7712 uint8_t namePtr[ 2 ]; // Name compression pointer.
7713
7714 if( inQClass != kDNSServiceClass_IN )
7715 {
7716 notImplemented = true;
7717 goto done;
7718 }
7719
7720 for( labelPtr = inQName; ( labelLen = *labelPtr ) != 0; labelPtr += ( 1 + labelLen ) )
7721 {
7722 const char * const labelStr = (const char *) &labelPtr[ 1 ];
7723 const char * next;
7724 long long arg;
7725 int n;
7726
7727 require_action( labelLen <= kDomainNameLengthMax, exit, err = kUnexpectedErr );
7728
7729 // Check if the first label is a valid alias TTL sequence label.
7730
7731 if( ( labelPtr == inQName ) && ( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_AliasTTL ) == 0 ) )
7732 {
7733 const char * src = &labelStr[ sizeof_string( kLabelPrefix_AliasTTL ) ];
7734 const char * const end = &labelStr[ labelLen ];
7735 int argCount = 0;
7736
7737 while( src < end )
7738 {
7739 n = SNScanF( src, (size_t)( end - src ), "-%10lld%#n", &arg, &next );
7740 if( n != 1 ) break;
7741 if( ( arg < 0 ) || ( arg > INT32_MAX ) ) break; // TTL must be >= 0 and <= (2^31 - 1).
7742 aliasTTLs[ argCount++ ] = (uint32_t) arg;
7743 src = next;
7744 }
7745 if( ( argCount > 0 ) && ( src == end ) )
7746 {
7747 aliasCount = argCount;
7748 useAliasTTLs = true;
7749 continue;
7750 }
7751 }
7752
7753 // Check if the first label is a valid alias label.
7754
7755 if( ( labelPtr == inQName ) && ( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_Alias ) == 0 ) )
7756 {
7757 const char * src = &labelStr[ sizeof_string( kLabelPrefix_Alias ) ];
7758 const char * const end = &labelStr[ labelLen ];
7759
7760 if( src == end )
7761 {
7762 aliasCount = 1;
7763 continue;
7764 }
7765
7766 n = SNScanF( src, (size_t)( end - src ), "-%10lld%#n", &arg, &next );
7767 if( ( n == 1 ) && ( next == end ) )
7768 {
7769 if( ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be >= 2 and <= (2^31 - 1).
7770 aliasCount = (int32_t) arg;
7771 continue;
7772 }
7773 }
7774
7775 // Check if the label is a valid count label.
7776
7777 if( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_Count ) == 0 )
7778 {
7779 const char * src = &labelStr[ sizeof_string( kLabelPrefix_Count ) ];
7780 const char * const end = &labelStr[ labelLen ];
7781
7782 n = SNScanF( src, (size_t)( end - src ), "-%3lld%#n", &arg, &next );
7783 if( n == 1 )
7784 {
7785 if( count > 0 ) break; // Count cannot be specified more than once.
7786 if( ( arg < 1 ) || ( arg > 255 ) ) break; // Count must be >= 1 and <= 255.
7787 count = (int) arg;
7788
7789 src = next;
7790 if( src < end )
7791 {
7792 n = SNScanF( src, (size_t)( end - src ), "-%3lld%#n", &arg, &next );
7793 if( ( n != 1 ) || ( next != end ) ) break;
7794 if( ( arg < count ) || ( arg > 255 ) ) break; // Rand count must be >= count and <= 255.
7795 randCount = (int) arg;
7796 }
7797 continue;
7798 }
7799 }
7800
7801 // Check if the label is a valid tag label.
7802
7803 if( strnicmp_prefix( labelStr, labelLen, "tag-" ) == 0 ) continue;
7804
7805 // Check if the label is a valid TTL label.
7806
7807 if( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_TTL ) == 0 )
7808 {
7809 const char * src = &labelStr[ sizeof_string( kLabelPrefix_TTL ) ];
7810 const char * const end = &labelStr[ labelLen ];
7811
7812 n = SNScanF( src, (size_t)( end - src ), "-%10lld%#n", &arg, &next );
7813 if( ( n == 1 ) && ( next == end ) )
7814 {
7815 if( ttl >= 0 ) break; // TTL cannot be specified more than once.
7816 if( ( arg < 0 ) || ( arg > INT32_MAX ) ) break; // TTL must be >= 0 and <= (2^31 - 1).
7817 ttl = (int32_t) arg;
7818 continue;
7819 }
7820 }
7821
7822 // Check if the label is a valid IPv4 or IPv6 label.
7823
7824 if( MemIEqual( labelStr, labelLen, kLabel_IPv4, sizeof_string( kLabel_IPv4 ) ) )
7825 {
7826 if( nameHasA || nameHasAAAA ) break; // Valid names have at most one IPv4 or IPv6 label.
7827 nameHasA = true;
7828 continue;
7829 }
7830 if( MemIEqual( labelStr, labelLen, kLabel_IPv6, sizeof_string( kLabel_IPv6 ) ) )
7831 {
7832 if( nameHasA || nameHasAAAA ) break; // Valid names have at most one IPv4 or IPv6 label.
7833 nameHasAAAA = true;
7834 continue;
7835 }
7836
7837 // If the remaining labels are equal to "d.test.", the name exists.
7838
7839 if( DomainNameEqual( labelPtr, (const uint8_t *) "\x01" "d" "\x04" "test" ) ) nameExists = true;
7840 break;
7841 }
7842 require_quiet( nameExists, done );
7843
7844 // Set default values for count and TTL, if those labels were present.
7845
7846 if( count <= 0 ) count = 1;
7847 check( ( gDNSServer_DefaultTTL >= 0 ) && ( gDNSServer_DefaultTTL <= INT32_MAX ) );
7848 if( ttl < 0 ) ttl = gDNSServer_DefaultTTL;
7849
7850 // Names that don't specify v4 or v6 have both A and AAAA records.
7851
7852 if( !nameHasA && !nameHasAAAA )
7853 {
7854 nameHasA = true;
7855 nameHasAAAA = true;
7856 }
7857
7858 check( ( count >= 1 ) && ( count <= 255 ) );
7859 check( ( randCount <= 0 ) || ( ( randCount >= count ) && ( randCount <= 255 ) ) );
7860
7861 if( aliasCount > 0 )
7862 {
7863 size_t nameOffset;
7864 uint8_t rdataLabel[ 1 + kDomainLabelLengthMax + 1 ];
7865
7866 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7867 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7868 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7869 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7870
7871 const uint8_t superPtr[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength + 1 + inQName[ 0 ] ) };
7872
7873 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7874
7875 nameOffset = kDNSHeaderLength;
7876
7877 for( i = aliasCount; i >= 1; --i )
7878 {
7879 size_t nameLen;
7880 size_t rdataLen;
7881 int j;
7882 uint32_t aliasTTL;
7883 uint8_t nameLabel[ 1 + kDomainLabelLengthMax + 1 ];
7884 DNSRecordFixedFields fields;
7885
7886 if( nameOffset <= kDNSCompressionOffsetMax )
7887 {
7888 namePtr[ 0 ] = (uint8_t)( ( ( nameOffset >> 8 ) & 0x3F ) | 0xC0 );
7889 namePtr[ 1 ] = (uint8_t)( nameOffset & 0xFF );
7890
7891 nameLen = sizeof( namePtr );
7892 }
7893 else
7894 {
7895 memcpy( nameLabel, rdataLabel, 1 + rdataLabel[ 0 ] );
7896 nameLen = 1 + nameLabel[ 0 ] + sizeof( superPtr );
7897 }
7898
7899 if( i >= 2 )
7900 {
7901 char * dst = (char *) &rdataLabel[ 1 ];
7902 char * const end = (char *) &rdataLabel[ countof( rdataLabel ) ];
7903
7904 if( useAliasTTLs )
7905 {
7906 err = SNPrintF_Add( &dst, end, kLabelPrefix_AliasTTL );
7907 require_noerr( err, exit );
7908
7909 for( j = aliasCount - ( i - 1 ); j < aliasCount; ++j )
7910 {
7911 err = SNPrintF_Add( &dst, end, "-%u", aliasTTLs[ j ] );
7912 require_noerr( err, exit );
7913 }
7914 }
7915 else
7916 {
7917 err = SNPrintF_Add( &dst, end, kLabelPrefix_Alias "%?{end}-%u", i == 2, i - 1 );
7918 require_noerr( err, exit );
7919 }
7920 rdataLabel[ 0 ] = (uint8_t)( dst - (char *) &rdataLabel[ 1 ] );
7921 rdataLen = 1 + rdataLabel[ 0 ] + sizeof( superPtr );
7922 }
7923 else
7924 {
7925 rdataLen = sizeof( superPtr );
7926 }
7927
7928 if( !inForTCP )
7929 {
7930 size_t recordLen = nameLen + sizeof( fields ) + rdataLen;
7931
7932 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
7933 {
7934 truncated = true;
7935 goto done;
7936 }
7937 }
7938 ++answerCount;
7939
7940 // Set CNAME record's NAME.
7941
7942 if( nameOffset <= kDNSCompressionOffsetMax )
7943 {
7944 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
7945 require_noerr( err, exit );
7946 }
7947 else
7948 {
7949 err = DataBuffer_Append( inDB, nameLabel, 1 + nameLabel[ 0 ] );
7950 require_noerr( err, exit );
7951
7952 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
7953 require_noerr( err, exit );
7954 }
7955
7956 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
7957
7958 aliasTTL = useAliasTTLs ? aliasTTLs[ aliasCount - i ] : ( (uint32_t) gDNSServer_DefaultTTL );
7959 DNSRecordFixedFieldsInit( &fields, kDNSServiceType_CNAME, kDNSServiceClass_IN, aliasTTL, rdataLen );
7960 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
7961 require_noerr( err, exit );
7962
7963 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
7964
7965 nameOffset = DataBuffer_GetLen( inDB );
7966
7967 // Set CNAME record's RDATA.
7968
7969 if( i >= 2 )
7970 {
7971 err = DataBuffer_Append( inDB, rdataLabel, 1 + rdataLabel[ 0 ] );
7972 require_noerr( err, exit );
7973 }
7974 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
7975 require_noerr( err, exit );
7976 }
7977
7978 namePtr[ 0 ] = superPtr[ 0 ];
7979 namePtr[ 1 ] = superPtr[ 1 ];
7980 }
7981 else
7982 {
7983 // There are no aliases, so initialize the name compression pointer to point to QNAME.
7984
7985 namePtr[ 0 ] = 0xC0;
7986 namePtr[ 1 ] = kDNSHeaderLength;
7987 }
7988
7989 if( ( ( inQType == kDNSServiceType_A ) && nameHasA ) ||
7990 ( ( inQType == kDNSServiceType_AAAA ) && nameHasAAAA ) )
7991 {
7992 uint8_t * lsb; // Pointer to the least significant byte of record data.
7993 size_t recordLen; // Length of the entire record.
7994 size_t rdataLen; // Length of record's RDATA.
7995 uint8_t rdata[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
7996 uint8_t randItegers[ 255 ]; // Array for random integers in [1 .. 255].
7997 DNSRecordFixedFields fields;
7998
7999 if( inQType == kDNSServiceType_A )
8000 {
8001 rdataLen = 4;
8002 WriteBig32( rdata, kTestDNSServerBaseAddrV4 );
8003 lsb = &rdata[ 3 ];
8004 }
8005 else
8006 {
8007 rdataLen = 16;
8008 memcpy( rdata, kTestDNSServerBaseAddrV6, 16 );
8009 lsb = &rdata[ 15 ];
8010 }
8011
8012 if( randCount > 0 )
8013 {
8014 // Populate the array with all integers between 1 and <randCount>, inclusive.
8015
8016 for( i = 0; i < randCount; ++i ) randItegers[ i ] = (uint8_t)( i + 1 );
8017
8018 // Create a contiguous subarray starting at index 0 that contains <count> randomly chosen integers between
8019 // 1 and <randCount>, inclusive.
8020 // Loop invariant 1: Array elements with indexes in [0 .. i - 1] have been randomly chosen.
8021 // Loop invariant 2: Array elements with indexes in [i .. randCount - 1] are candidates for being chosen.
8022
8023 for( i = 0; i < count; ++i )
8024 {
8025 uint8_t tmp;
8026 int j;
8027
8028 j = (int) RandomRange( i, randCount - 1 );
8029 if( i != j )
8030 {
8031 tmp = randItegers[ i ];
8032 randItegers[ i ] = randItegers[ j ];
8033 randItegers[ j ] = tmp;
8034 }
8035 }
8036 }
8037
8038 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8039 for( i = 0; i < count; ++i )
8040 {
8041 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8042 {
8043 truncated = true;
8044 goto done;
8045 }
8046 ++answerCount;
8047
8048 // Set record NAME.
8049
8050 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8051 require_noerr( err, exit );
8052
8053 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8054
8055 DNSRecordFixedFieldsInit( &fields, inQType, kDNSServiceClass_IN, ttl, rdataLen );
8056 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8057 require_noerr( err, exit );
8058
8059 // Set record RDATA.
8060
8061 *lsb = ( randCount > 0 ) ? randItegers[ i ] : ( *lsb + 1 );
8062
8063 err = DataBuffer_Append( inDB, rdata, rdataLen );
8064 require_noerr( err, exit );
8065 }
8066 }
8067
8068 done:
8069 hdr = (DNSHeader *) DataBuffer_GetPtr( inDB );
8070 flags = DNSHeaderGetFlags( hdr );
8071 if( truncated ) flags |= kDNSHeaderFlag_Truncation;
8072 if( notImplemented )
8073 {
8074 rcode = kDNSRCode_NotImplemented;
8075 }
8076 else
8077 {
8078 flags |= kDNSHeaderFlag_AuthAnswer;
8079 rcode = nameExists ? kDNSRCode_NoError : kDNSRCode_NXDomain;
8080 }
8081 DNSFlagsSetRCode( flags, rcode );
8082 DNSHeaderSetFlags( hdr, flags );
8083 DNSHeaderSetAnswerCount( hdr, answerCount );
8084 err = kNoErr;
8085
8086 exit:
8087 return( err );
8088 }
8089
8090 //===========================================================================================================================
8091 // _DNSServerTCPReadHandler
8092 //===========================================================================================================================
8093
8094 typedef struct
8095 {
8096 sockaddr_ip clientAddr; // Client's address.
8097 dispatch_source_t readSource; // Dispatch read source for client socket.
8098 dispatch_source_t writeSource; // Dispatch write source for client socket.
8099 size_t offset; // Offset into receive buffer.
8100 void * msgPtr; // Pointer to dynamically allocated message buffer.
8101 size_t msgLen; // Length of message buffer.
8102 Boolean readSuspended; // True if the read source is currently suspended.
8103 Boolean writeSuspended; // True if the write source is currently suspended.
8104 Boolean receivedLength; // True if receiving DNS message as opposed to the message length.
8105 uint8_t lenBuf[ 2 ]; // Buffer for two-octet message length field.
8106 iovec_t iov[ 2 ]; // IO vector for writing response message.
8107 iovec_t * iovPtr; // Vector pointer for SocketWriteData().
8108 int iovCount; // Vector count for SocketWriteData().
8109
8110 } TCPConnectionContext;
8111
8112 static void TCPConnectionStop( TCPConnectionContext *inContext );
8113 static void TCPConnectionContextFree( TCPConnectionContext *inContext );
8114 static void TCPConnectionReadHandler( void *inContext );
8115 static void TCPConnectionWriteHandler( void *inContext );
8116
8117 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8118
8119 static void _DNSServerTCPReadHandler( void *inContext )
8120 {
8121 OSStatus err;
8122 SocketContext * const sockCtx = (SocketContext *) inContext;
8123 TCPConnectionContext * connection;
8124 socklen_t clientAddrLen;
8125 SocketRef newSock = kInvalidSocketRef;
8126 SocketContext * newSockCtx = NULL;
8127
8128 connection = (TCPConnectionContext *) calloc( 1, sizeof( *connection ) );
8129 require_action( connection, exit, err = kNoMemoryErr );
8130
8131 clientAddrLen = (socklen_t) sizeof( connection->clientAddr );
8132 newSock = accept( sockCtx->sock, &connection->clientAddr.sa, &clientAddrLen );
8133 err = map_socket_creation_errno( newSock );
8134 require_noerr( err, exit );
8135
8136 err = SocketContextCreate( newSock, connection, &newSockCtx );
8137 require_noerr( err, exit );
8138 newSock = kInvalidSocketRef;
8139
8140 err = DispatchReadSourceCreate( newSockCtx->sock, NULL, TCPConnectionReadHandler, SocketContextCancelHandler,
8141 newSockCtx, &connection->readSource );
8142 require_noerr( err, exit );
8143 SocketContextRetain( newSockCtx );
8144 dispatch_resume( connection->readSource );
8145
8146 err = DispatchWriteSourceCreate( newSockCtx->sock, NULL, TCPConnectionWriteHandler, SocketContextCancelHandler,
8147 newSockCtx, &connection->writeSource );
8148 require_noerr( err, exit );
8149 SocketContextRetain( newSockCtx );
8150 connection->writeSuspended = true;
8151 connection = NULL;
8152
8153 exit:
8154 ForgetSocket( &newSock );
8155 SocketContextRelease( newSockCtx );
8156 TCPConnectionForget( &connection );
8157 }
8158
8159 //===========================================================================================================================
8160 // TCPConnectionStop
8161 //===========================================================================================================================
8162
8163 static void TCPConnectionStop( TCPConnectionContext *inContext )
8164 {
8165 dispatch_source_forget_ex( &inContext->readSource, &inContext->readSuspended );
8166 dispatch_source_forget_ex( &inContext->writeSource, &inContext->writeSuspended );
8167 }
8168
8169 //===========================================================================================================================
8170 // TCPConnectionContextFree
8171 //===========================================================================================================================
8172
8173 static void TCPConnectionContextFree( TCPConnectionContext *inContext )
8174 {
8175 check( !inContext->readSource );
8176 check( !inContext->writeSource );
8177 ForgetMem( &inContext->msgPtr );
8178 free( inContext );
8179 }
8180
8181 //===========================================================================================================================
8182 // TCPConnectionReadHandler
8183 //===========================================================================================================================
8184
8185 static void TCPConnectionReadHandler( void *inContext )
8186 {
8187 OSStatus err;
8188 SocketContext * const sockCtx = (SocketContext *) inContext;
8189 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8190 struct timeval now;
8191 uint8_t * responsePtr = NULL; // malloc'd
8192 size_t responseLen;
8193
8194 // Receive message length.
8195
8196 if( !connection->receivedLength )
8197 {
8198 err = SocketReadData( sockCtx->sock, connection->lenBuf, sizeof( connection->lenBuf ), &connection->offset );
8199 if( err == EWOULDBLOCK ) goto exit;
8200 require_noerr( err, exit );
8201
8202 connection->offset = 0;
8203 connection->msgLen = ReadBig16( connection->lenBuf );
8204 connection->msgPtr = malloc( connection->msgLen );
8205 require_action( connection->msgPtr, exit, err = kNoMemoryErr );
8206 connection->receivedLength = true;
8207 }
8208
8209 // Receive message.
8210
8211 err = SocketReadData( sockCtx->sock, connection->msgPtr, connection->msgLen, &connection->offset );
8212 if( err == EWOULDBLOCK ) goto exit;
8213 require_noerr( err, exit );
8214
8215 gettimeofday( &now, NULL );
8216 dispatch_suspend( connection->readSource );
8217 connection->readSuspended = true;
8218
8219 ds_ulog( kLogLevelInfo, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8220 connection->msgLen, &connection->clientAddr, &now );
8221
8222 if( connection->msgLen < kDNSHeaderLength )
8223 {
8224 ds_ulog( kLogLevelInfo, "TCP DNS message is too small (%zu < %d).\n", connection->msgLen, kDNSHeaderLength );
8225 goto exit;
8226 }
8227
8228 ds_ulog( kLogLevelInfo, "TCP received message:\n\n%1{du:dnsmsg}", connection->msgPtr, connection->msgLen );
8229
8230 // Create response.
8231
8232 err = _DNSServerAnswerQueryForTCP( connection->msgPtr, connection->msgLen, &responsePtr, &responseLen );
8233 require_noerr_quiet( err, exit );
8234
8235 // Send response.
8236
8237 ds_ulog( kLogLevelInfo, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
8238
8239 free( connection->msgPtr );
8240 connection->msgPtr = responsePtr;
8241 connection->msgLen = responseLen;
8242 responsePtr = NULL;
8243
8244 check( connection->msgLen <= UINT16_MAX );
8245 WriteBig16( connection->lenBuf, connection->msgLen );
8246 connection->iov[ 0 ].iov_base = connection->lenBuf;
8247 connection->iov[ 0 ].iov_len = sizeof( connection->lenBuf );
8248 connection->iov[ 1 ].iov_base = connection->msgPtr;
8249 connection->iov[ 1 ].iov_len = connection->msgLen;
8250
8251 connection->iovPtr = connection->iov;
8252 connection->iovCount = 2;
8253
8254 check( connection->writeSuspended );
8255 dispatch_resume( connection->writeSource );
8256 connection->writeSuspended = false;
8257
8258 exit:
8259 FreeNullSafe( responsePtr );
8260 if( err && ( err != EWOULDBLOCK ) ) TCPConnectionForget( &connection );
8261 }
8262
8263 //===========================================================================================================================
8264 // TCPConnectionWriteHandler
8265 //===========================================================================================================================
8266
8267 static void TCPConnectionWriteHandler( void *inContext )
8268 {
8269 OSStatus err;
8270 SocketContext * const sockCtx = (SocketContext *) inContext;
8271 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8272
8273 err = SocketWriteData( sockCtx->sock, &connection->iovPtr, &connection->iovCount );
8274 if( err == EWOULDBLOCK ) goto exit;
8275 check_noerr( err );
8276
8277 TCPConnectionForget( &connection );
8278
8279 exit:
8280 return;
8281 }
8282
8283 //===========================================================================================================================
8284 // GAIPerfCmd
8285 //===========================================================================================================================
8286
8287 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
8288
8289 typedef struct GAITesterPrivate * GAITesterRef;
8290 typedef struct GAITestCase GAITestCase;
8291
8292 typedef uint32_t GAITesterEventType;
8293 #define kGAITesterEvent_Started 1
8294 #define kGAITesterEvent_Stopped 2
8295
8296 typedef struct
8297 {
8298 const char * name; // Domain name that was resolved.
8299 int64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
8300 int64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
8301 int64_t timeUs; // Time in microseconds that it took to get all expected address results.
8302
8303 } GAITestItemResult;
8304
8305 typedef void ( *GAITesterEventHandler_f )( GAITesterEventType inType, void *inContext );
8306 typedef void
8307 ( *GAITesterResultsHandler_f )(
8308 const char * inCaseTitle,
8309 MicroTime64 inCaseStartTime,
8310 MicroTime64 inCaseEndTime,
8311 const GAITestItemResult * inResults,
8312 size_t inResultCount,
8313 size_t inItemCount,
8314 void * inContext );
8315
8316 typedef unsigned int GAITestAddrType;
8317 #define kGAITestAddrType_None 0
8318 #define kGAITestAddrType_IPv4 ( 1U << 0 )
8319 #define kGAITestAddrType_IPv6 ( 1U << 1 )
8320 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
8321
8322 #define GAITestAddrTypeIsValid( X ) \
8323 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
8324
8325 typedef enum
8326 {
8327 kGAIPerfOutputFormat_JSON = 1,
8328 kGAIPerfOutputFormat_XML = 2,
8329 kGAIPerfOutputFormat_Binary = 3
8330
8331 } GAIPerfOutputFormatType;
8332
8333 typedef struct
8334 {
8335 GAITesterRef tester; // GAI tester object.
8336 CFMutableArrayRef caseResults; // Array of test case results.
8337 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
8338 GAIPerfOutputFormatType outputFormat; // Format of test results output.
8339 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
8340 unsigned int serverDelayMs; // Amount of additional time to have server delay its responses.
8341 unsigned int defaultIterCount; // Default test case iteration count.
8342 dispatch_source_t sigIntSource; // Dispatch source for SIGINT.
8343 dispatch_source_t sigTermSource; // Dispatch source for SIGTERM.
8344 Boolean gotSignal; // True if SIGINT or SIGTERM was caught.
8345 Boolean testerStarted; // True if the GAI tester was started.
8346 Boolean appendNewLine; // True if a newline character should be appended to JSON output.
8347
8348 } GAIPerfContext;
8349
8350 static void GAIPerfContextFree( GAIPerfContext *inContext );
8351 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext );
8352 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext );
8353 static void GAIPerfEventHandler( GAITesterEventType inType, void *inContext );
8354 static void
8355 GAIPerfResultsHandler(
8356 const char * inCaseTitle,
8357 MicroTime64 inCaseStartTime,
8358 MicroTime64 inCaseEndTime,
8359 const GAITestItemResult * inResults,
8360 size_t inResultCount,
8361 size_t inItemCount,
8362 void * inContext );
8363 static void GAIPerfSignalHandler( void *inContext );
8364
8365 CFTypeID GAITesterGetTypeID( void );
8366 static OSStatus
8367 GAITesterCreate(
8368 dispatch_queue_t inQueue,
8369 int inCallDelayMs,
8370 int inServerDelayMs,
8371 int inServerDefaultTTL,
8372 GAITesterRef * outTester );
8373 static void GAITesterStart( GAITesterRef inTester );
8374 static void GAITesterStop( GAITesterRef inTester );
8375 static void GAITesterAddCase( GAITesterRef inTester, GAITestCase *inCase );
8376 static void
8377 GAITesterSetEventHandler(
8378 GAITesterRef inTester,
8379 GAITesterEventHandler_f inEventHandler,
8380 void * inEventContext );
8381 static void
8382 GAITesterSetResultsHandler(
8383 GAITesterRef inTester,
8384 GAITesterResultsHandler_f inResultsHandler,
8385 void * inResultsContext );
8386
8387 static OSStatus GAITestCaseCreate( const char *inTitle, unsigned int inTimeLimitMs, GAITestCase **outSet );
8388 static void GAITestCaseFree( GAITestCase *inCase );
8389 static OSStatus
8390 GAITestCaseAddItem(
8391 GAITestCase * inCase,
8392 unsigned int inAliasCount,
8393 unsigned int inAddressCount,
8394 int inTTL,
8395 GAITestAddrType inHasAddrs,
8396 GAITestAddrType inWantAddrs,
8397 unsigned int inItemCount );
8398 static OSStatus GAITestCaseAddLocalHostItem( GAITestCase *inCase, GAITestAddrType inWantAddrs, unsigned int inItemCount );
8399
8400 #define kGAIPerfTestSuite_Basic 1
8401 #define kGAIPerfTestSuite_Advanced 2
8402
8403 static void GAIPerfCmd( void )
8404 {
8405 OSStatus err;
8406 GAIPerfContext * context;
8407 int suiteValue;
8408
8409 context = (GAIPerfContext *) calloc( 1, sizeof( *context ) );
8410 require_action( context, exit, err = kNoMemoryErr );
8411
8412 context->caseResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
8413 require_action( context->caseResults, exit, err = kNoMemoryErr );
8414
8415 context->outputFormat = (GAIPerfOutputFormatType) CLIArgToValue( "format", gGAIPerf_OutputFormat, &err,
8416 "json", kGAIPerfOutputFormat_JSON,
8417 "xml", kGAIPerfOutputFormat_XML,
8418 "binary", kGAIPerfOutputFormat_Binary,
8419 NULL );
8420 require_noerr_quiet( err, exit );
8421
8422 context->callDelayMs = ( gGAIPerf_CallDelayMs >= 0 ) ? (unsigned int) gGAIPerf_CallDelayMs : 0;
8423 context->serverDelayMs = ( gGAIPerf_ServerDelayMs >= 0 ) ? (unsigned int) gGAIPerf_ServerDelayMs : 0;
8424 context->defaultIterCount = ( gGAIPerf_DefaultIterCount >= 0 ) ? (unsigned int) gGAIPerf_DefaultIterCount : 0;
8425 context->appendNewLine = gGAIPerf_OutputAppendNewLine ? true : false;
8426
8427 if( gGAIPerf_OutputFilePath )
8428 {
8429 context->outputFilePath = strdup( gGAIPerf_OutputFilePath );
8430 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
8431 }
8432
8433 err = GAITesterCreate( dispatch_get_main_queue(), (int) context->callDelayMs, (int) context->serverDelayMs,
8434 kGAIPerfStandardTTL, &context->tester );
8435 require_noerr( err, exit );
8436
8437 check( gGAIPerf_TestSuite );
8438 suiteValue = CLIArgToValue( "suite", gGAIPerf_TestSuite, &err,
8439 "basic", kGAIPerfTestSuite_Basic,
8440 "advanced", kGAIPerfTestSuite_Advanced,
8441 NULL );
8442 require_noerr_quiet( err, exit );
8443
8444 switch( suiteValue )
8445 {
8446 case kGAIPerfTestSuite_Basic:
8447 err = GAIPerfAddBasicTestCases( context );
8448 require_noerr( err, exit );
8449 break;
8450
8451 case kGAIPerfTestSuite_Advanced:
8452 err = GAIPerfAddAdvancedTestCases( context );
8453 require_noerr( err, exit );
8454 break;
8455
8456 default:
8457 err = kValueErr;
8458 break;
8459 }
8460
8461 GAITesterSetEventHandler( context->tester, GAIPerfEventHandler, context );
8462 GAITesterSetResultsHandler( context->tester, GAIPerfResultsHandler, context );
8463
8464 signal( SIGINT, SIG_IGN );
8465 err = DispatchSignalSourceCreate( SIGINT, GAIPerfSignalHandler, context, &context->sigIntSource );
8466 require_noerr( err, exit );
8467 dispatch_resume( context->sigIntSource );
8468
8469 signal( SIGTERM, SIG_IGN );
8470 err = DispatchSignalSourceCreate( SIGTERM, GAIPerfSignalHandler, context, &context->sigTermSource );
8471 require_noerr( err, exit );
8472 dispatch_resume( context->sigTermSource );
8473
8474 GAITesterStart( context->tester );
8475 dispatch_main();
8476
8477 exit:
8478 if( context ) GAIPerfContextFree( context );
8479 if( err ) exit( 1 );
8480 }
8481
8482 //===========================================================================================================================
8483 // GAIPerfContextFree
8484 //===========================================================================================================================
8485
8486 static void GAIPerfContextFree( GAIPerfContext *inContext )
8487 {
8488 ForgetCF( &inContext->tester );
8489 ForgetCF( &inContext->caseResults );
8490 ForgetMem( &inContext->outputFilePath );
8491 dispatch_source_forget( &inContext->sigIntSource );
8492 dispatch_source_forget( &inContext->sigTermSource );
8493 free( inContext );
8494 }
8495
8496 //===========================================================================================================================
8497 // GAIPerfAddAdvancedTestCases
8498 //===========================================================================================================================
8499
8500 #define kTestCaseTitleBufferSize 128
8501
8502 static void
8503 _GAIPerfWriteTestCaseTitle(
8504 char inBuffer[ kTestCaseTitleBufferSize ],
8505 unsigned int inCNAMERecordCount,
8506 unsigned int inARecordCount,
8507 unsigned int inAAAARecordCount,
8508 GAITestAddrType inRequested,
8509 unsigned int inIterationCount,
8510 Boolean inIterationsAreUnique );
8511 static void
8512 _GAIPerfWriteLocalHostTestCaseTitle(
8513 char inBuffer[ kTestCaseTitleBufferSize ],
8514 GAITestAddrType inRequested,
8515 unsigned int inIterationCount );
8516 static unsigned int
8517 _GAIPerfTimeLimitMs(
8518 unsigned int inCallDelayMs,
8519 unsigned int inServerDelayMs,
8520 unsigned int inIterationCount );
8521
8522 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
8523 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
8524
8525 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext )
8526 {
8527 OSStatus err;
8528 unsigned int aliasCount, addressCount, timeLimitMs, i;
8529 GAITestCase * testCase = NULL;
8530 char title[ kTestCaseTitleBufferSize ];
8531
8532 aliasCount = 0;
8533 while( aliasCount <= kGAIPerfAdvancedTestSuite_MaxAliasCount )
8534 {
8535 for( addressCount = 1; addressCount <= kGAIPerfAdvancedTestSuite_MaxAddrCount; addressCount *= 2 )
8536 {
8537 // Add a test case to resolve a domain name with
8538 //
8539 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
8540 //
8541 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
8542 // requires server queries.
8543
8544 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
8545 inContext->defaultIterCount, true );
8546
8547 timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, inContext->serverDelayMs,
8548 inContext->defaultIterCount );
8549 err = GAITestCaseCreate( title, timeLimitMs, &testCase );
8550 require_noerr( err, exit );
8551
8552 for( i = 0; i < inContext->defaultIterCount; ++i )
8553 {
8554 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
8555 kGAITestAddrType_Both, kGAITestAddrType_Both, 1 );
8556 require_noerr( err, exit );
8557 }
8558
8559 GAITesterAddCase( inContext->tester, testCase );
8560 testCase = NULL;
8561
8562 // Add a test case to resolve a domain name with
8563 //
8564 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
8565 //
8566 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
8567 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
8568 // ideally require no server queries, i.e., the results should come from the cache.
8569
8570 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
8571 inContext->defaultIterCount, false );
8572
8573 timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, inContext->serverDelayMs, 1 ) +
8574 _GAIPerfTimeLimitMs( inContext->callDelayMs, 0, inContext->defaultIterCount );
8575 err = GAITestCaseCreate( title, timeLimitMs, &testCase );
8576 require_noerr( err, exit );
8577
8578 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
8579 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->defaultIterCount + 1 );
8580 require_noerr( err, exit );
8581
8582 GAITesterAddCase( inContext->tester, testCase );
8583 testCase = NULL;
8584 }
8585
8586 if( aliasCount == 0 ) aliasCount = 1;
8587 else aliasCount *= 2;
8588 }
8589
8590 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
8591
8592 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
8593
8594 timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, 0, inContext->defaultIterCount );
8595 err = GAITestCaseCreate( title, timeLimitMs, &testCase );
8596 require_noerr( err, exit );
8597
8598 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->defaultIterCount );
8599 require_noerr( err, exit );
8600
8601 GAITesterAddCase( inContext->tester, testCase );
8602 testCase = NULL;
8603
8604 exit:
8605 if( testCase ) GAITestCaseFree( testCase );
8606 return( err );
8607 }
8608
8609 //===========================================================================================================================
8610 // _GAIPerfWriteTestCaseTitle
8611 //===========================================================================================================================
8612
8613 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
8614 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
8615 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
8616 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
8617 "" )
8618
8619 static void
8620 _GAIPerfWriteTestCaseTitle(
8621 char inBuffer[ kTestCaseTitleBufferSize ],
8622 unsigned int inCNAMERecordCount,
8623 unsigned int inARecordCount,
8624 unsigned int inAAAARecordCount,
8625 GAITestAddrType inRequested,
8626 unsigned int inIterationCount,
8627 Boolean inIterationsAreUnique )
8628 {
8629 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
8630 inCNAMERecordCount, inARecordCount, inAAAARecordCount, GAITestAddrTypeToRequestKeyValue( inRequested ),
8631 inIterationCount, inIterationsAreUnique, ",unique" );
8632 }
8633
8634 //===========================================================================================================================
8635 // _GAIPerfWriteLocalHostTestCaseTitle
8636 //===========================================================================================================================
8637
8638 static void
8639 _GAIPerfWriteLocalHostTestCaseTitle(
8640 char inBuffer[ kTestCaseTitleBufferSize ],
8641 GAITestAddrType inRequested,
8642 unsigned int inIterationCount )
8643 {
8644 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=localhost,req=%s,iterations=%u",
8645 GAITestAddrTypeToRequestKeyValue( inRequested ), inIterationCount );
8646 }
8647
8648 //===========================================================================================================================
8649 // _GAIPerfTimeLimitMs
8650 //===========================================================================================================================
8651
8652 static unsigned int
8653 _GAIPerfTimeLimitMs(
8654 unsigned int inCallDelayMs,
8655 unsigned int inServerDelayMs,
8656 unsigned int inIterationCount )
8657 {
8658 // Allow each iteration 20 ms to complete (in addition to the call and server delay times).
8659
8660 return( ( inCallDelayMs + inServerDelayMs + 20 ) * inIterationCount );
8661 }
8662
8663 //===========================================================================================================================
8664 // GAIPerfAddBasicTestCases
8665 //===========================================================================================================================
8666
8667 #define kGAIPerfBasicTestSuite_AliasCount 2
8668 #define kGAIPerfBasicTestSuite_AddrCount 4
8669
8670 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext )
8671 {
8672 OSStatus err;
8673 GAITestCase * testCase = NULL;
8674 char title[ kTestCaseTitleBufferSize ];
8675 unsigned int timeLimitMs, i;
8676
8677 // Test Case #1:
8678 // Resolve a domain name with
8679 //
8680 // 2 CNAME records, 4 A records, and 4 AAAA records
8681 //
8682 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
8683 // queries.
8684
8685 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
8686 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
8687 inContext->defaultIterCount, true );
8688
8689 timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, inContext->serverDelayMs, inContext->defaultIterCount );
8690 err = GAITestCaseCreate( title, timeLimitMs, &testCase );
8691 require_noerr( err, exit );
8692
8693 for( i = 0; i < inContext->defaultIterCount; ++i )
8694 {
8695 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
8696 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, 1 );
8697 require_noerr( err, exit );
8698 }
8699
8700 GAITesterAddCase( inContext->tester, testCase );
8701 testCase = NULL;
8702
8703 // Test Case #2:
8704 // Resolve a domain name with
8705 //
8706 // 2 CNAME records, 4 A records, and 4 AAAA records
8707 //
8708 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
8709 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
8710 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
8711
8712 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
8713 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
8714 inContext->defaultIterCount, false );
8715
8716 timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, inContext->serverDelayMs, 1 ) +
8717 _GAIPerfTimeLimitMs( inContext->callDelayMs, 0, inContext->defaultIterCount );
8718 err = GAITestCaseCreate( title, timeLimitMs, &testCase );
8719 require_noerr( err, exit );
8720
8721 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
8722 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->defaultIterCount + 1 );
8723 require_noerr( err, exit );
8724
8725 GAITesterAddCase( inContext->tester, testCase );
8726 testCase = NULL;
8727
8728 // Test Case #3:
8729 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
8730
8731 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
8732
8733 timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, 0, inContext->defaultIterCount );
8734 err = GAITestCaseCreate( title, timeLimitMs, &testCase );
8735 require_noerr( err, exit );
8736
8737 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->defaultIterCount );
8738 require_noerr( err, exit );
8739
8740 GAITesterAddCase( inContext->tester, testCase );
8741 testCase = NULL;
8742
8743 exit:
8744 if( testCase ) GAITestCaseFree( testCase );
8745 return( err );
8746 }
8747
8748 //===========================================================================================================================
8749 // GAIPerfEventHandler
8750 //===========================================================================================================================
8751
8752 static void _GAIPerfOutputResultsAndExit( GAIPerfContext *inContext ) ATTRIBUTE_NORETURN;
8753
8754 static void GAIPerfEventHandler( GAITesterEventType inType, void *inContext )
8755 {
8756 GAIPerfContext * const context = (GAIPerfContext *) inContext;
8757
8758 if( inType == kGAITesterEvent_Started )
8759 {
8760 context->testerStarted = true;
8761 }
8762 else if( inType == kGAITesterEvent_Stopped )
8763 {
8764 if( context->gotSignal ) exit( 1 );
8765 _GAIPerfOutputResultsAndExit( context );
8766 }
8767 }
8768
8769 //===========================================================================================================================
8770 // _GAIPerfOutputResultsAndExit
8771 //===========================================================================================================================
8772
8773 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
8774 #define kGAIPerfResultsKey_Info CFSTR( "info" )
8775
8776 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
8777 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
8778
8779 static void _GAIPerfOutputResultsAndExit( GAIPerfContext *inContext )
8780 {
8781 OSStatus err;
8782 CFPropertyListRef plist = NULL;
8783 CFDataRef results = NULL;
8784 FILE * file = NULL;
8785
8786 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
8787 "{"
8788 "%kO=%O"
8789 "%kO="
8790 "{"
8791 "%kO=%lli"
8792 "%kO=%lli"
8793 "}"
8794 "}",
8795 kGAIPerfResultsKey_TestCases, inContext->caseResults,
8796 kGAIPerfResultsKey_Info,
8797 kGAIPerfInfoKey_CallDelay, (int64_t) inContext->callDelayMs,
8798 kGAIPerfInfoKey_ServerDelay, (int64_t) inContext->serverDelayMs );
8799 require_noerr( err, exit );
8800
8801 // Convert results to a specific format.
8802
8803 switch( inContext->outputFormat )
8804 {
8805 case kGAIPerfOutputFormat_JSON:
8806 results = CFCreateJSONData( plist, kJSONFlags_None, NULL );
8807 require_action( results, exit, err = kUnknownErr );
8808 break;
8809
8810 case kGAIPerfOutputFormat_XML:
8811 results = CFPropertyListCreateData( NULL, plist, kCFPropertyListXMLFormat_v1_0, 0, NULL );
8812 require_action( results, exit, err = kUnknownErr );
8813 break;
8814
8815 case kGAIPerfOutputFormat_Binary:
8816 results = CFPropertyListCreateData( NULL, plist, kCFPropertyListBinaryFormat_v1_0, 0, NULL );
8817 require_action( results, exit, err = kUnknownErr );
8818 break;
8819
8820 default:
8821 err = kValueErr;
8822 goto exit;
8823 }
8824
8825 // Write formatted results to file or stdout.
8826
8827 if( inContext->outputFilePath )
8828 {
8829 file = fopen( inContext->outputFilePath, "wb" );
8830 err = map_global_value_errno( file, file );
8831 require_noerr( err, exit );
8832 }
8833 else
8834 {
8835 file = stdout;
8836 }
8837
8838 err = WriteANSIFile( file, CFDataGetBytePtr( results ), (size_t) CFDataGetLength( results ) );
8839 require_noerr( err, exit );
8840
8841 // Write a trailing newline for JSON-formatted results if requested.
8842
8843 if( ( inContext->outputFormat == kGAIPerfOutputFormat_JSON ) && inContext->appendNewLine )
8844 {
8845 err = WriteANSIFile( file, "\n", 1 );
8846 require_noerr( err, exit );
8847 }
8848
8849 exit:
8850 CFReleaseNullSafe( plist );
8851 CFReleaseNullSafe( results );
8852 if( file && ( file != stdout ) ) fclose( file );
8853 GAIPerfContextFree( inContext );
8854 exit( err ? 1 : 0 );
8855 }
8856
8857 //===========================================================================================================================
8858 // GAIPerfResultsHandler
8859 //===========================================================================================================================
8860
8861 // Keys for test case dictionary
8862
8863 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
8864 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTimeUs" )
8865 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTimeUs" )
8866 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
8867 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
8868 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
8869 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
8870 #define kGAIPerfTestCaseKey_TimedOut CFSTR( "timedOut" )
8871
8872 // Keys for test case results array entry dictionaries
8873
8874 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
8875 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
8876 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
8877 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
8878
8879 // Keys for test case stats dictionaries
8880
8881 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
8882 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
8883 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
8884 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
8885 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
8886
8887 typedef struct
8888 {
8889 double min;
8890 double max;
8891 double mean;
8892 double stdDev;
8893
8894 } GAIPerfStats;
8895
8896 #define GAIPerfStatsInit( X ) \
8897 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
8898
8899 static void
8900 GAIPerfResultsHandler(
8901 const char * inCaseTitle,
8902 MicroTime64 inCaseStartTime,
8903 MicroTime64 inCaseEndTime,
8904 const GAITestItemResult * inResults,
8905 size_t inResultCount,
8906 size_t inItemCount,
8907 void * inContext )
8908 {
8909 OSStatus err;
8910 GAIPerfContext * const context = (GAIPerfContext *) inContext;
8911 int namesAreDynamic, namesAreUnique;
8912 const char * ptr;
8913 size_t count, startIndex;
8914 CFMutableArrayRef results = NULL;
8915 GAIPerfStats stats, firstStats, connStats;
8916 double sum, firstSum, connSum, value, diff;
8917 size_t keyValueLen, i;
8918 char keyValue[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
8919
8920 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
8921 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with the
8922 // domain name's CNAME, A, and AAAA records.
8923
8924 namesAreDynamic = false;
8925 namesAreUnique = false;
8926 ptr = inCaseTitle;
8927 while( ParseQuotedEscapedString( ptr, NULL, ",", keyValue, sizeof( keyValue ), &keyValueLen, NULL, &ptr ) )
8928 {
8929 if( strnicmpx( keyValue, keyValueLen, "name=dynamic" ) == 0 )
8930 {
8931 namesAreDynamic = true;
8932 }
8933 else if( strnicmpx( keyValue, keyValueLen, "unique" ) == 0 )
8934 {
8935 namesAreUnique = true;
8936 }
8937 if( namesAreDynamic && namesAreUnique ) break;
8938 }
8939
8940 if( namesAreDynamic && !namesAreUnique && ( inItemCount > 0 ) )
8941 {
8942 count = ( inResultCount > 0 ) ? ( inResultCount - 1 ) : 0;
8943 startIndex = 1;
8944 }
8945 else
8946 {
8947 count = inResultCount;
8948 startIndex = 0;
8949 }
8950
8951 results = CFArrayCreateMutable( NULL, (CFIndex) count, &kCFTypeArrayCallBacks );
8952 require_action( results, exit, err = kNoMemoryErr );
8953
8954 GAIPerfStatsInit( &stats );
8955 GAIPerfStatsInit( &firstStats );
8956 GAIPerfStatsInit( &connStats );
8957
8958 sum = 0.0;
8959 firstSum = 0.0;
8960 connSum = 0.0;
8961 for( i = startIndex; i < count; ++i )
8962 {
8963 value = (double) inResults[ i ].timeUs;
8964 if( value < stats.min ) stats.min = value;
8965 if( value > stats.max ) stats.max = value;
8966 sum += value;
8967
8968 value = (double) inResults[ i ].firstTimeUs;
8969 if( value < firstStats.min ) firstStats.min = value;
8970 if( value > firstStats.max ) firstStats.max = value;
8971 firstSum += value;
8972
8973 value = (double) inResults[ i ].connectionTimeUs;
8974 if( value < connStats.min ) connStats.min = value;
8975 if( value > connStats.max ) connStats.max = value;
8976 connSum += value;
8977
8978 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results,
8979 "{"
8980 "%kO=%s"
8981 "%kO=%lli"
8982 "%kO=%lli"
8983 "%kO=%lli"
8984 "}",
8985 kGAIPerfTestCaseResultKey_Name, inResults[ i ].name,
8986 kGAIPerfTestCaseResultKey_ConnectionTime, inResults[ i ].connectionTimeUs,
8987 kGAIPerfTestCaseResultKey_FirstTime, inResults[ i ].firstTimeUs,
8988 kGAIPerfTestCaseResultKey_Time, inResults[ i ].timeUs );
8989 require_noerr( err, exit );
8990 }
8991
8992 if( count > 0 )
8993 {
8994 stats.mean = sum / count;
8995 firstStats.mean = firstSum / count;
8996 connStats.mean = connSum / count;
8997
8998 sum = 0.0;
8999 firstSum = 0.0;
9000 connSum = 0.0;
9001 for( i = startIndex; i < count; ++i )
9002 {
9003 diff = stats.mean - (double) inResults[ i ].timeUs;
9004 sum += ( diff * diff );
9005
9006 diff = firstStats.mean - (double) inResults[ i ].firstTimeUs;
9007 firstSum += ( diff * diff );
9008
9009 diff = connStats.mean - (double) inResults[ i ].connectionTimeUs;
9010 connSum += ( diff * diff );
9011 }
9012 stats.stdDev = sqrt( sum / count );
9013 firstStats.stdDev = sqrt( firstSum / count );
9014 connStats.stdDev = sqrt( connSum / count );
9015 }
9016
9017 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->caseResults,
9018 "{"
9019 "%kO=%s"
9020 "%kO=%lli"
9021 "%kO=%lli"
9022 "%kO=%O"
9023 "%kO="
9024 "{"
9025 "%kO=%lli"
9026 "%kO=%f"
9027 "%kO=%f"
9028 "%kO=%f"
9029 "%kO=%f"
9030 "}"
9031 "%kO="
9032 "{"
9033 "%kO=%lli"
9034 "%kO=%f"
9035 "%kO=%f"
9036 "%kO=%f"
9037 "%kO=%f"
9038 "}"
9039 "%kO="
9040 "{"
9041 "%kO=%lli"
9042 "%kO=%f"
9043 "%kO=%f"
9044 "%kO=%f"
9045 "%kO=%f"
9046 "}"
9047 "%kO=%b"
9048 "}",
9049 kGAIPerfTestCaseKey_Title, inCaseTitle,
9050 kGAIPerfTestCaseKey_StartTime, (int64_t) inCaseStartTime,
9051 kGAIPerfTestCaseKey_EndTime, (int64_t) inCaseEndTime,
9052 kGAIPerfTestCaseKey_Results, results,
9053 kGAIPerfTestCaseKey_Stats,
9054 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
9055 kGAIPerfTestCaseStatsKey_Min, stats.min,
9056 kGAIPerfTestCaseStatsKey_Max, stats.max,
9057 kGAIPerfTestCaseStatsKey_Mean, stats.mean,
9058 kGAIPerfTestCaseStatsKey_StdDev, stats.stdDev,
9059 kGAIPerfTestCaseKey_FirstStats,
9060 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
9061 kGAIPerfTestCaseStatsKey_Min, firstStats.min,
9062 kGAIPerfTestCaseStatsKey_Max, firstStats.max,
9063 kGAIPerfTestCaseStatsKey_Mean, firstStats.mean,
9064 kGAIPerfTestCaseStatsKey_StdDev, firstStats.stdDev,
9065 kGAIPerfTestCaseKey_ConnectionStats,
9066 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
9067 kGAIPerfTestCaseStatsKey_Min, connStats.min,
9068 kGAIPerfTestCaseStatsKey_Max, connStats.max,
9069 kGAIPerfTestCaseStatsKey_Mean, connStats.mean,
9070 kGAIPerfTestCaseStatsKey_StdDev, connStats.stdDev,
9071 kGAIPerfTestCaseKey_TimedOut, ( inResultCount < inItemCount ) ? true : false );
9072 require_noerr( err, exit );
9073
9074 exit:
9075 CFReleaseNullSafe( results );
9076 }
9077
9078 //===========================================================================================================================
9079 // GAIPerfSignalHandler
9080 //===========================================================================================================================
9081
9082 static void GAIPerfSignalHandler( void *inContext )
9083 {
9084 GAIPerfContext * const context = (GAIPerfContext *) inContext;
9085
9086 context->gotSignal = true;
9087 if( context->tester && context->testerStarted )
9088 {
9089 GAITesterStop( context->tester );
9090 }
9091 else
9092 {
9093 exit( 1 );
9094 }
9095 }
9096
9097 //===========================================================================================================================
9098 // GAITesterCreate
9099 //===========================================================================================================================
9100
9101 typedef enum
9102 {
9103 kGAITestConnType_UseMainConnection = 1,
9104 kGAITestConnType_OwnSharedConnection = 2
9105
9106 } GAITestConnType;
9107
9108 typedef struct GAITestItem GAITestItem;
9109 struct GAITestItem
9110 {
9111 GAITestItem * next; // Next test item in list.
9112 char * name; // Domain name to resolve.
9113 int64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
9114 int64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
9115 int64_t timeUs; // Time in microseconds that it took to get all expected address results.
9116 unsigned int addressCount; // Address count of the domain name, i.e., the Count label argument.
9117 Boolean hasV4; // True if the domain name has one or more IPv4 addresses.
9118 Boolean hasV6; // True if the domain name has one or more IPv6 addresses.
9119 Boolean wantV4; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
9120 Boolean wantV6; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
9121 };
9122
9123 struct GAITestCase
9124 {
9125 GAITestCase * next; // Next test case in list.
9126 GAITestItem * itemList; // List of test items.
9127 char * title; // Title of the test case.
9128 unsigned int timeLimitMs; // Time limit in milliseconds for the test case's completion.
9129 };
9130
9131 struct GAITesterPrivate
9132 {
9133 CFRuntimeBase base; // CF object base.
9134 dispatch_queue_t queue; // Serial work queue.
9135 DNSServiceRef mainRef; // Reference to the main shared DNS-SD connection.
9136 DNSServiceRef opRef; // Reference to the current DNSServiceGetAddrInfo operation.
9137 GAITestCase * caseList; // List of test cases.
9138 GAITestCase * currentCase; // Pointer to the current test case.
9139 GAITestItem * currentItem; // Pointer to the current test item.
9140 MicroTime64 caseStartTime; // Start time of current test case in Unix time as microseconds.
9141 MicroTime64 caseEndTime; // End time of current test case in Unix time as microseconds.
9142 Boolean started; // True if the tester has been successfully started.
9143 Boolean stopped; // True if the tester has been stopped.
9144 int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
9145 dispatch_source_t caseTimer; // Timer for enforcing a test case time limits.
9146 pcap_t * pcap; // Captures traffic between mDNSResponder and test DNS server.
9147 pid_t serverPID; // PID of the test DNS server.
9148 int serverDelayMs; // Additional time to have the server delay its responses by.
9149 int serverDefaultTTL; // Default TTL for the server's records.
9150 GAITesterEventHandler_f eventHandler; // User's event handler.
9151 void * eventContext; // User's event handler context.
9152 GAITesterResultsHandler_f resultsHandler; // User's results handler.
9153 void * resultsContext; // User's results handler context.
9154
9155 // Variables for current test item.
9156
9157 uint64_t bitmapV4; // Bitmap of IPv4 results that have yet to be received.
9158 uint64_t bitmapV6; // Bitmap of IPv6 results that have yet to be received.
9159 uint64_t startTicks; // Start ticks of DNSServiceGetAddrInfo().
9160 uint64_t connTicks; // Ticks when the connection was created.
9161 uint64_t firstTicks; // Ticks when the first DNSServiceGetAddrInfo result was received.
9162 uint64_t endTicks; // Ticks when the last DNSServiceGetAddrInfo result was received.
9163 Boolean gotFirstResult; // True if the first result has been received.
9164 };
9165
9166 CF_CLASS_DEFINE( GAITester );
9167
9168 static void _GAITesterRun( void *inContext );
9169 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap );
9170 static void _GAITesterTimeout( void *inContext );
9171 static void _GAITesterAdvanceCurrentItem( GAITesterRef inTester );
9172 static void _GAITesterAdvanceCurrentSet( GAITesterRef inTester );
9173 static void _GAITesterInitializeCurrentTest( GAITesterRef inTester );
9174 static void DNSSD_API
9175 _GAITesterGetAddrInfoCallback(
9176 DNSServiceRef inSDRef,
9177 DNSServiceFlags inFlags,
9178 uint32_t inInterfaceIndex,
9179 DNSServiceErrorType inError,
9180 const char * inHostname,
9181 const struct sockaddr * inSockAddr,
9182 uint32_t inTTL,
9183 void * inContext );
9184 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester, Boolean inTimedOut );
9185
9186 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
9187
9188 static OSStatus
9189 GAITestItemCreate(
9190 const char * inName,
9191 unsigned int inAddressCount,
9192 GAITestAddrType inHasAddrs,
9193 GAITestAddrType inWantAddrs,
9194 GAITestItem ** outItem );
9195 static OSStatus GAITestItemDuplicate( const GAITestItem *inItem, GAITestItem **outItem );
9196 static void GAITestItemFree( GAITestItem *inItem );
9197
9198 static OSStatus
9199 GAITesterCreate(
9200 dispatch_queue_t inQueue,
9201 int inCallDelayMs,
9202 int inServerDelayMs,
9203 int inServerDefaultTTL,
9204 GAITesterRef * outTester )
9205 {
9206 OSStatus err;
9207 GAITesterRef obj = NULL;
9208
9209 CF_OBJECT_CREATE( GAITester, obj, err, exit );
9210
9211 ReplaceDispatchQueue( &obj->queue, inQueue );
9212 obj->callDelayMs = inCallDelayMs;
9213 obj->serverPID = -1;
9214 obj->serverDelayMs = inServerDelayMs;
9215 obj->serverDefaultTTL = inServerDefaultTTL;
9216
9217 *outTester = obj;
9218 obj = NULL;
9219 err = kNoErr;
9220
9221 exit:
9222 CFReleaseNullSafe( obj );
9223 return( err );
9224 }
9225
9226 //===========================================================================================================================
9227 // _GAITesterFinalize
9228 //===========================================================================================================================
9229
9230 static void _GAITesterFinalize( CFTypeRef inObj )
9231 {
9232 GAITesterRef const me = (GAITesterRef) inObj;
9233 GAITestCase * testCase;
9234
9235 check( !me->opRef );
9236 check( !me->mainRef );
9237 check( !me->caseTimer );
9238 dispatch_forget( &me->queue );
9239 while( ( testCase = me->caseList ) != NULL )
9240 {
9241 me->caseList = testCase->next;
9242 GAITestCaseFree( testCase );
9243 }
9244 }
9245
9246 //===========================================================================================================================
9247 // GAITesterStart
9248 //===========================================================================================================================
9249
9250 static void _GAITesterStart( void *inContext );
9251 static void _GAITesterStop( GAITesterRef me );
9252
9253 static void GAITesterStart( GAITesterRef me )
9254 {
9255 CFRetain( me );
9256 dispatch_async_f( me->queue, me, _GAITesterStart );
9257 }
9258
9259 extern char ** environ;
9260
9261 static void _GAITesterStart( void *inContext )
9262 {
9263 OSStatus err;
9264 GAITesterRef const me = (GAITesterRef) inContext;
9265 char * argv[ 4 ];
9266 char * ptr;
9267 char * end;
9268 char command[ 128 ];
9269
9270 ptr = &command[ 0 ];
9271 end = &command[ countof( command ) ];
9272 SNPrintF_Add( &ptr, end, "dnssdutil server --loopback --followPID %lld", (int64_t) getpid() );
9273 if( me->serverDefaultTTL >= 0 ) SNPrintF_Add( &ptr, end, " --defaultTTL %d", me->serverDefaultTTL );
9274 if( me->serverDelayMs >= 0 ) SNPrintF_Add( &ptr, end, " --responseDelay %d", me->serverDelayMs );
9275
9276 argv[ 0 ] = "/bin/sh";
9277 argv[ 1 ] = "-c";
9278 argv[ 2 ] = command;
9279 argv[ 3 ] = NULL;
9280 err = posix_spawn( &me->serverPID, argv[ 0 ], NULL, NULL, argv, environ );
9281 require_noerr( err, exit );
9282
9283 me->currentCase = me->caseList;
9284 me->currentItem = me->currentCase ? me->currentCase->itemList : NULL;
9285 _GAITesterInitializeCurrentTest( me );
9286
9287 // Hack: The first tester run is delayed for three seconds to allow the test DNS server to start up.
9288 // A better way to handle this is to issue an asynchronous query for something in the d.test. domain. As soon as an
9289 // expected response is received, the server can be considered to be up and running.
9290
9291 CFRetain( me );
9292 dispatch_after_f( dispatch_time_seconds( 3 ), me->queue, me, _GAITesterRun );
9293
9294 CFRetain( me );
9295 me->started = true;
9296 if( me->eventHandler ) me->eventHandler( kGAITesterEvent_Started, me->eventContext );
9297
9298 exit:
9299 if( err ) _GAITesterStop( me );
9300 CFRelease( me );
9301 }
9302
9303 //===========================================================================================================================
9304 // GAITesterStop
9305 //===========================================================================================================================
9306
9307 static void _GAITesterUserStop( void *inContext );
9308
9309 static void GAITesterStop( GAITesterRef me )
9310 {
9311 CFRetain( me );
9312 dispatch_async_f( me->queue, me, _GAITesterUserStop );
9313 }
9314
9315 static void _GAITesterUserStop( void *inContext )
9316 {
9317 GAITesterRef const me = (GAITesterRef) inContext;
9318
9319 _GAITesterStop( me );
9320 CFRelease( me );
9321 }
9322
9323 static void _GAITesterStop( GAITesterRef me )
9324 {
9325 OSStatus err;
9326
9327 DNSServiceForget( &me->opRef );
9328 DNSServiceForget( &me->mainRef );
9329 ForgetPacketCapture( &me->pcap );
9330 dispatch_source_forget( &me->caseTimer );
9331 if( me->serverPID != -1 )
9332 {
9333 err = kill( me->serverPID, SIGTERM );
9334 err = map_global_noerr_errno( err );
9335 check_noerr( err );
9336 }
9337
9338 if( !me->stopped )
9339 {
9340 me->stopped = true;
9341 if( me->eventHandler ) me->eventHandler( kGAITesterEvent_Stopped, me->eventContext );
9342 if( me->started ) CFRelease( me );
9343 }
9344 }
9345
9346 //===========================================================================================================================
9347 // GAITesterAddCase
9348 //===========================================================================================================================
9349
9350 static void GAITesterAddCase( GAITesterRef me, GAITestCase *inCase )
9351 {
9352 GAITestCase ** ptr;
9353
9354 for( ptr = &me->caseList; *ptr != NULL; ptr = &( *ptr )->next ) {}
9355 *ptr = inCase;
9356 }
9357
9358 //===========================================================================================================================
9359 // GAITesterSetEventHandler
9360 //===========================================================================================================================
9361
9362 static void GAITesterSetEventHandler( GAITesterRef me, GAITesterEventHandler_f inEventHandler, void *inEventContext )
9363 {
9364 me->eventHandler = inEventHandler;
9365 me->eventContext = inEventContext;
9366 }
9367
9368 //===========================================================================================================================
9369 // GAITesterSetResultsHandler
9370 //===========================================================================================================================
9371
9372 static void GAITesterSetResultsHandler( GAITesterRef me, GAITesterResultsHandler_f inResultsHandler, void *inResultsContext )
9373 {
9374 me->resultsHandler = inResultsHandler;
9375 me->resultsContext = inResultsContext;
9376 }
9377
9378 //===========================================================================================================================
9379 // _GAITesterRun
9380 //===========================================================================================================================
9381
9382 static void _GAITesterRun( void *inContext )
9383 {
9384 OSStatus err;
9385 GAITesterRef const me = (GAITesterRef) inContext;
9386 GAITestItem * item;
9387 GAITestItemResult * results = NULL;
9388
9389 require_action_quiet( !me->stopped, exit, err = kNoErr );
9390
9391 for( ;; )
9392 {
9393 item = me->currentItem;
9394 if( item )
9395 {
9396 DNSServiceProtocol protocols;
9397
9398 check( !me->opRef );
9399 check( ( me->bitmapV4 != 0 ) || ( me->bitmapV6 != 0 ) );
9400
9401 // Perform preliminary tasks if this is the start of a new test case.
9402
9403 if( item == me->currentCase->itemList )
9404 {
9405 // Flush mDNSResponder's cache.
9406
9407 err = systemf( NULL, "killall -HUP mDNSResponder" );
9408 require_noerr( err, exit );
9409 usleep( kMicrosecondsPerSecond );
9410
9411 // Start a packet capture.
9412
9413 check( !me->pcap );
9414 err = _GAITesterCreatePacketCapture( &me->pcap );
9415 require_noerr( err, exit );
9416
9417 // Start the test case time limit timer.
9418
9419 check( !me->caseTimer );
9420 if( me->currentCase->timeLimitMs > 0 )
9421 {
9422 const int64_t timeLimitSecs = ( me->currentCase->timeLimitMs + 999 ) / 1000;
9423
9424 err = DispatchTimerCreate( dispatch_time_seconds( timeLimitSecs ), DISPATCH_TIME_FOREVER,
9425 ( (uint64_t) timeLimitSecs ) * kNanosecondsPerSecond / 10,
9426 me->queue, _GAITesterTimeout, NULL, me, &me->caseTimer );
9427 require_noerr( err, exit );
9428 dispatch_resume( me->caseTimer );
9429 }
9430
9431 me->caseStartTime = GetCurrentMicroTime();
9432 }
9433
9434 // Call DNSServiceGetAddrInfo().
9435
9436 if( me->callDelayMs > 0 ) usleep( ( (useconds_t) me->callDelayMs ) * kMicrosecondsPerMillisecond );
9437
9438 protocols = 0;
9439 if( item->wantV4 ) protocols |= kDNSServiceProtocol_IPv4;
9440 if( item->wantV6 ) protocols |= kDNSServiceProtocol_IPv6;
9441
9442 check( !me->mainRef );
9443 me->startTicks = UpTicks();
9444
9445 err = DNSServiceCreateConnection( &me->mainRef );
9446 require_noerr( err, exit );
9447
9448 err = DNSServiceSetDispatchQueue( me->mainRef, me->queue );
9449 require_noerr( err, exit );
9450
9451 me->connTicks = UpTicks();
9452
9453 me->opRef = me->mainRef;
9454 err = DNSServiceGetAddrInfo( &me->opRef, kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates,
9455 kDNSServiceInterfaceIndexAny, protocols, item->name, _GAITesterGetAddrInfoCallback, me );
9456 require_noerr( err, exit );
9457 break;
9458 }
9459 else
9460 {
9461 // No more test items means that this test case has completed (or timed out).
9462
9463 me->caseEndTime = GetCurrentMicroTime();
9464 dispatch_source_forget( &me->caseTimer );
9465 ForgetPacketCapture( &me->pcap );
9466
9467 if( me->resultsHandler )
9468 {
9469 size_t resultCount, itemCount, i;
9470 int timedOut;
9471
9472 itemCount = 0;
9473 resultCount = 0;
9474 timedOut = false;
9475 for( item = me->currentCase->itemList; item; item = item->next )
9476 {
9477 if( !timedOut )
9478 {
9479 if( item->timeUs < 0 )
9480 {
9481 timedOut = true;
9482 }
9483 else
9484 {
9485 ++resultCount;
9486 }
9487 }
9488 ++itemCount;
9489 }
9490 if( resultCount > 0 )
9491 {
9492 results = (GAITestItemResult *) calloc( resultCount, sizeof( *results ) );
9493 require_action( results, exit, err = kNoMemoryErr );
9494
9495 item = me->currentCase->itemList;
9496 for( i = 0; i < resultCount; ++i )
9497 {
9498 results[ i ].name = item->name;
9499 results[ i ].connectionTimeUs = item->connectionTimeUs;
9500 results[ i ].firstTimeUs = item->firstTimeUs;
9501 results[ i ].timeUs = item->timeUs;
9502 item = item->next;
9503 }
9504 }
9505 me->resultsHandler( me->currentCase->title, me->caseStartTime, me->caseEndTime, results, resultCount,
9506 itemCount, me->resultsContext );
9507 ForgetMem( &results );
9508 }
9509
9510 _GAITesterAdvanceCurrentSet( me );
9511 require_action_quiet( me->currentCase, exit, err = kEndingErr );
9512 }
9513 }
9514
9515 exit:
9516 FreeNullSafe( results );
9517 if( err ) _GAITesterStop( me );
9518 CFRelease( me );
9519 }
9520
9521 //===========================================================================================================================
9522 // _GAITesterCreatePacketCapture
9523 //===========================================================================================================================
9524
9525 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap )
9526 {
9527 OSStatus err;
9528 pcap_t * pcap;
9529 struct bpf_program program;
9530 char errBuf[ PCAP_ERRBUF_SIZE ];
9531
9532 pcap = pcap_create( "lo0", errBuf );
9533 require_action_string( pcap, exit, err = kUnknownErr, errBuf );
9534
9535 err = pcap_set_buffer_size( pcap, 512 * kBytesPerKiloByte );
9536 require_noerr_action( err, exit, err = kUnknownErr );
9537
9538 err = pcap_set_snaplen( pcap, 512 );
9539 require_noerr_action( err, exit, err = kUnknownErr );
9540
9541 err = pcap_set_immediate_mode( pcap, 0 );
9542 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
9543
9544 err = pcap_activate( pcap );
9545 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
9546
9547 err = pcap_setdirection( pcap, PCAP_D_INOUT );
9548 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
9549
9550 err = pcap_setnonblock( pcap, 1, errBuf );
9551 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
9552
9553 err = pcap_compile( pcap, &program, "udp port 53", 1, PCAP_NETMASK_UNKNOWN );
9554 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
9555
9556 err = pcap_setfilter( pcap, &program );
9557 pcap_freecode( &program );
9558 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
9559
9560 *outPCap = pcap;
9561 pcap = NULL;
9562
9563 exit:
9564 if( pcap ) pcap_close( pcap );
9565 return( err );
9566 }
9567
9568 //===========================================================================================================================
9569 // _GAITesterTimeout
9570 //===========================================================================================================================
9571
9572 static void _GAITesterTimeout( void *inContext )
9573 {
9574 GAITesterRef const me = (GAITesterRef) inContext;
9575
9576 dispatch_source_forget( &me->caseTimer );
9577
9578 _GAITesterCompleteCurrentTest( me, true );
9579 }
9580
9581 //===========================================================================================================================
9582 // _GAITesterAdvanceCurrentItem
9583 //===========================================================================================================================
9584
9585 static void _GAITesterAdvanceCurrentItem( GAITesterRef me )
9586 {
9587 if( me->currentItem )
9588 {
9589 me->currentItem = me->currentItem->next;
9590 _GAITesterInitializeCurrentTest( me );
9591 }
9592 }
9593
9594 //===========================================================================================================================
9595 // _GAITesterAdvanceCurrentSet
9596 //===========================================================================================================================
9597
9598 static void _GAITesterAdvanceCurrentSet( GAITesterRef me )
9599 {
9600 if( me->currentCase )
9601 {
9602 me->caseStartTime = 0;
9603 me->caseEndTime = 0;
9604 me->currentCase = me->currentCase->next;
9605 if( me->currentCase )
9606 {
9607 me->currentItem = me->currentCase->itemList;
9608 _GAITesterInitializeCurrentTest( me );
9609 }
9610 }
9611 }
9612
9613 //===========================================================================================================================
9614 // _GAITesterInitializeCurrentTest
9615 //===========================================================================================================================
9616
9617 static void _GAITesterInitializeCurrentTest( GAITesterRef me )
9618 {
9619 GAITestItem * const item = me->currentItem;
9620
9621 if( item )
9622 {
9623 check( item->addressCount > 0 );
9624 if( item->wantV4 )
9625 {
9626 me->bitmapV4 = item->hasV4 ? ( ( UINT64_C( 1 ) << item->addressCount ) - 1 ) : 1;
9627 }
9628 else
9629 {
9630 me->bitmapV4 = 0;
9631 }
9632
9633 if( item->wantV6 )
9634 {
9635 me->bitmapV6 = item->hasV6 ? ( ( UINT64_C( 1 ) << item->addressCount ) - 1 ) : 1;
9636 }
9637 else
9638 {
9639 me->bitmapV6 = 0;
9640 }
9641 me->gotFirstResult = false;
9642 }
9643 }
9644
9645 //===========================================================================================================================
9646 // _GAITesterGetAddrInfoCallback
9647 //===========================================================================================================================
9648
9649 static void DNSSD_API
9650 _GAITesterGetAddrInfoCallback(
9651 DNSServiceRef inSDRef,
9652 DNSServiceFlags inFlags,
9653 uint32_t inInterfaceIndex,
9654 DNSServiceErrorType inError,
9655 const char * inHostname,
9656 const struct sockaddr * inSockAddr,
9657 uint32_t inTTL,
9658 void * inContext )
9659 {
9660 GAITesterRef const me = (GAITesterRef) inContext;
9661 GAITestItem * const item = me->currentItem;
9662 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
9663 uint64_t nowTicks;
9664 uint64_t * bitmapPtr;
9665 uint64_t bitmask;
9666 unsigned int addrOffset;
9667
9668 Unused( inSDRef );
9669 Unused( inInterfaceIndex );
9670 Unused( inHostname );
9671 Unused( inTTL );
9672
9673 nowTicks = UpTicks();
9674
9675 require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
9676 require_quiet( !inError || ( inError == kDNSServiceErr_NoSuchRecord ), exit );
9677
9678 bitmapPtr = NULL;
9679 bitmask = 0;
9680 if( ( sip->sa.sa_family == AF_INET ) && item->wantV4 )
9681 {
9682 if( item->hasV4 )
9683 {
9684 if( !inError )
9685 {
9686 const uint32_t addrV4 = ntohl( sip->v4.sin_addr.s_addr );
9687
9688 if( strcasecmp( item->name, "localhost." ) == 0 )
9689 {
9690 if( addrV4 == INADDR_LOOPBACK )
9691 {
9692 bitmask = 1;
9693 bitmapPtr = &me->bitmapV4;
9694 }
9695 }
9696 else
9697 {
9698 addrOffset = addrV4 - kTestDNSServerBaseAddrV4;
9699 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
9700 {
9701 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
9702 bitmapPtr = &me->bitmapV4;
9703 }
9704 }
9705 }
9706 }
9707 else if( inError == kDNSServiceErr_NoSuchRecord )
9708 {
9709 bitmask = 1;
9710 bitmapPtr = &me->bitmapV4;
9711 }
9712 }
9713 else if( ( sip->sa.sa_family == AF_INET6 ) && item->wantV6 )
9714 {
9715 if( item->hasV6 )
9716 {
9717 if( !inError )
9718 {
9719 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
9720
9721 if( strcasecmp( item->name, "localhost." ) == 0 )
9722 {
9723 if( memcmp( addrV6, in6addr_loopback.s6_addr, 16 ) == 0 )
9724 {
9725 bitmask = 1;
9726 bitmapPtr = &me->bitmapV6;
9727 }
9728 }
9729 else if( memcmp( addrV6, kTestDNSServerBaseAddrV6, 15 ) == 0 )
9730 {
9731 addrOffset = addrV6[ 15 ];
9732 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
9733 {
9734 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
9735 bitmapPtr = &me->bitmapV6;
9736 }
9737 }
9738 }
9739 }
9740 else if( inError == kDNSServiceErr_NoSuchRecord )
9741 {
9742 bitmask = 1;
9743 bitmapPtr = &me->bitmapV6;
9744 }
9745 }
9746
9747 if( bitmapPtr && ( *bitmapPtr & bitmask ) )
9748 {
9749 *bitmapPtr &= ~bitmask;
9750 if( !me->gotFirstResult )
9751 {
9752 me->firstTicks = nowTicks;
9753 me->gotFirstResult = true;
9754 }
9755
9756 if( ( me->bitmapV4 == 0 ) && ( me->bitmapV6 == 0 ) )
9757 {
9758 me->endTicks = nowTicks;
9759 _GAITesterCompleteCurrentTest( me, false );
9760 }
9761 }
9762
9763 exit:
9764 return;
9765 }
9766
9767 //===========================================================================================================================
9768 // _GAITesterCompleteCurrentTest
9769 //===========================================================================================================================
9770
9771 static OSStatus
9772 _GAITesterGetDNSMessageFromPacket(
9773 const uint8_t * inPacketPtr,
9774 size_t inPacketLen,
9775 const uint8_t ** outMsgPtr,
9776 size_t * outMsgLen );
9777
9778 static void _GAITesterCompleteCurrentTest( GAITesterRef me, Boolean inTimedOut )
9779 {
9780 OSStatus err;
9781 GAITestItem * item;
9782 struct timeval * tsQA = NULL;
9783 struct timeval * tsQAAAA = NULL;
9784 struct timeval * tsRA = NULL;
9785 struct timeval * tsRAAAA = NULL;
9786 struct timeval timeStamps[ 4 ];
9787 struct timeval * tsPtr = &timeStamps[ 0 ];
9788 struct timeval * tsQ;
9789 struct timeval * tsR;
9790 int64_t idleTimeUs;
9791 uint8_t name[ kDomainNameLengthMax ];
9792
9793 DNSServiceForget( &me->opRef );
9794 DNSServiceForget( &me->mainRef );
9795
9796 if( inTimedOut )
9797 {
9798 for( item = me->currentItem; item; item = item->next )
9799 {
9800 item->firstTimeUs = -1;
9801 item->timeUs = -1;
9802 }
9803 me->currentItem = NULL;
9804
9805 CFRetain( me );
9806 dispatch_async_f( me->queue, me, _GAITesterRun );
9807 return;
9808 }
9809
9810 item = me->currentItem;
9811 err = DomainNameFromString( name, item->name, NULL );
9812 require_noerr( err, exit );
9813
9814 for( ;; )
9815 {
9816 int status;
9817 struct pcap_pkthdr * pktHdr;
9818 const uint8_t * packet;
9819 const uint8_t * msgPtr;
9820 size_t msgLen;
9821 const DNSHeader * hdr;
9822 unsigned int flags;
9823 const uint8_t * ptr;
9824 const DNSQuestionFixedFields * qfields;
9825 unsigned int qtype;
9826 uint8_t qname[ kDomainNameLengthMax ];
9827
9828 status = pcap_next_ex( me->pcap, &pktHdr, &packet );
9829 if( status != 1 ) break;
9830 if( _GAITesterGetDNSMessageFromPacket( packet, pktHdr->caplen, &msgPtr, &msgLen ) != kNoErr ) continue;
9831 if( msgLen < kDNSHeaderLength ) continue;
9832
9833 hdr = (const DNSHeader *) msgPtr;
9834 flags = DNSHeaderGetFlags( hdr );
9835 if( DNSFlagsGetOpCode( flags ) != kDNSOpCode_Query ) continue;
9836 if( DNSHeaderGetQuestionCount( hdr ) < 1 ) continue;
9837
9838 ptr = (const uint8_t *) &hdr[ 1 ];
9839 if( DNSMessageExtractDomainName( msgPtr, msgLen, ptr, qname, &ptr ) != kNoErr ) continue;
9840 if( !DomainNameEqual( qname, name ) ) continue;
9841
9842 qfields = (const DNSQuestionFixedFields *) ptr;
9843 if( DNSQuestionFixedFieldsGetClass( qfields ) != kDNSServiceClass_IN ) continue;
9844
9845 qtype = DNSQuestionFixedFieldsGetType( qfields );
9846 if( item->wantV4 && ( qtype == kDNSServiceType_A ) )
9847 {
9848 if( flags & kDNSHeaderFlag_Response )
9849 {
9850 if( tsQA && !tsRA )
9851 {
9852 tsRA = tsPtr++;
9853 *tsRA = pktHdr->ts;
9854 }
9855 }
9856 else if( !tsQA )
9857 {
9858 tsQA = tsPtr++;
9859 *tsQA = pktHdr->ts;
9860 }
9861 }
9862 else if( item->wantV6 && ( qtype == kDNSServiceType_AAAA ) )
9863 {
9864 if( flags & kDNSHeaderFlag_Response )
9865 {
9866 if( tsQAAAA && !tsRAAAA )
9867 {
9868 tsRAAAA = tsPtr++;
9869 *tsRAAAA = pktHdr->ts;
9870 }
9871 }
9872 else if( !tsQAAAA )
9873 {
9874 tsQAAAA = tsPtr++;
9875 *tsQAAAA = pktHdr->ts;
9876 }
9877 }
9878 }
9879
9880 if( tsQA && tsQAAAA ) tsQ = TIMEVAL_GT( *tsQA, *tsQAAAA ) ? tsQA : tsQAAAA;
9881 else tsQ = tsQA ? tsQA : tsQAAAA;
9882
9883 if( tsRA && tsRAAAA ) tsR = TIMEVAL_LT( *tsRA, *tsRAAAA ) ? tsRA : tsRAAAA;
9884 else tsR = tsQA ? tsQA : tsQAAAA;
9885
9886 if( tsQ && tsR )
9887 {
9888 idleTimeUs = TIMEVAL_USEC64_DIFF( *tsR, *tsQ );
9889 if( idleTimeUs < 0 ) idleTimeUs = 0;
9890 }
9891 else
9892 {
9893 idleTimeUs = 0;
9894 }
9895
9896 item->connectionTimeUs = (int64_t) UpTicksToMicroseconds( me->connTicks - me->startTicks );
9897 item->firstTimeUs = (int64_t)( UpTicksToMicroseconds( me->firstTicks - me->connTicks ) - (uint64_t) idleTimeUs );
9898 item->timeUs = (int64_t)( UpTicksToMicroseconds( me->endTicks - me->connTicks ) - (uint64_t) idleTimeUs );
9899
9900 _GAITesterAdvanceCurrentItem( me );
9901 CFRetain( me );
9902 dispatch_async_f( me->queue, me, _GAITesterRun );
9903
9904 exit:
9905 if( err ) _GAITesterStop( me );
9906 }
9907
9908 //===========================================================================================================================
9909 // _GAITesterGetDNSMessageFromPacket
9910 //===========================================================================================================================
9911
9912 #define kHeaderSizeNullLink 4
9913 #define kHeaderSizeIPv4Min 20
9914 #define kHeaderSizeIPv6 40
9915 #define kHeaderSizeUDP 8
9916
9917 #define kIPProtocolUDP 0x11
9918
9919 static OSStatus
9920 _GAITesterGetDNSMessageFromPacket(
9921 const uint8_t * inPacketPtr,
9922 size_t inPacketLen,
9923 const uint8_t ** outMsgPtr,
9924 size_t * outMsgLen )
9925 {
9926 OSStatus err;
9927 const uint8_t * nullLink;
9928 uint32_t addressFamily;
9929 const uint8_t * ip;
9930 int ipHeaderLen;
9931 int protocol;
9932 const uint8_t * msg;
9933 const uint8_t * const end = &inPacketPtr[ inPacketLen ];
9934
9935 nullLink = &inPacketPtr[ 0 ];
9936 require_action_quiet( ( end - nullLink ) >= kHeaderSizeNullLink, exit, err = kUnderrunErr );
9937 addressFamily = ReadHost32( &nullLink[ 0 ] );
9938
9939 ip = &nullLink[ kHeaderSizeNullLink ];
9940 if( addressFamily == AF_INET )
9941 {
9942 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv4Min, exit, err = kUnderrunErr );
9943 ipHeaderLen = ( ip[ 0 ] & 0x0F ) * 4;
9944 protocol = ip[ 9 ];
9945 }
9946 else if( addressFamily == AF_INET6 )
9947 {
9948 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv6, exit, err = kUnderrunErr );
9949 ipHeaderLen = kHeaderSizeIPv6;
9950 protocol = ip[ 6 ];
9951 }
9952 else
9953 {
9954 err = kTypeErr;
9955 goto exit;
9956 }
9957 require_action_quiet( protocol == kIPProtocolUDP, exit, err = kTypeErr );
9958 require_action_quiet( ( end - ip ) >= ( ipHeaderLen + kHeaderSizeUDP ), exit, err = kUnderrunErr );
9959
9960 msg = &ip[ ipHeaderLen + kHeaderSizeUDP ];
9961
9962 *outMsgPtr = msg;
9963 *outMsgLen = (size_t)( end - msg );
9964 err = kNoErr;
9965
9966 exit:
9967 return( err );
9968 }
9969
9970 //===========================================================================================================================
9971 // GAITestCaseCreate
9972 //===========================================================================================================================
9973
9974 static OSStatus GAITestCaseCreate( const char *inTitle, unsigned int inTimeLimitMs, GAITestCase **outSet )
9975 {
9976 OSStatus err;
9977 GAITestCase * obj;
9978
9979 obj = (GAITestCase *) calloc( 1, sizeof( *obj ) );
9980 require_action( obj, exit, err = kNoMemoryErr );
9981
9982 obj->title = strdup( inTitle );
9983 require_action( obj->title, exit, err = kNoMemoryErr );
9984
9985 obj->timeLimitMs = inTimeLimitMs;
9986
9987 *outSet = obj;
9988 obj = NULL;
9989 err = kNoErr;
9990
9991 exit:
9992 if( obj ) GAITestCaseFree( obj );
9993 return( err );
9994 }
9995
9996 //===========================================================================================================================
9997 // GAITestCaseFree
9998 //===========================================================================================================================
9999
10000 static void GAITestCaseFree( GAITestCase *inCase )
10001 {
10002 GAITestItem * item;
10003
10004 while( ( item = inCase->itemList ) != NULL )
10005 {
10006 inCase->itemList = item->next;
10007 GAITestItemFree( item );
10008 }
10009 ForgetMem( &inCase->title );
10010 free( inCase );
10011 }
10012
10013 //===========================================================================================================================
10014 // GAITestCaseAddItem
10015 //===========================================================================================================================
10016
10017 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
10018 // possible strings to use in the Tag label.
10019
10020 #define kUniqueStringCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
10021 #define kUniqueStringCharSetLen sizeof_string( kUniqueStringCharSet )
10022 #define kUniqueStringLen 6
10023
10024 static OSStatus
10025 GAITestCaseAddItem(
10026 GAITestCase * inCase,
10027 unsigned int inAliasCount,
10028 unsigned int inAddressCount,
10029 int inTTL,
10030 GAITestAddrType inHasAddrs,
10031 GAITestAddrType inWantAddrs,
10032 unsigned int inItemCount )
10033 {
10034 OSStatus err;
10035 GAITestItem * item;
10036 GAITestItem * item2;
10037 GAITestItem * newItemList = NULL;
10038 GAITestItem ** itemPtr;
10039 char * ptr;
10040 char * end;
10041 unsigned int i;
10042 char name[ 64 ];
10043 char uniqueStr[ kUniqueStringLen + 1 ];
10044
10045 require_action_quiet( inItemCount > 0, exit, err = kNoErr );
10046
10047 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
10048
10049 require_action_quiet( ( inAddressCount >= 1 ) && ( inAddressCount <= 64 ), exit, err = kCountErr );
10050 require_action_quiet( ( inAliasCount >= 0 ) && ( inAliasCount <= INT32_MAX ), exit, err = kCountErr );
10051 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
10052
10053 ptr = &name[ 0 ];
10054 end = &name[ countof( name ) ];
10055
10056 // Add Alias label.
10057
10058 if( inAliasCount == 1 ) SNPrintF_Add( &ptr, end, "alias." );
10059 else if( inAliasCount >= 2 ) SNPrintF_Add( &ptr, end, "alias-%u.", inAliasCount );
10060
10061 // Add Count label.
10062
10063 SNPrintF_Add( &ptr, end, "count-%u.", inAddressCount );
10064
10065 // Add TTL label.
10066
10067 if( inTTL >= 0 ) SNPrintF_Add( &ptr, end, "ttl-%d.", inTTL );
10068
10069 // Add Tag label.
10070
10071 RandomString( kUniqueStringCharSet, kUniqueStringCharSetLen, kUniqueStringLen, kUniqueStringLen, uniqueStr );
10072 SNPrintF_Add( &ptr, end, "tag-%s.", uniqueStr );
10073
10074 // Add IPv4 or IPv6 label if necessary.
10075
10076 switch( inHasAddrs )
10077 {
10078 case kGAITestAddrType_IPv4:
10079 SNPrintF_Add( &ptr, end, "ipv4." );
10080 break;
10081
10082 case kGAITestAddrType_IPv6:
10083 SNPrintF_Add( &ptr, end, "ipv6." );
10084 break;
10085 }
10086
10087 // Add d.test. labels.
10088
10089 SNPrintF_Add( &ptr, end, "d.test." );
10090
10091 // Create item.
10092
10093 err = GAITestItemCreate( name, inAddressCount, inHasAddrs, inWantAddrs, &item );
10094 require_noerr( err, exit );
10095
10096 newItemList = item;
10097 itemPtr = &item->next;
10098
10099 // Create repeat items.
10100
10101 for( i = 1; i < inItemCount; ++i )
10102 {
10103 err = GAITestItemDuplicate( item, &item2 );
10104 require_noerr( err, exit );
10105
10106 *itemPtr = item2;
10107 itemPtr = &item2->next;
10108 }
10109
10110 // Append to test case's item list.
10111
10112 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
10113 *itemPtr = newItemList;
10114 newItemList = NULL;
10115
10116 exit:
10117 while( ( item = newItemList ) != NULL )
10118 {
10119 newItemList = item->next;
10120 GAITestItemFree( item );
10121 }
10122 return( err );
10123 }
10124
10125 //===========================================================================================================================
10126 // GAITestCaseAddLocalHostItem
10127 //===========================================================================================================================
10128
10129 static OSStatus GAITestCaseAddLocalHostItem( GAITestCase *inCase, GAITestAddrType inWantAddrs, unsigned int inItemCount )
10130 {
10131 OSStatus err;
10132 GAITestItem * item;
10133 GAITestItem * item2;
10134 GAITestItem * newItemList = NULL;
10135 GAITestItem ** itemPtr;
10136 unsigned int i;
10137
10138 require_action_quiet( inItemCount > 1, exit, err = kNoErr );
10139
10140 err = GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both, inWantAddrs, &item );
10141 require_noerr( err, exit );
10142
10143 newItemList = item;
10144 itemPtr = &item->next;
10145
10146 // Create repeat items.
10147
10148 for( i = 1; i < inItemCount; ++i )
10149 {
10150 err = GAITestItemDuplicate( item, &item2 );
10151 require_noerr( err, exit );
10152
10153 *itemPtr = item2;
10154 itemPtr = &item2->next;
10155 }
10156
10157 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
10158 *itemPtr = newItemList;
10159 newItemList = NULL;
10160
10161 exit:
10162 while( ( item = newItemList ) != NULL )
10163 {
10164 newItemList = item->next;
10165 GAITestItemFree( item );
10166 }
10167 return( err );
10168 }
10169
10170 //===========================================================================================================================
10171 // GAITestItemCreate
10172 //===========================================================================================================================
10173
10174 static OSStatus
10175 GAITestItemCreate(
10176 const char * inName,
10177 unsigned int inAddressCount,
10178 GAITestAddrType inHasAddrs,
10179 GAITestAddrType inWantAddrs,
10180 GAITestItem ** outItem )
10181 {
10182 OSStatus err;
10183 GAITestItem * obj = NULL;
10184
10185 require_action_quiet( inAddressCount >= 1, exit, err = kCountErr );
10186 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
10187 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs ), exit, err = kValueErr );
10188
10189 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
10190 require_action( obj, exit, err = kNoMemoryErr );
10191
10192 obj->name = strdup( inName );
10193 require_action( obj->name, exit, err = kNoMemoryErr );
10194
10195 obj->addressCount = inAddressCount;
10196 obj->hasV4 = ( inHasAddrs & kGAITestAddrType_IPv4 ) ? true : false;
10197 obj->hasV6 = ( inHasAddrs & kGAITestAddrType_IPv6 ) ? true : false;
10198 obj->wantV4 = ( inWantAddrs & kGAITestAddrType_IPv4 ) ? true : false;
10199 obj->wantV6 = ( inWantAddrs & kGAITestAddrType_IPv6 ) ? true : false;
10200
10201 *outItem = obj;
10202 obj = NULL;
10203 err = kNoErr;
10204
10205 exit:
10206 if( obj ) GAITestItemFree( obj );
10207 return( err );
10208 }
10209
10210 //===========================================================================================================================
10211 // GAITestItemDuplicate
10212 //===========================================================================================================================
10213
10214 static OSStatus GAITestItemDuplicate( const GAITestItem *inItem, GAITestItem **outItem )
10215 {
10216 OSStatus err;
10217 GAITestItem * obj;
10218
10219 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
10220 require_action( obj, exit, err = kNoMemoryErr );
10221
10222 *obj = *inItem;
10223 obj->next = NULL;
10224 if( inItem->name )
10225 {
10226 obj->name = strdup( inItem->name );
10227 require_action( obj->name, exit, err = kNoMemoryErr );
10228 }
10229
10230 *outItem = obj;
10231 obj = NULL;
10232 err = kNoErr;
10233
10234 exit:
10235 if( obj ) GAITestItemFree( obj );
10236 return( err );
10237 }
10238
10239 //===========================================================================================================================
10240 // GAITestItemFree
10241 //===========================================================================================================================
10242
10243 static void GAITestItemFree( GAITestItem *inItem )
10244 {
10245 ForgetMem( &inItem->name );
10246 free( inItem );
10247 }
10248
10249 //===========================================================================================================================
10250 // SSDPDiscoverCmd
10251 //===========================================================================================================================
10252
10253 #define kSSDPPort 1900
10254
10255 typedef struct
10256 {
10257 HTTPHeader header; // HTTP header object for sending and receiving.
10258 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
10259 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
10260 int receiveSecs; // After send, the amount of time to spend receiving.
10261 uint32_t ifindex; // Index of the interface over which to send the query.
10262 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
10263 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
10264
10265 } SSDPDiscoverContext;
10266
10267 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
10268 static void SSDPDiscoverReadHandler( void *inContext );
10269 static int SocketToPortNumber( SocketRef inSock );
10270 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
10271
10272 static void SSDPDiscoverCmd( void )
10273 {
10274 OSStatus err;
10275 struct timeval now;
10276 SSDPDiscoverContext * context;
10277 dispatch_source_t signalSource = NULL;
10278 SocketRef sockV4 = kInvalidSocketRef;
10279 SocketRef sockV6 = kInvalidSocketRef;
10280 ssize_t n;
10281 int sendCount;
10282
10283 // Set up SIGINT handler.
10284
10285 signal( SIGINT, SIG_IGN );
10286 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
10287 require_noerr( err, exit );
10288 dispatch_resume( signalSource );
10289
10290 // Check command parameters.
10291
10292 if( gSSDPDiscover_ReceiveSecs < -1 )
10293 {
10294 FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
10295 err = kParamErr;
10296 goto exit;
10297 }
10298
10299 // Create context.
10300
10301 context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
10302 require_action( context, exit, err = kNoMemoryErr );
10303
10304 context->receiveSecs = gSSDPDiscover_ReceiveSecs;
10305 context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
10306 context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
10307
10308 err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
10309 require_noerr_quiet( err, exit );
10310
10311 // Set up IPv4 socket.
10312
10313 if( context->useIPv4 )
10314 {
10315 int port;
10316 err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
10317 require_noerr( err, exit );
10318
10319 err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
10320 require_noerr( err, exit );
10321
10322 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
10323 err = map_socket_noerr_errno( sockV4, err );
10324 require_noerr( err, exit );
10325 }
10326
10327 // Set up IPv6 socket.
10328
10329 if( context->useIPv6 )
10330 {
10331 err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
10332 require_noerr( err, exit );
10333
10334 err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
10335 require_noerr( err, exit );
10336
10337 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
10338 err = map_socket_noerr_errno( sockV6, err );
10339 require_noerr( err, exit );
10340 }
10341
10342 // Print prologue.
10343
10344 SSDPDiscoverPrintPrologue( context );
10345
10346 // Send mDNS query message.
10347
10348 sendCount = 0;
10349 if( IsValidSocket( sockV4 ) )
10350 {
10351 struct sockaddr_in mcastAddr4;
10352
10353 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
10354 SIN_LEN_SET( &mcastAddr4 );
10355 mcastAddr4.sin_family = AF_INET;
10356 mcastAddr4.sin_port = htons( kSSDPPort );
10357 mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250
10358
10359 err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
10360 require_noerr( err, exit );
10361
10362 n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
10363 (socklen_t) sizeof( mcastAddr4 ) );
10364 err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
10365 if( err )
10366 {
10367 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
10368 ForgetSocket( &sockV4 );
10369 }
10370 else
10371 {
10372 if( gSSDPDiscover_Verbose )
10373 {
10374 gettimeofday( &now, NULL );
10375 FPrintF( stdout, "---\n" );
10376 FPrintF( stdout, "Send time: %{du:time}\n", &now );
10377 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) );
10378 FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 );
10379 FPrintF( stdout, "Message size: %zu\n", context->header.len );
10380 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
10381 }
10382 ++sendCount;
10383 }
10384 }
10385
10386 if( IsValidSocket( sockV6 ) )
10387 {
10388 struct sockaddr_in6 mcastAddr6;
10389
10390 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
10391 SIN6_LEN_SET( &mcastAddr6 );
10392 mcastAddr6.sin6_family = AF_INET6;
10393 mcastAddr6.sin6_port = htons( kSSDPPort );
10394 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
10395 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
10396 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C;
10397
10398 err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
10399 require_noerr( err, exit );
10400
10401 n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
10402 (socklen_t) sizeof( mcastAddr6 ) );
10403 err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
10404 if( err )
10405 {
10406 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
10407 ForgetSocket( &sockV6 );
10408 }
10409 else
10410 {
10411 if( gSSDPDiscover_Verbose )
10412 {
10413 gettimeofday( &now, NULL );
10414 FPrintF( stdout, "---\n" );
10415 FPrintF( stdout, "Send time: %{du:time}\n", &now );
10416 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) );
10417 FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 );
10418 FPrintF( stdout, "Message size: %zu\n", context->header.len );
10419 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
10420 }
10421 ++sendCount;
10422 }
10423 }
10424 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
10425
10426 // If there's no wait period after the send, then exit.
10427
10428 if( context->receiveSecs == 0 ) goto exit;
10429
10430 // Create dispatch read sources for socket(s).
10431
10432 if( IsValidSocket( sockV4 ) )
10433 {
10434 SocketContext * sockCtx;
10435
10436 err = SocketContextCreate( sockV4, context, &sockCtx );
10437 require_noerr( err, exit );
10438 sockV4 = kInvalidSocketRef;
10439
10440 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
10441 &context->readSourceV4 );
10442 if( err ) ForgetSocketContext( &sockCtx );
10443 require_noerr( err, exit );
10444
10445 dispatch_resume( context->readSourceV4 );
10446 }
10447
10448 if( IsValidSocket( sockV6 ) )
10449 {
10450 SocketContext * sockCtx;
10451
10452 err = SocketContextCreate( sockV6, context, &sockCtx );
10453 require_noerr( err, exit );
10454 sockV6 = kInvalidSocketRef;
10455
10456 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
10457 &context->readSourceV6 );
10458 if( err ) ForgetSocketContext( &sockCtx );
10459 require_noerr( err, exit );
10460
10461 dispatch_resume( context->readSourceV6 );
10462 }
10463
10464 if( context->receiveSecs > 0 )
10465 {
10466 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
10467 Exit );
10468 }
10469 dispatch_main();
10470
10471 exit:
10472 ForgetSocket( &sockV4 );
10473 ForgetSocket( &sockV6 );
10474 dispatch_source_forget( &signalSource );
10475 if( err ) exit( 1 );
10476 }
10477
10478 static int SocketToPortNumber( SocketRef inSock )
10479 {
10480 OSStatus err;
10481 sockaddr_ip sip;
10482 socklen_t len;
10483
10484 len = (socklen_t) sizeof( sip );
10485 err = getsockname( inSock, &sip.sa, &len );
10486 err = map_socket_noerr_errno( inSock, err );
10487 check_noerr( err );
10488 return( err ? -1 : SockAddrGetPort( &sip ) );
10489 }
10490
10491 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST )
10492 {
10493 OSStatus err;
10494
10495 err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" );
10496 require_noerr( err, exit );
10497
10498 err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA );
10499 require_noerr( err, exit );
10500
10501 err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" );
10502 require_noerr( err, exit );
10503
10504 err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" );
10505 require_noerr( err, exit );
10506
10507 err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX );
10508 require_noerr( err, exit );
10509
10510 err = HTTPHeader_Commit( inHeader );
10511 require_noerr( err, exit );
10512
10513 exit:
10514 return( err );
10515 }
10516
10517 //===========================================================================================================================
10518 // SSDPDiscoverPrintPrologue
10519 //===========================================================================================================================
10520
10521 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext )
10522 {
10523 const int receiveSecs = inContext->receiveSecs;
10524 const char * ifName;
10525 char ifNameBuf[ IF_NAMESIZE + 1 ];
10526 NetTransportType ifType;
10527
10528 ifName = if_indextoname( inContext->ifindex, ifNameBuf );
10529
10530 ifType = kNetTransportType_Undefined;
10531 if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType );
10532
10533 FPrintF( stdout, "Interface: %s/%d/%s\n",
10534 ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) );
10535 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
10536 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
10537 FPrintF( stdout, "Receive duration: " );
10538 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
10539 else FPrintF( stdout, "∞\n" );
10540 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
10541 }
10542
10543 //===========================================================================================================================
10544 // SSDPDiscoverReadHandler
10545 //===========================================================================================================================
10546
10547 static void SSDPDiscoverReadHandler( void *inContext )
10548 {
10549 OSStatus err;
10550 struct timeval now;
10551 SocketContext * const sockCtx = (SocketContext *) inContext;
10552 SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockCtx->userContext;
10553 HTTPHeader * const header = &context->header;
10554 sockaddr_ip fromAddr;
10555 size_t msgLen;
10556
10557 gettimeofday( &now, NULL );
10558
10559 err = SocketRecvFrom( sockCtx->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ),
10560 NULL, NULL, NULL, NULL );
10561 require_noerr( err, exit );
10562
10563 FPrintF( stdout, "---\n" );
10564 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
10565 FPrintF( stdout, "Source: %##a\n", &fromAddr );
10566 FPrintF( stdout, "Message size: %zu\n", msgLen );
10567 header->len = msgLen;
10568 if( HTTPHeader_Validate( header ) )
10569 {
10570 FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len );
10571 if( header->extraDataLen > 0 )
10572 {
10573 FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX );
10574 }
10575 }
10576 else
10577 {
10578 FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX );
10579 goto exit;
10580 }
10581
10582 exit:
10583 if( err ) exit( 1 );
10584 }
10585
10586 //===========================================================================================================================
10587 // HTTPHeader_Validate
10588 //
10589 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
10590 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
10591 //
10592 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
10593 //===========================================================================================================================
10594
10595 Boolean HTTPHeader_Validate( HTTPHeader *inHeader )
10596 {
10597 const char * src;
10598 const char * end;
10599
10600 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
10601
10602 require( inHeader->len < sizeof( inHeader->buf ), exit );
10603 src = inHeader->buf;
10604 end = src + inHeader->len;
10605 if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) )
10606 {
10607 src += 4;
10608 }
10609 else
10610 {
10611 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
10612 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
10613
10614 for( ;; )
10615 {
10616 while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src;
10617 if( src >= end ) goto exit;
10618 ++src;
10619 if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
10620 {
10621 src += 2;
10622 break;
10623 }
10624 else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF
10625 {
10626 src += 1;
10627 break;
10628 }
10629 }
10630 }
10631 inHeader->extraDataPtr = src;
10632 inHeader->extraDataLen = (size_t)( end - src );
10633 inHeader->len = (size_t)( src - inHeader->buf );
10634 return( true );
10635
10636 exit:
10637 return( false );
10638 }
10639
10640 #if( TARGET_OS_DARWIN )
10641 //===========================================================================================================================
10642 // ResQueryCmd
10643 //===========================================================================================================================
10644
10645 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
10646
10647 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv );
10648 SOFT_LINK_FUNCTION_EX( resolv, res_9_query,
10649 int,
10650 ( const char *dname, int class, int type, u_char *answer, int anslen ),
10651 ( dname, class, type, answer, anslen ) );
10652
10653 // res_query() from libinfo
10654
10655 SOFT_LINK_LIBRARY_EX( "/usr/lib", info );
10656 SOFT_LINK_FUNCTION_EX( info, res_query,
10657 int,
10658 ( const char *dname, int class, int type, u_char *answer, int anslen ),
10659 ( dname, class, type, answer, anslen ) );
10660
10661 typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen );
10662
10663 static void ResQueryCmd( void )
10664 {
10665 OSStatus err;
10666 res_query_f res_query_ptr;
10667 int n;
10668 uint16_t type, class;
10669 uint8_t answer[ 1024 ];
10670
10671 // Get pointer to one of the res_query() functions.
10672
10673 if( gResQuery_UseLibInfo )
10674 {
10675 if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) )
10676 {
10677 FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" );
10678 err = kNotFoundErr;
10679 goto exit;
10680 }
10681 res_query_ptr = soft_res_query;
10682 }
10683 else
10684 {
10685 if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) )
10686 {
10687 FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" );
10688 err = kNotFoundErr;
10689 goto exit;
10690 }
10691 res_query_ptr = soft_res_9_query;
10692 }
10693
10694 // Get record type.
10695
10696 err = RecordTypeFromArgString( gResQuery_Type, &type );
10697 require_noerr( err, exit );
10698
10699 // Get record class.
10700
10701 if( gResQuery_Class )
10702 {
10703 err = RecordClassFromArgString( gResQuery_Class, &class );
10704 require_noerr( err, exit );
10705 }
10706 else
10707 {
10708 class = kDNSServiceClass_IN;
10709 }
10710
10711 // Print prologue.
10712
10713 FPrintF( stdout, "Name: %s\n", gResQuery_Name );
10714 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
10715 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
10716 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
10717 FPrintF( stdout, "---\n" );
10718
10719 // Call res_query().
10720
10721 n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) );
10722 if( n < 0 )
10723 {
10724 FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
10725 err = kUnknownErr;
10726 goto exit;
10727 }
10728
10729 // Print result.
10730
10731 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
10732
10733 exit:
10734 if( err ) exit( 1 );
10735 }
10736
10737 //===========================================================================================================================
10738 // ResolvDNSQueryCmd
10739 //===========================================================================================================================
10740
10741 // 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
10742 // avoid including the header file.
10743
10744 typedef void * dns_handle_t;
10745
10746 SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) );
10747 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) );
10748 SOFT_LINK_FUNCTION_EX( resolv, dns_query,
10749 int32_t, (
10750 dns_handle_t dns,
10751 const char * name,
10752 uint32_t dnsclass,
10753 uint32_t dnstype,
10754 char * buf,
10755 uint32_t len,
10756 struct sockaddr * from,
10757 uint32_t * fromlen ),
10758 ( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) );
10759
10760 static void ResolvDNSQueryCmd( void )
10761 {
10762 OSStatus err;
10763 int n;
10764 dns_handle_t dns = NULL;
10765 uint16_t type, class;
10766 sockaddr_ip from;
10767 uint32_t fromLen;
10768 uint8_t answer[ 1024 ];
10769
10770 // Make sure that the required symbols are available.
10771
10772 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) )
10773 {
10774 FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" );
10775 err = kNotFoundErr;
10776 goto exit;
10777 }
10778
10779 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) )
10780 {
10781 FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" );
10782 err = kNotFoundErr;
10783 goto exit;
10784 }
10785
10786 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) )
10787 {
10788 FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" );
10789 err = kNotFoundErr;
10790 goto exit;
10791 }
10792
10793 // Get record type.
10794
10795 err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type );
10796 require_noerr( err, exit );
10797
10798 // Get record class.
10799
10800 if( gResolvDNSQuery_Class )
10801 {
10802 err = RecordClassFromArgString( gResolvDNSQuery_Class, &class );
10803 require_noerr( err, exit );
10804 }
10805 else
10806 {
10807 class = kDNSServiceClass_IN;
10808 }
10809
10810 // Get dns handle.
10811
10812 dns = soft_dns_open( gResolvDNSQuery_Path );
10813 if( !dns )
10814 {
10815 FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path );
10816 err = kUnknownErr;
10817 goto exit;
10818 }
10819
10820 // Print prologue.
10821
10822 FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name );
10823 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
10824 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
10825 FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "<NULL>" );
10826 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
10827 FPrintF( stdout, "---\n" );
10828
10829 // Call dns_query().
10830
10831 memset( &from, 0, sizeof( from ) );
10832 fromLen = (uint32_t) sizeof( from );
10833 n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa,
10834 &fromLen );
10835 if( n < 0 )
10836 {
10837 FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
10838 err = kUnknownErr;
10839 goto exit;
10840 }
10841
10842 // Print result.
10843
10844 FPrintF( stdout, "From: %##a\n", &from );
10845 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
10846
10847 exit:
10848 if( dns ) soft_dns_free( dns );
10849 if( err ) exit( 1 );
10850 }
10851
10852 //===========================================================================================================================
10853 // CFHostCmd
10854 //===========================================================================================================================
10855
10856 static void
10857 _CFHostResolveCallback(
10858 CFHostRef inHost,
10859 CFHostInfoType inInfoType,
10860 const CFStreamError * inError,
10861 void * inInfo );
10862
10863 static void CFHostCmd( void )
10864 {
10865 OSStatus err;
10866 CFStringRef name;
10867 Boolean success;
10868 CFHostRef host = NULL;
10869 CFHostClientContext context;
10870 CFStreamError streamErr;
10871
10872 name = CFStringCreateWithCString( kCFAllocatorDefault, gCFHost_Name, kCFStringEncodingUTF8 );
10873 require_action( name, exit, err = kUnknownErr );
10874
10875 host = CFHostCreateWithName( kCFAllocatorDefault, name );
10876 ForgetCF( &name );
10877 require_action( host, exit, err = kUnknownErr );
10878
10879 memset( &context, 0, sizeof( context ) );
10880 success = CFHostSetClient( host, _CFHostResolveCallback, &context );
10881 require_action( success, exit, err = kUnknownErr );
10882
10883 CFHostScheduleWithRunLoop( host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
10884
10885 // Print prologue.
10886
10887 FPrintF( stdout, "Hostname: %s\n", gCFHost_Name );
10888 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
10889 FPrintF( stdout, "---\n" );
10890
10891 success = CFHostStartInfoResolution( host, kCFHostAddresses, &streamErr );
10892 require_action( success, exit, err = kUnknownErr );
10893 err = kNoErr;
10894
10895 CFRunLoopRun();
10896
10897 exit:
10898 CFReleaseNullSafe( host );
10899 if( err ) exit( 1 );
10900 }
10901
10902 static void _CFHostResolveCallback( CFHostRef inHost, CFHostInfoType inInfoType, const CFStreamError *inError, void *inInfo )
10903 {
10904 OSStatus err;
10905 struct timeval now;
10906
10907 gettimeofday( &now, NULL );
10908
10909 Unused( inInfoType );
10910 Unused( inInfo );
10911
10912 if( inError && ( inError->domain != 0 ) && ( inError->error ) )
10913 {
10914 err = inError->error;
10915 if( inError->domain == kCFStreamErrorDomainNetDB )
10916 {
10917 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
10918 }
10919 else
10920 {
10921 FPrintF( stderr, "Error %#m\n", err );
10922 }
10923 }
10924 else
10925 {
10926 CFArrayRef addresses;
10927 CFIndex count, i;
10928 CFDataRef addrData;
10929 const struct sockaddr * sockAddr;
10930 Boolean wasResolved = false;
10931
10932 addresses = CFHostGetAddressing( inHost, &wasResolved );
10933 check( wasResolved );
10934
10935 if( addresses )
10936 {
10937 count = CFArrayGetCount( addresses );
10938 for( i = 0; i < count; ++i )
10939 {
10940 addrData = CFArrayGetCFDataAtIndex( addresses, i, &err );
10941 require_noerr( err, exit );
10942
10943 sockAddr = (const struct sockaddr *) CFDataGetBytePtr( addrData );
10944 FPrintF( stdout, "%##a\n", sockAddr );
10945 }
10946 }
10947 err = kNoErr;
10948 }
10949
10950 FPrintF( stdout, "---\n" );
10951 FPrintF( stdout, "End time: %{du:time}\n", &now );
10952
10953 if( gCFHost_WaitSecs > 0 ) sleep( (unsigned int) gCFHost_WaitSecs );
10954
10955 exit:
10956 exit( err ? 1 : 0 );
10957 }
10958
10959 //===========================================================================================================================
10960 // DNSConfigAddCmd
10961 //
10962 // Note: Based on ajn's supplemental test tool.
10963 //===========================================================================================================================
10964
10965 static void DNSConfigAddCmd( void )
10966 {
10967 OSStatus err;
10968 CFMutableDictionaryRef dict = NULL;
10969 CFMutableArrayRef array = NULL;
10970 size_t i;
10971 SCDynamicStoreRef store = NULL;
10972 CFStringRef key = NULL;
10973 Boolean success;
10974
10975 if( geteuid() != 0 )
10976 {
10977 FPrintF( stderr, "error: This command must to be run as root.\n" );
10978 err = kIDErr;
10979 goto exit;
10980 }
10981
10982 // Create dictionary.
10983
10984 dict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
10985 require_action( dict, exit, err = kNoMemoryErr );
10986
10987 // Add DNS server IP addresses.
10988
10989 array = CFArrayCreateMutable( NULL, (CFIndex) gDNSConfigAdd_IPAddrCount, &kCFTypeArrayCallBacks );
10990 require_action( array, exit, err = kNoMemoryErr );
10991
10992 for( i = 0; i < gDNSConfigAdd_IPAddrCount; ++i )
10993 {
10994 CFStringRef addrStr;
10995
10996 addrStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_IPAddrArray[ i ], kCFStringEncodingUTF8 );
10997 require_action( addrStr, exit, err = kUnknownErr );
10998
10999 CFArrayAppendValue( array, addrStr );
11000 CFRelease( addrStr );
11001 }
11002
11003 CFDictionarySetValue( dict, kSCPropNetDNSServerAddresses, array );
11004 ForgetCF( &array );
11005
11006 // Add domains, if any.
11007
11008 array = CFArrayCreateMutable( NULL, (CFIndex) Min( gDNSConfigAdd_DomainCount, 1 ), &kCFTypeArrayCallBacks );
11009 require_action( array, exit, err = kNoMemoryErr );
11010
11011 if( gDNSConfigAdd_DomainCount > 0 )
11012 {
11013 for( i = 0; i < gDNSConfigAdd_DomainCount; ++i )
11014 {
11015 CFStringRef domainStr;
11016
11017 domainStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_DomainArray[ i ], kCFStringEncodingUTF8 );
11018 require_action( domainStr, exit, err = kUnknownErr );
11019
11020 CFArrayAppendValue( array, domainStr );
11021 CFRelease( domainStr );
11022 }
11023 }
11024 else
11025 {
11026 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
11027
11028 CFArrayAppendValue( array, CFSTR( "" ) );
11029 }
11030
11031 CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchDomains, array );
11032 ForgetCF( &array );
11033
11034 // Add interface, if any.
11035
11036 if( gDNSConfigAdd_Interface )
11037 {
11038 err = CFDictionarySetCString( dict, kSCPropInterfaceName, gDNSConfigAdd_Interface, kSizeCString );
11039 require_noerr( err, exit );
11040
11041 CFDictionarySetValue( dict, kSCPropNetDNSConfirmedServiceID, gDNSConfigAdd_ID );
11042 }
11043
11044 // Set dictionary in dynamic store.
11045
11046 store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
11047 err = map_scerror( store );
11048 require_noerr( err, exit );
11049
11050 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigAdd_ID, kSCEntNetDNS );
11051 require_action( key, exit, err = kUnknownErr );
11052
11053 success = SCDynamicStoreSetValue( store, key, dict );
11054 require_action( success, exit, err = kUnknownErr );
11055
11056 exit:
11057 CFReleaseNullSafe( dict );
11058 CFReleaseNullSafe( array );
11059 CFReleaseNullSafe( store );
11060 CFReleaseNullSafe( key );
11061 gExitCode = err ? 1 : 0;
11062 }
11063
11064 //===========================================================================================================================
11065 // DNSConfigRemoveCmd
11066 //===========================================================================================================================
11067
11068 static void DNSConfigRemoveCmd( void )
11069 {
11070 OSStatus err;
11071 SCDynamicStoreRef store = NULL;
11072 CFStringRef key = NULL;
11073 Boolean success;
11074
11075 if( geteuid() != 0 )
11076 {
11077 FPrintF( stderr, "error: This command must to be run as root.\n" );
11078 err = kIDErr;
11079 goto exit;
11080 }
11081
11082 store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
11083 err = map_scerror( store );
11084 require_noerr( err, exit );
11085
11086 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigRemove_ID, kSCEntNetDNS );
11087 require_action( key, exit, err = kUnknownErr );
11088
11089 success = SCDynamicStoreRemoveValue( store, key );
11090 require_action( success, exit, err = kUnknownErr );
11091
11092 exit:
11093 CFReleaseNullSafe( store );
11094 CFReleaseNullSafe( key );
11095 gExitCode = err ? 1 : 0;
11096 }
11097 #endif // TARGET_OS_DARWIN
11098
11099 //===========================================================================================================================
11100 // DaemonVersionCmd
11101 //===========================================================================================================================
11102
11103 static void DaemonVersionCmd( void )
11104 {
11105 OSStatus err;
11106 uint32_t size, version;
11107 char strBuf[ 16 ];
11108
11109 size = (uint32_t) sizeof( version );
11110 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
11111 require_noerr( err, exit );
11112
11113 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
11114
11115 exit:
11116 if( err ) exit( 1 );
11117 }
11118
11119 //===========================================================================================================================
11120 // Exit
11121 //===========================================================================================================================
11122
11123 static void Exit( void *inContext )
11124 {
11125 const char * const reason = (const char *) inContext;
11126
11127 FPrintF( stdout, "---\n" );
11128 FPrintF( stdout, "End time: %{du:time}\n", NULL );
11129 if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
11130 exit( gExitCode );
11131 }
11132
11133 //===========================================================================================================================
11134 // PrintFTimestampHandler
11135 //===========================================================================================================================
11136
11137 static int
11138 PrintFTimestampHandler(
11139 PrintFContext * inContext,
11140 PrintFFormat * inFormat,
11141 PrintFVAList * inArgs,
11142 void * inUserContext )
11143 {
11144 struct timeval now;
11145 const struct timeval * tv;
11146 struct tm * localTime;
11147 size_t len;
11148 int n;
11149 char dateTimeStr[ 32 ];
11150
11151 Unused( inUserContext );
11152
11153 tv = va_arg( inArgs->args, const struct timeval * );
11154 require_action_quiet( !inFormat->suppress, exit, n = 0 );
11155
11156 if( !tv )
11157 {
11158 gettimeofday( &now, NULL );
11159 tv = &now;
11160 }
11161 localTime = localtime( &tv->tv_sec );
11162 len = strftime( dateTimeStr, sizeof( dateTimeStr ), "%Y-%m-%d %H:%M:%S", localTime );
11163 if( len == 0 ) dateTimeStr[ 0 ] = '\0';
11164
11165 n = PrintFCore( inContext, "%s.%06u", dateTimeStr, (unsigned int) tv->tv_usec );
11166
11167 exit:
11168 return( n );
11169 }
11170
11171 //===========================================================================================================================
11172 // PrintFDNSMessageHandler
11173 //===========================================================================================================================
11174
11175 static int
11176 PrintFDNSMessageHandler(
11177 PrintFContext * inContext,
11178 PrintFFormat * inFormat,
11179 PrintFVAList * inArgs,
11180 void * inUserContext )
11181 {
11182 OSStatus err;
11183 const void * msgPtr;
11184 size_t msgLen;
11185 char * text;
11186 int n;
11187 Boolean isMDNS;
11188 Boolean printRawRData;
11189
11190 Unused( inUserContext );
11191
11192 msgPtr = va_arg( inArgs->args, const void * );
11193 msgLen = va_arg( inArgs->args, size_t );
11194 require_action_quiet( !inFormat->suppress, exit, n = 0 );
11195
11196 isMDNS = ( inFormat->altForm > 0 ) ? true : false;
11197 if( inFormat->precision == 0 ) printRawRData = false;
11198 else if( inFormat->precision == 1 ) printRawRData = true;
11199 else
11200 {
11201 n = PrintFCore( inContext, "<< BAD %%{du:dnsmsg} PRECISION >>" );
11202 goto exit;
11203 }
11204
11205 err = DNSMessageToText( msgPtr, msgLen, isMDNS, printRawRData, &text );
11206 if( !err )
11207 {
11208 n = PrintFCore( inContext, "%*{text}", inFormat->fieldWidth, text, kSizeCString );
11209 free( text );
11210 }
11211 else
11212 {
11213 n = PrintFCore( inContext, "%*.1H", inFormat->fieldWidth, msgPtr, (int) msgLen, (int) msgLen );
11214 }
11215
11216 exit:
11217 return( n );
11218 }
11219
11220 //===========================================================================================================================
11221 // GetDNSSDFlagsFromOpts
11222 //===========================================================================================================================
11223
11224 static DNSServiceFlags GetDNSSDFlagsFromOpts( void )
11225 {
11226 DNSServiceFlags flags;
11227
11228 flags = (DNSServiceFlags) gDNSSDFlags;
11229 if( flags & kDNSServiceFlagsShareConnection )
11230 {
11231 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
11232 kDNSServiceFlagsShareConnection );
11233 }
11234
11235 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains;
11236 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular;
11237 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive;
11238 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast;
11239 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
11240 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename;
11241 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone;
11242 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains;
11243 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates;
11244 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared;
11245 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable;
11246 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout;
11247 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse;
11248 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique;
11249 if( gDNSSDFlag_WakeOnResolve ) flags |= kDNSServiceFlagsWakeOnResolve;
11250
11251 return( flags );
11252 }
11253
11254 //===========================================================================================================================
11255 // CreateConnectionFromArgString
11256 //===========================================================================================================================
11257
11258 static OSStatus
11259 CreateConnectionFromArgString(
11260 const char * inString,
11261 dispatch_queue_t inQueue,
11262 DNSServiceRef * outSDRef,
11263 ConnectionDesc * outDesc )
11264 {
11265 OSStatus err;
11266 DNSServiceRef sdRef = NULL;
11267 ConnectionType type;
11268 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized.
11269 uint8_t uuid[ 16 ];
11270
11271 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
11272 {
11273 err = DNSServiceCreateConnection( &sdRef );
11274 require_noerr( err, exit );
11275 type = kConnectionType_Normal;
11276 }
11277 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
11278 {
11279 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
11280
11281 err = StringToInt32( pidStr, &pid );
11282 if( err )
11283 {
11284 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
11285 err = kParamErr;
11286 goto exit;
11287 }
11288
11289 memset( uuid, 0, sizeof( uuid ) );
11290 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
11291 if( err )
11292 {
11293 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
11294 goto exit;
11295 }
11296 type = kConnectionType_DelegatePID;
11297 }
11298 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
11299 {
11300 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
11301
11302 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
11303
11304 err = StringToUUID( uuidStr, kSizeCString, false, uuid );
11305 if( err )
11306 {
11307 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
11308 err = kParamErr;
11309 goto exit;
11310 }
11311
11312 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
11313 if( err )
11314 {
11315 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
11316 goto exit;
11317 }
11318 type = kConnectionType_DelegateUUID;
11319 }
11320 else
11321 {
11322 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
11323 err = kParamErr;
11324 goto exit;
11325 }
11326
11327 err = DNSServiceSetDispatchQueue( sdRef, inQueue );
11328 require_noerr( err, exit );
11329
11330 *outSDRef = sdRef;
11331 if( outDesc )
11332 {
11333 outDesc->type = type;
11334 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid;
11335 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 );
11336 }
11337 sdRef = NULL;
11338
11339 exit:
11340 if( sdRef ) DNSServiceRefDeallocate( sdRef );
11341 return( err );
11342 }
11343
11344 //===========================================================================================================================
11345 // InterfaceIndexFromArgString
11346 //===========================================================================================================================
11347
11348 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
11349 {
11350 OSStatus err;
11351 uint32_t ifIndex;
11352
11353 if( inString )
11354 {
11355 ifIndex = if_nametoindex( inString );
11356 if( ifIndex == 0 )
11357 {
11358 err = StringToUInt32( inString, &ifIndex );
11359 if( err )
11360 {
11361 FPrintF( stderr, "Invalid interface value: %s\n", inString );
11362 err = kParamErr;
11363 goto exit;
11364 }
11365 }
11366 }
11367 else
11368 {
11369 ifIndex = 0;
11370 }
11371
11372 *outIndex = ifIndex;
11373 err = kNoErr;
11374
11375 exit:
11376 return( err );
11377 }
11378
11379 //===========================================================================================================================
11380 // RecordDataFromArgString
11381 //===========================================================================================================================
11382
11383 #define kRDataMaxLen UINT16_C( 0xFFFF )
11384
11385 static OSStatus StringToSRVRData( const char *inString, uint8_t **outPtr, size_t *outLen );
11386 static OSStatus StringToTXTRData( const char *inString, char inDelimiter, uint8_t **outPtr, size_t *outLen );
11387
11388 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
11389 {
11390 OSStatus err;
11391 uint8_t * dataPtr = NULL;
11392 size_t dataLen;
11393
11394 if( 0 ) {}
11395
11396 // Domain name
11397
11398 else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 )
11399 {
11400 const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain );
11401
11402 err = StringToDomainName( str, &dataPtr, &dataLen );
11403 require_noerr_quiet( err, exit );
11404 }
11405
11406 // File path
11407
11408 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
11409 {
11410 const char * const path = inString + sizeof_string( kRDataArgPrefix_File );
11411
11412 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
11413 require_noerr( err, exit );
11414 require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr );
11415 }
11416
11417 // Hexadecimal string
11418
11419 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
11420 {
11421 const char * const str = inString + sizeof_string( kRDataArgPrefix_HexString );
11422
11423 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
11424 require_noerr( err, exit );
11425 require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr );
11426 }
11427
11428 // IPv4 address string
11429
11430 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 )
11431 {
11432 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 );
11433
11434 err = StringToARecordData( str, &dataPtr, &dataLen );
11435 require_noerr_quiet( err, exit );
11436 }
11437
11438 // IPv6 address string
11439
11440 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 )
11441 {
11442 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 );
11443
11444 err = StringToAAAARecordData( str, &dataPtr, &dataLen );
11445 require_noerr_quiet( err, exit );
11446 }
11447
11448 // SRV record
11449
11450 else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 )
11451 {
11452 const char * const str = inString + sizeof_string( kRDataArgPrefix_SRV );
11453
11454 err = StringToSRVRData( str, &dataPtr, &dataLen );
11455 require_noerr( err, exit );
11456 }
11457
11458 // String with escaped hex and octal bytes
11459
11460 else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
11461 {
11462 const char * const str = inString + sizeof_string( kRDataArgPrefix_String );
11463 const char * const end = str + strlen( str );
11464 size_t copiedLen;
11465 size_t totalLen;
11466 Boolean success;
11467
11468 if( str < end )
11469 {
11470 success = ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL );
11471 require_action( success, exit, err = kParamErr );
11472 require_action( totalLen <= kRDataMaxLen, exit, err = kSizeErr );
11473
11474 dataLen = totalLen;
11475 dataPtr = (uint8_t *) malloc( dataLen );
11476 require_action( dataPtr, exit, err = kNoMemoryErr );
11477
11478 success = ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL );
11479 require_action( success, exit, err = kParamErr );
11480 check( copiedLen == dataLen );
11481 }
11482 else
11483 {
11484 dataPtr = NULL;
11485 dataLen = 0;
11486 }
11487 }
11488
11489 // TXT record
11490
11491 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
11492 {
11493 const char * const str = inString + sizeof_string( kRDataArgPrefix_TXT );
11494
11495 err = StringToTXTRData( str, ',', &dataPtr, &dataLen );
11496 require_noerr( err, exit );
11497 }
11498
11499 // Unrecognized format
11500
11501 else
11502 {
11503 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
11504 err = kParamErr;
11505 goto exit;
11506 }
11507
11508 err = kNoErr;
11509 *outDataLen = dataLen;
11510 *outDataPtr = dataPtr;
11511 dataPtr = NULL;
11512
11513 exit:
11514 FreeNullSafe( dataPtr );
11515 return( err );
11516 }
11517
11518 static OSStatus StringToSRVRData( const char *inString, uint8_t **outPtr, size_t *outLen )
11519 {
11520 OSStatus err;
11521 DataBuffer dataBuf;
11522 const char * ptr;
11523 int i;
11524 uint8_t * end;
11525 uint8_t target[ kDomainNameLengthMax ];
11526
11527 DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax );
11528
11529 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
11530
11531 ptr = inString;
11532 for( i = 0; i < 3; ++i )
11533 {
11534 char * next;
11535 long value;
11536 uint8_t buf[ 2 ];
11537
11538 value = strtol( ptr, &next, 0 );
11539 require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr );
11540 require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr );
11541 ptr = next + 1;
11542
11543 WriteBig16( buf, value );
11544
11545 err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) );
11546 require_noerr( err, exit );
11547 }
11548
11549 // Set the target domain name.
11550
11551 err = DomainNameFromString( target, ptr, &end );
11552 require_noerr_quiet( err, exit );
11553
11554 err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) );
11555 require_noerr( err, exit );
11556
11557 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
11558 require_noerr( err, exit );
11559
11560 exit:
11561 DataBuffer_Free( &dataBuf );
11562 return( err );
11563 }
11564
11565 static OSStatus StringToTXTRData( const char *inString, char inDelimiter, uint8_t **outPtr, size_t *outLen )
11566 {
11567 OSStatus err;
11568 DataBuffer dataBuf;
11569 const char * src;
11570 uint8_t txtStr[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
11571
11572 DataBuffer_Init( &dataBuf, NULL, 0, kRDataMaxLen );
11573
11574 src = inString;
11575 for( ;; )
11576 {
11577 uint8_t * dst = &txtStr[ 1 ];
11578 const uint8_t * const lim = &txtStr[ 256 ];
11579 int c;
11580
11581 while( *src && ( *src != inDelimiter ) )
11582 {
11583 if( ( c = *src++ ) == '\\' )
11584 {
11585 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
11586 c = *src++;
11587 }
11588 require_action_quiet( dst < lim, exit, err = kOverrunErr );
11589 *dst++ = (uint8_t) c;
11590 }
11591 txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] );
11592 err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] );
11593 require_noerr( err, exit );
11594
11595 if( *src == '\0' ) break;
11596 ++src;
11597 }
11598
11599 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
11600 require_noerr( err, exit );
11601
11602 exit:
11603 DataBuffer_Free( &dataBuf );
11604 return( err );
11605 }
11606
11607 //===========================================================================================================================
11608 // RecordTypeFromArgString
11609 //===========================================================================================================================
11610
11611 typedef struct
11612 {
11613 uint16_t value; // Record type's numeric value.
11614 const char * name; // Record type's name as a string (e.g., "A", "PTR", "SRV").
11615
11616 } RecordType;
11617
11618 static const RecordType kRecordTypes[] =
11619 {
11620 // Common types.
11621
11622 { kDNSServiceType_A, "A" },
11623 { kDNSServiceType_AAAA, "AAAA" },
11624 { kDNSServiceType_PTR, "PTR" },
11625 { kDNSServiceType_SRV, "SRV" },
11626 { kDNSServiceType_TXT, "TXT" },
11627 { kDNSServiceType_CNAME, "CNAME" },
11628 { kDNSServiceType_SOA, "SOA" },
11629 { kDNSServiceType_NSEC, "NSEC" },
11630 { kDNSServiceType_NS, "NS" },
11631 { kDNSServiceType_MX, "MX" },
11632 { kDNSServiceType_ANY, "ANY" },
11633 { kDNSServiceType_OPT, "OPT" },
11634
11635 // Less common types.
11636
11637 { kDNSServiceType_MD, "MD" },
11638 { kDNSServiceType_NS, "NS" },
11639 { kDNSServiceType_MD, "MD" },
11640 { kDNSServiceType_MF, "MF" },
11641 { kDNSServiceType_MB, "MB" },
11642 { kDNSServiceType_MG, "MG" },
11643 { kDNSServiceType_MR, "MR" },
11644 { kDNSServiceType_NULL, "NULL" },
11645 { kDNSServiceType_WKS, "WKS" },
11646 { kDNSServiceType_HINFO, "HINFO" },
11647 { kDNSServiceType_MINFO, "MINFO" },
11648 { kDNSServiceType_RP, "RP" },
11649 { kDNSServiceType_AFSDB, "AFSDB" },
11650 { kDNSServiceType_X25, "X25" },
11651 { kDNSServiceType_ISDN, "ISDN" },
11652 { kDNSServiceType_RT, "RT" },
11653 { kDNSServiceType_NSAP, "NSAP" },
11654 { kDNSServiceType_NSAP_PTR, "NSAP_PTR" },
11655 { kDNSServiceType_SIG, "SIG" },
11656 { kDNSServiceType_KEY, "KEY" },
11657 { kDNSServiceType_PX, "PX" },
11658 { kDNSServiceType_GPOS, "GPOS" },
11659 { kDNSServiceType_LOC, "LOC" },
11660 { kDNSServiceType_NXT, "NXT" },
11661 { kDNSServiceType_EID, "EID" },
11662 { kDNSServiceType_NIMLOC, "NIMLOC" },
11663 { kDNSServiceType_ATMA, "ATMA" },
11664 { kDNSServiceType_NAPTR, "NAPTR" },
11665 { kDNSServiceType_KX, "KX" },
11666 { kDNSServiceType_CERT, "CERT" },
11667 { kDNSServiceType_A6, "A6" },
11668 { kDNSServiceType_DNAME, "DNAME" },
11669 { kDNSServiceType_SINK, "SINK" },
11670 { kDNSServiceType_APL, "APL" },
11671 { kDNSServiceType_DS, "DS" },
11672 { kDNSServiceType_SSHFP, "SSHFP" },
11673 { kDNSServiceType_IPSECKEY, "IPSECKEY" },
11674 { kDNSServiceType_RRSIG, "RRSIG" },
11675 { kDNSServiceType_DNSKEY, "DNSKEY" },
11676 { kDNSServiceType_DHCID, "DHCID" },
11677 { kDNSServiceType_NSEC3, "NSEC3" },
11678 { kDNSServiceType_NSEC3PARAM, "NSEC3PARAM" },
11679 { kDNSServiceType_HIP, "HIP" },
11680 { kDNSServiceType_SPF, "SPF" },
11681 { kDNSServiceType_UINFO, "UINFO" },
11682 { kDNSServiceType_UID, "UID" },
11683 { kDNSServiceType_GID, "GID" },
11684 { kDNSServiceType_UNSPEC, "UNSPEC" },
11685 { kDNSServiceType_TKEY, "TKEY" },
11686 { kDNSServiceType_TSIG, "TSIG" },
11687 { kDNSServiceType_IXFR, "IXFR" },
11688 { kDNSServiceType_AXFR, "AXFR" },
11689 { kDNSServiceType_MAILB, "MAILB" },
11690 { kDNSServiceType_MAILA, "MAILA" }
11691 };
11692
11693 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue )
11694 {
11695 OSStatus err;
11696 int32_t i32;
11697 const RecordType * type;
11698 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
11699
11700 for( type = kRecordTypes; type < end; ++type )
11701 {
11702 if( strcasecmp( type->name, inString ) == 0 )
11703 {
11704 *outValue = type->value;
11705 return( kNoErr );
11706 }
11707 }
11708
11709 err = StringToInt32( inString, &i32 );
11710 require_noerr_quiet( err, exit );
11711 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
11712
11713 *outValue = (uint16_t) i32;
11714
11715 exit:
11716 return( err );
11717 }
11718
11719 //===========================================================================================================================
11720 // RecordClassFromArgString
11721 //===========================================================================================================================
11722
11723 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue )
11724 {
11725 OSStatus err;
11726 int32_t i32;
11727
11728 if( strcasecmp( inString, "IN" ) == 0 )
11729 {
11730 *outValue = kDNSServiceClass_IN;
11731 err = kNoErr;
11732 goto exit;
11733 }
11734
11735 err = StringToInt32( inString, &i32 );
11736 require_noerr_quiet( err, exit );
11737 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
11738
11739 *outValue = (uint16_t) i32;
11740
11741 exit:
11742 return( err );
11743 }
11744
11745 //===========================================================================================================================
11746 // InterfaceIndexToName
11747 //===========================================================================================================================
11748
11749 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
11750 {
11751 switch( inIfIndex )
11752 {
11753 case kDNSServiceInterfaceIndexAny:
11754 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
11755 break;
11756
11757 case kDNSServiceInterfaceIndexLocalOnly:
11758 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
11759 break;
11760
11761 case kDNSServiceInterfaceIndexUnicast:
11762 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
11763 break;
11764
11765 case kDNSServiceInterfaceIndexP2P:
11766 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
11767 break;
11768
11769 #if( defined( kDNSServiceInterfaceIndexBLE ) )
11770 case kDNSServiceInterfaceIndexBLE:
11771 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
11772 break;
11773 #endif
11774
11775 default:
11776 {
11777 const char * name;
11778
11779 name = if_indextoname( inIfIndex, inNameBuf );
11780 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
11781 break;
11782 }
11783 }
11784
11785 return( inNameBuf );
11786 }
11787
11788 //===========================================================================================================================
11789 // RecordTypeToString
11790 //===========================================================================================================================
11791
11792 static const char * RecordTypeToString( unsigned int inValue )
11793 {
11794 const RecordType * type;
11795 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
11796
11797 for( type = kRecordTypes; type < end; ++type )
11798 {
11799 if( type->value == inValue ) return( type->name );
11800 }
11801 return( "???" );
11802 }
11803
11804 //===========================================================================================================================
11805 // DNSMessageExtractDomainName
11806 //===========================================================================================================================
11807
11808 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
11809
11810 static OSStatus
11811 DNSMessageExtractDomainName(
11812 const uint8_t * inMsgPtr,
11813 size_t inMsgLen,
11814 const uint8_t * inNamePtr,
11815 uint8_t inBuf[ kDomainNameLengthMax ],
11816 const uint8_t ** outNextPtr )
11817 {
11818 OSStatus err;
11819 const uint8_t * label;
11820 uint8_t labelLen;
11821 const uint8_t * nextLabel;
11822 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
11823 uint8_t * dst = inBuf;
11824 const uint8_t * const dstLim = inBuf ? ( inBuf + kDomainNameLengthMax ) : NULL;
11825 const uint8_t * nameEnd = NULL;
11826
11827 require_action( ( inNamePtr >= inMsgPtr ) && ( inNamePtr < msgEnd ), exit, err = kRangeErr );
11828
11829 for( label = inNamePtr; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
11830 {
11831 if( labelLen <= kDomainLabelLengthMax )
11832 {
11833 nextLabel = label + 1 + labelLen;
11834 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr );
11835 if( dst )
11836 {
11837 require_action( ( dstLim - dst ) > ( 1 + labelLen ), exit, err = kOverrunErr );
11838 memcpy( dst, label, 1 + labelLen );
11839 dst += ( 1 + labelLen );
11840 }
11841 }
11842 else if( IsCompressionByte( labelLen ) )
11843 {
11844 uint16_t offset;
11845
11846 require_action( ( msgEnd - label ) >= 2, exit, err = kUnderrunErr );
11847 if( !nameEnd )
11848 {
11849 nameEnd = label + 2;
11850 if( !dst ) break;
11851 }
11852 offset = (uint16_t)( ( ( label[ 0 ] & 0x3F ) << 8 ) | label[ 1 ] );
11853 nextLabel = inMsgPtr + offset;
11854 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr );
11855 require_action( !IsCompressionByte( nextLabel[ 0 ] ), exit, err = kMalformedErr );
11856 }
11857 else
11858 {
11859 dlogassert( "Unhandled label length 0x%02X\n", labelLen );
11860 err = kMalformedErr;
11861 goto exit;
11862 }
11863 }
11864
11865 if( dst ) *dst = 0;
11866 if( !nameEnd ) nameEnd = label + 1;
11867
11868 if( outNextPtr ) *outNextPtr = nameEnd;
11869 err = kNoErr;
11870
11871 exit:
11872 return( err );
11873 }
11874
11875 //===========================================================================================================================
11876 // DNSMessageExtractDomainNameString
11877 //===========================================================================================================================
11878
11879 static OSStatus
11880 DNSMessageExtractDomainNameString(
11881 const void * inMsgPtr,
11882 size_t inMsgLen,
11883 const void * inNamePtr,
11884 char inBuf[ kDNSServiceMaxDomainName ],
11885 const uint8_t ** outNextPtr )
11886 {
11887 OSStatus err;
11888 const uint8_t * nextPtr;
11889 uint8_t domainName[ kDomainNameLengthMax ];
11890
11891 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inNamePtr, domainName, &nextPtr );
11892 require_noerr( err, exit );
11893
11894 err = DomainNameToString( domainName, NULL, inBuf, NULL );
11895 require_noerr( err, exit );
11896
11897 if( outNextPtr ) *outNextPtr = nextPtr;
11898
11899 exit:
11900 return( err );
11901 }
11902
11903 //===========================================================================================================================
11904 // DNSMessageExtractRecord
11905 //===========================================================================================================================
11906
11907 typedef struct
11908 {
11909 uint8_t type[ 2 ];
11910 uint8_t class[ 2 ];
11911 uint8_t ttl[ 4 ];
11912 uint8_t rdLength[ 2 ];
11913 uint8_t rdata[ 1 ];
11914
11915 } DNSRecordFields;
11916
11917 check_compile_time( offsetof( DNSRecordFields, rdata ) == 10 );
11918
11919 static OSStatus
11920 DNSMessageExtractRecord(
11921 const uint8_t * inMsgPtr,
11922 size_t inMsgLen,
11923 const uint8_t * inPtr,
11924 uint8_t inNameBuf[ kDomainNameLengthMax ],
11925 uint16_t * outType,
11926 uint16_t * outClass,
11927 uint32_t * outTTL,
11928 const uint8_t ** outRDataPtr,
11929 size_t * outRDataLen,
11930 const uint8_t ** outPtr )
11931 {
11932 OSStatus err;
11933 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
11934 const uint8_t * ptr;
11935 const DNSRecordFields * record;
11936 size_t rdLength;
11937
11938 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, inNameBuf, &ptr );
11939 require_noerr_quiet( err, exit );
11940 require_action_quiet( (size_t)( msgEnd - ptr ) >= offsetof( DNSRecordFields, rdata ), exit, err = kUnderrunErr );
11941
11942 record = (DNSRecordFields *) ptr;
11943 rdLength = ReadBig16( record->rdLength );
11944 require_action_quiet( (size_t)( msgEnd - record->rdata ) >= rdLength , exit, err = kUnderrunErr );
11945
11946 if( outType ) *outType = ReadBig16( record->type );
11947 if( outClass ) *outClass = ReadBig16( record->class );
11948 if( outTTL ) *outTTL = ReadBig32( record->ttl );
11949 if( outRDataPtr ) *outRDataPtr = record->rdata;
11950 if( outRDataLen ) *outRDataLen = rdLength;
11951 if( outPtr ) *outPtr = record->rdata + rdLength;
11952
11953 exit:
11954 return( err );
11955 }
11956
11957 //===========================================================================================================================
11958 // DNSMessageGetAnswerSection
11959 //===========================================================================================================================
11960
11961 static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr )
11962 {
11963 OSStatus err;
11964 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
11965 unsigned int questionCount, i;
11966 const DNSHeader * hdr;
11967 const uint8_t * ptr;
11968
11969 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
11970
11971 hdr = (DNSHeader *) inMsgPtr;
11972 questionCount = DNSHeaderGetQuestionCount( hdr );
11973
11974 ptr = (uint8_t *)( hdr + 1 );
11975 for( i = 0; i < questionCount; ++i )
11976 {
11977 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, ptr, NULL, &ptr );
11978 require_noerr( err, exit );
11979 require_action_quiet( ( msgEnd - ptr ) >= 4, exit, err = kUnderrunErr );
11980 ptr += 4;
11981 }
11982
11983 if( outPtr ) *outPtr = ptr;
11984 err = kNoErr;
11985
11986 exit:
11987 return( err );
11988 }
11989
11990 //===========================================================================================================================
11991 // DNSRecordDataToString
11992 //===========================================================================================================================
11993
11994 static OSStatus
11995 DNSRecordDataToString(
11996 const void * inRDataPtr,
11997 size_t inRDataLen,
11998 unsigned int inRDataType,
11999 const void * inMsgPtr,
12000 size_t inMsgLen,
12001 char ** outString )
12002 {
12003 OSStatus err;
12004 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr;
12005 const uint8_t * const rdataEnd = rdataPtr + inRDataLen;
12006 char * rdataStr;
12007 const uint8_t * ptr;
12008 int n;
12009 char domainNameStr[ kDNSServiceMaxDomainName ];
12010
12011 rdataStr = NULL;
12012 if( inRDataType == kDNSServiceType_A )
12013 {
12014 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr );
12015
12016 ASPrintF( &rdataStr, "%.4a", rdataPtr );
12017 require_action( rdataStr, exit, err = kNoMemoryErr );
12018 }
12019 else if( inRDataType == kDNSServiceType_AAAA )
12020 {
12021 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr );
12022
12023 ASPrintF( &rdataStr, "%.16a", rdataPtr );
12024 require_action( rdataStr, exit, err = kNoMemoryErr );
12025 }
12026 else if( ( inRDataType == kDNSServiceType_PTR ) || ( inRDataType == kDNSServiceType_CNAME ) ||
12027 ( inRDataType == kDNSServiceType_NS ) )
12028 {
12029 if( inMsgPtr )
12030 {
12031 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, NULL );
12032 require_noerr( err, exit );
12033 }
12034 else
12035 {
12036 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL );
12037 require_noerr( err, exit );
12038 }
12039
12040 rdataStr = strdup( domainNameStr );
12041 require_action( rdataStr, exit, err = kNoMemoryErr );
12042 }
12043 else if( inRDataType == kDNSServiceType_SRV )
12044 {
12045 uint16_t priority, weight, port;
12046 const uint8_t * target;
12047
12048 require_action_quiet( ( rdataPtr + 6 ) < rdataEnd, exit, err = kMalformedErr );
12049
12050 priority = ReadBig16( rdataPtr );
12051 weight = ReadBig16( rdataPtr + 2 );
12052 port = ReadBig16( rdataPtr + 4 );
12053 target = rdataPtr + 6;
12054
12055 if( inMsgPtr )
12056 {
12057 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, target, domainNameStr, NULL );
12058 require_noerr( err, exit );
12059 }
12060 else
12061 {
12062 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL );
12063 require_noerr( err, exit );
12064 }
12065
12066 ASPrintF( &rdataStr, "%u %u %u %s", priority, weight, port, domainNameStr );
12067 require_action( rdataStr, exit, err = kNoMemoryErr );
12068 }
12069 else if( inRDataType == kDNSServiceType_TXT )
12070 {
12071 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr );
12072
12073 if( inRDataLen == 1 )
12074 {
12075 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) inRDataLen, INT_MAX );
12076 require_action( rdataStr, exit, err = kNoMemoryErr );
12077 }
12078 else
12079 {
12080 ASPrintF( &rdataStr, "%#{txt}", rdataPtr, inRDataLen );
12081 require_action( rdataStr, exit, err = kNoMemoryErr );
12082 }
12083 }
12084 else if( inRDataType == kDNSServiceType_SOA )
12085 {
12086 uint32_t serial, refresh, retry, expire, minimum;
12087
12088 if( inMsgPtr )
12089 {
12090 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
12091 require_noerr( err, exit );
12092
12093 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
12094
12095 rdataStr = strdup( domainNameStr );
12096 require_action( rdataStr, exit, err = kNoMemoryErr );
12097
12098 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, domainNameStr, &ptr );
12099 require_noerr( err, exit );
12100 }
12101 else
12102 {
12103 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
12104 require_noerr( err, exit );
12105
12106 rdataStr = strdup( domainNameStr );
12107 require_action( rdataStr, exit, err = kNoMemoryErr );
12108
12109 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
12110 require_noerr( err, exit );
12111 }
12112
12113 require_action_quiet( ( ptr + 20 ) == rdataEnd, exit, err = kMalformedErr );
12114
12115 serial = ReadBig32( ptr );
12116 refresh = ReadBig32( ptr + 4 );
12117 retry = ReadBig32( ptr + 8 );
12118 expire = ReadBig32( ptr + 12 );
12119 minimum = ReadBig32( ptr + 16 );
12120
12121 n = AppendPrintF( &rdataStr, " %s %u %u %u %u %u\n", domainNameStr, serial, refresh, retry, expire, minimum );
12122 require_action( n > 0, exit, err = kUnknownErr );
12123 }
12124 else if( inRDataType == kDNSServiceType_NSEC )
12125 {
12126 unsigned int windowBlock, bitmapLen, i, recordType;
12127 const uint8_t * bitmapPtr;
12128
12129 if( inMsgPtr )
12130 {
12131 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
12132 require_noerr( err, exit );
12133 }
12134 else
12135 {
12136 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
12137 require_noerr( err, exit );
12138 }
12139
12140 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
12141
12142 rdataStr = strdup( domainNameStr );
12143 require_action( rdataStr, exit, err = kNoMemoryErr );
12144
12145 for( ; ptr < rdataEnd; ptr += ( 2 + bitmapLen ) )
12146 {
12147 require_action_quiet( ( ptr + 2 ) < rdataEnd, exit, err = kMalformedErr );
12148
12149 windowBlock = ptr[ 0 ];
12150 bitmapLen = ptr[ 1 ];
12151 bitmapPtr = &ptr[ 2 ];
12152
12153 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr );
12154 require_action_quiet( ( bitmapPtr + bitmapLen ) <= rdataEnd, exit, err = kMalformedErr );
12155
12156 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i )
12157 {
12158 if( BitArray_GetBit( bitmapPtr, bitmapLen, i ) )
12159 {
12160 recordType = ( windowBlock * 256 ) + i;
12161 n = AppendPrintF( &rdataStr, " %s", RecordTypeToString( recordType ) );
12162 require_action( n > 0, exit, err = kUnknownErr );
12163 }
12164 }
12165 }
12166 }
12167 else if( inRDataType == kDNSServiceType_MX )
12168 {
12169 uint16_t preference;
12170 const uint8_t * exchange;
12171
12172 require_action_quiet( ( rdataPtr + 2 ) < rdataEnd, exit, err = kMalformedErr );
12173
12174 preference = ReadBig16( rdataPtr );
12175 exchange = &rdataPtr[ 2 ];
12176
12177 if( inMsgPtr )
12178 {
12179 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, exchange, domainNameStr, NULL );
12180 require_noerr( err, exit );
12181 }
12182 else
12183 {
12184 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL );
12185 require_noerr( err, exit );
12186 }
12187
12188 n = ASPrintF( &rdataStr, "%u %s", preference, domainNameStr );
12189 require_action( n > 0, exit, err = kUnknownErr );
12190 }
12191 else
12192 {
12193 err = kNotHandledErr;
12194 goto exit;
12195 }
12196
12197 check( rdataStr );
12198 *outString = rdataStr;
12199 rdataStr = NULL;
12200 err = kNoErr;
12201
12202 exit:
12203 FreeNullSafe( rdataStr );
12204 return( err );
12205 }
12206
12207 //===========================================================================================================================
12208 // DomainNameAppendString
12209 //===========================================================================================================================
12210
12211 static OSStatus
12212 DomainNameAppendString(
12213 uint8_t inDomainName[ kDomainNameLengthMax ],
12214 const char * inString,
12215 uint8_t ** outEndPtr )
12216 {
12217 OSStatus err;
12218 const char * src;
12219 uint8_t * root;
12220 const uint8_t * const nameLim = inDomainName + kDomainNameLengthMax;
12221
12222 for( root = inDomainName; ( root < nameLim ) && *root; root += ( 1 + *root ) ) {}
12223 require_action_quiet( root < nameLim, exit, err = kMalformedErr );
12224
12225 // If the string is a single dot, denoting the root domain, then there are no non-empty labels.
12226
12227 src = inString;
12228 if( ( src[ 0 ] == '.' ) && ( src[ 1 ] == '\0' ) ) ++src;
12229 while( *src )
12230 {
12231 uint8_t * const label = root;
12232 const uint8_t * const labelLim = Min( &label[ 1 + kDomainLabelLengthMax ], nameLim - 1 );
12233 uint8_t * dst;
12234 int c;
12235 size_t labelLen;
12236
12237 dst = &label[ 1 ];
12238 while( *src && ( ( c = *src++ ) != '.' ) )
12239 {
12240 if( c == '\\' )
12241 {
12242 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
12243 c = *src++;
12244 if( isdigit_safe( c ) && isdigit_safe( src[ 0 ] ) && isdigit_safe( src[ 1 ] ) )
12245 {
12246 const int decimal = ( ( c - '0' ) * 100 ) + ( ( src[ 0 ] - '0' ) * 10 ) + ( src[ 1 ] - '0' );
12247
12248 if( decimal <= 255 )
12249 {
12250 c = decimal;
12251 src += 2;
12252 }
12253 }
12254 }
12255 require_action_quiet( dst < labelLim, exit, err = kOverrunErr );
12256 *dst++ = (uint8_t) c;
12257 }
12258
12259 labelLen = (size_t)( dst - &label[ 1 ] );
12260 require_action_quiet( labelLen > 0, exit, err = kMalformedErr );
12261
12262 label[ 0 ] = (uint8_t) labelLen;
12263 root = dst;
12264 *root = 0;
12265 }
12266
12267 if( outEndPtr ) *outEndPtr = root + 1;
12268 err = kNoErr;
12269
12270 exit:
12271 return( err );
12272 }
12273
12274 //===========================================================================================================================
12275 // DomainNameEqual
12276 //===========================================================================================================================
12277
12278 static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 )
12279 {
12280 const uint8_t * p1 = inName1;
12281 const uint8_t * p2 = inName2;
12282 unsigned int len;
12283
12284 for( ;; )
12285 {
12286 if( ( len = *p1++ ) != *p2++ ) return( false );
12287 if( len == 0 ) break;
12288 for( ; len > 0; ++p1, ++p2, --len )
12289 {
12290 if( tolower_safe( *p1 ) != tolower_safe( *p2 ) ) return( false );
12291 }
12292 }
12293 return( true );
12294 }
12295
12296 //===========================================================================================================================
12297 // DomainNameLength
12298 //===========================================================================================================================
12299
12300 static size_t DomainNameLength( const uint8_t * const inName )
12301 {
12302 const uint8_t * ptr;
12303
12304 for( ptr = inName; *ptr != 0; ptr += ( 1 + *ptr ) ) {}
12305 return( (size_t)( ptr - inName ) + 1 );
12306 }
12307
12308 //===========================================================================================================================
12309 // DomainNameFromString
12310 //===========================================================================================================================
12311
12312 static OSStatus
12313 DomainNameFromString(
12314 uint8_t inDomainName[ kDomainNameLengthMax ],
12315 const char * inString,
12316 uint8_t ** outEndPtr )
12317 {
12318 inDomainName[ 0 ] = 0;
12319 return( DomainNameAppendString( inDomainName, inString, outEndPtr ) );
12320 }
12321
12322 //===========================================================================================================================
12323 // DomainNameToString
12324 //===========================================================================================================================
12325
12326 static OSStatus
12327 DomainNameToString(
12328 const uint8_t * inDomainName,
12329 const uint8_t * inEnd,
12330 char inBuf[ kDNSServiceMaxDomainName ],
12331 const uint8_t ** outNextPtr )
12332 {
12333 OSStatus err;
12334 const uint8_t * label;
12335 uint8_t labelLen;
12336 const uint8_t * nextLabel;
12337 char * dst;
12338 const uint8_t * src;
12339
12340 require_action( !inEnd || ( inDomainName < inEnd ), exit, err = kUnderrunErr );
12341
12342 // Convert each label up until the root label, i.e., the zero-length label.
12343
12344 dst = inBuf;
12345 for( label = inDomainName; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
12346 {
12347 require_action( labelLen <= kDomainLabelLengthMax, exit, err = kMalformedErr );
12348
12349 nextLabel = &label[ 1 ] + labelLen;
12350 require_action( ( nextLabel - inDomainName ) < kDomainNameLengthMax, exit, err = kMalformedErr );
12351 require_action( !inEnd || ( nextLabel < inEnd ), exit, err = kUnderrunErr );
12352
12353 for( src = &label[ 1 ]; src < nextLabel; ++src )
12354 {
12355 if( isprint_safe( *src ) )
12356 {
12357 if( ( *src == '.' ) || ( *src == '\\' ) || ( *src == ' ' ) ) *dst++ = '\\';
12358 *dst++ = (char) *src;
12359 }
12360 else
12361 {
12362 *dst++ = '\\';
12363 *dst++ = '0' + ( *src / 100 );
12364 *dst++ = '0' + ( ( *src / 10 ) % 10 );
12365 *dst++ = '0' + ( *src % 10 );
12366 }
12367 }
12368 *dst++ = '.';
12369 }
12370
12371 // At this point, label points to the root label.
12372 // If the root label was the only label, then write a dot for it.
12373
12374 if( label == inDomainName ) *dst++ = '.';
12375 *dst = '\0';
12376 if( outNextPtr ) *outNextPtr = label + 1;
12377 err = kNoErr;
12378
12379 exit:
12380 return( err );
12381 }
12382
12383 //===========================================================================================================================
12384 // DNSMessageToText
12385 //===========================================================================================================================
12386
12387 #define DNSFlagsOpCodeToString( X ) ( \
12388 ( (X) == kDNSOpCode_Query ) ? "Query" : \
12389 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
12390 ( (X) == kDNSOpCode_Status ) ? "Status" : \
12391 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
12392 ( (X) == kDNSOpCode_Update ) ? "Update" : \
12393 "Unassigned" )
12394
12395 #define DNSFlagsRCodeToString( X ) ( \
12396 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
12397 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
12398 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
12399 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
12400 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
12401 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
12402 "???" )
12403
12404 static OSStatus
12405 DNSMessageToText(
12406 const uint8_t * inMsgPtr,
12407 size_t inMsgLen,
12408 const Boolean inMDNS,
12409 const Boolean inPrintRaw,
12410 char ** outText )
12411 {
12412 OSStatus err;
12413 DataBuffer dataBuf;
12414 size_t len;
12415 const DNSHeader * hdr;
12416 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
12417 const uint8_t * ptr;
12418 unsigned int id, flags, opcode, rcode;
12419 unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount;
12420 char nameStr[ kDNSServiceMaxDomainName ];
12421
12422 DataBuffer_Init( &dataBuf, NULL, 0, SIZE_MAX );
12423 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
12424
12425 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
12426
12427 hdr = (DNSHeader *) inMsgPtr;
12428 id = DNSHeaderGetID( hdr );
12429 flags = DNSHeaderGetFlags( hdr );
12430 questionCount = DNSHeaderGetQuestionCount( hdr );
12431 answerCount = DNSHeaderGetAnswerCount( hdr );
12432 authorityCount = DNSHeaderGetAuthorityCount( hdr );
12433 additionalCount = DNSHeaderGetAdditionalCount( hdr );
12434 opcode = DNSFlagsGetOpCode( flags );
12435 rcode = DNSFlagsGetRCode( flags );
12436
12437 _Append( "ID: 0x%04X (%u)\n", id, id );
12438 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
12439 flags,
12440 ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ),
12441 ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!',
12442 ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!',
12443 ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!',
12444 ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!',
12445 !inMDNS, ( flags & kDNSHeaderFlag_AuthenticData ) ? " AD" : "!AD",
12446 !inMDNS, ( flags & kDNSHeaderFlag_CheckingDisabled ) ? " CD" : "!CD",
12447 DNSFlagsRCodeToString( rcode ) );
12448 _Append( "Question count: %u\n", questionCount );
12449 _Append( "Answer count: %u\n", answerCount );
12450 _Append( "Authority count: %u\n", authorityCount );
12451 _Append( "Additional count: %u\n", additionalCount );
12452
12453 ptr = (const uint8_t *) &hdr[ 1 ];
12454 for( i = 0; i < questionCount; ++i )
12455 {
12456 unsigned int qtype, qclass;
12457 Boolean isQU;
12458
12459 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, nameStr, &ptr );
12460 require_noerr( err, exit );
12461
12462 if( ( msgEnd - ptr ) < 4 )
12463 {
12464 err = kUnderrunErr;
12465 goto exit;
12466 }
12467
12468 qtype = DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields *) ptr );
12469 qclass = DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields *) ptr );
12470 ptr += 4;
12471
12472 isQU = ( inMDNS && ( qclass & kQClassUnicastResponseBit ) ) ? true : false;
12473 if( inMDNS ) qclass &= ~kQClassUnicastResponseBit;
12474
12475 if( i == 0 ) _Append( "\nQUESTION SECTION\n" );
12476
12477 _Append( "%s %2s %?2s%?2u %-5s\n",
12478 nameStr, inMDNS ? ( isQU ? "QU" : "QM" ) : "",
12479 ( qclass == kDNSServiceClass_IN ), "IN", ( qclass != kDNSServiceClass_IN ), qclass, RecordTypeToString( qtype ) );
12480 }
12481
12482 totalRRCount = answerCount + authorityCount + additionalCount;
12483 for( i = 0; i < totalRRCount; ++i )
12484 {
12485 uint16_t type;
12486 uint16_t class;
12487 uint32_t ttl;
12488 const uint8_t * rdataPtr;
12489 size_t rdataLen;
12490 char * rdataStr;
12491 Boolean cacheFlush;
12492 uint8_t name[ kDomainNameLengthMax ];
12493
12494 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr );
12495 require_noerr( err, exit );
12496
12497 err = DomainNameToString( name, NULL, nameStr, NULL );
12498 require_noerr( err, exit );
12499
12500 cacheFlush = ( inMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
12501 if( inMDNS ) class &= ~kRRClassCacheFlushBit;
12502
12503 rdataStr = NULL;
12504 if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr );
12505 if( !rdataStr )
12506 {
12507 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) rdataLen, INT_MAX );
12508 require_action( rdataStr, exit, err = kNoMemoryErr );
12509 }
12510
12511 if( answerCount && ( i == 0 ) ) _Append( "\nANSWER SECTION\n" );
12512 else if( authorityCount && ( i == answerCount ) ) _Append( "\nAUTHORITY SECTION\n" );
12513 else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) _Append( "\nADDITIONAL SECTION\n" );
12514
12515 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
12516 nameStr, ttl, cacheFlush ? "CF" : "",
12517 ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class,
12518 RecordTypeToString( type ), rdataStr );
12519 free( rdataStr );
12520 }
12521 _Append( "\n" );
12522
12523 err = DataBuffer_Append( &dataBuf, "", 1 );
12524 require_noerr( err, exit );
12525
12526 err = DataBuffer_Detach( &dataBuf, (uint8_t **) outText, &len );
12527 require_noerr( err, exit );
12528
12529 exit:
12530 DataBuffer_Free( &dataBuf );
12531 return( err );
12532 }
12533
12534 //===========================================================================================================================
12535 // WriteDNSQueryMessage
12536 //===========================================================================================================================
12537
12538 static OSStatus
12539 WriteDNSQueryMessage(
12540 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
12541 uint16_t inMsgID,
12542 uint16_t inFlags,
12543 const char * inQName,
12544 uint16_t inQType,
12545 uint16_t inQClass,
12546 size_t * outMsgLen )
12547 {
12548 OSStatus err;
12549 DNSHeader * const hdr = (DNSHeader *) inMsg;
12550 uint8_t * ptr;
12551 size_t msgLen;
12552
12553 memset( hdr, 0, sizeof( *hdr ) );
12554 DNSHeaderSetID( hdr, inMsgID );
12555 DNSHeaderSetFlags( hdr, inFlags );
12556 DNSHeaderSetQuestionCount( hdr, 1 );
12557
12558 ptr = (uint8_t *)( hdr + 1 );
12559 err = DomainNameFromString( ptr, inQName, &ptr );
12560 require_noerr_quiet( err, exit );
12561
12562 DNSQuestionFixedFieldsInit( (DNSQuestionFixedFields *) ptr, inQType, inQClass );
12563 ptr += 4;
12564
12565 msgLen = (size_t)( ptr - inMsg );
12566 check( msgLen <= kDNSQueryMessageMaxLen );
12567
12568 if( outMsgLen ) *outMsgLen = msgLen;
12569
12570 exit:
12571 return( err );
12572 }
12573
12574 //===========================================================================================================================
12575 // DispatchSignalSourceCreate
12576 //===========================================================================================================================
12577
12578 static OSStatus
12579 DispatchSignalSourceCreate(
12580 int inSignal,
12581 DispatchHandler inEventHandler,
12582 void * inContext,
12583 dispatch_source_t * outSource )
12584 {
12585 OSStatus err;
12586 dispatch_source_t source;
12587
12588 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, dispatch_get_main_queue() );
12589 require_action( source, exit, err = kUnknownErr );
12590
12591 dispatch_set_context( source, inContext );
12592 dispatch_source_set_event_handler_f( source, inEventHandler );
12593
12594 *outSource = source;
12595 err = kNoErr;
12596
12597 exit:
12598 return( err );
12599 }
12600
12601 //===========================================================================================================================
12602 // DispatchSocketSourceCreate
12603 //===========================================================================================================================
12604
12605 static OSStatus
12606 DispatchSocketSourceCreate(
12607 SocketRef inSock,
12608 dispatch_source_type_t inType,
12609 dispatch_queue_t inQueue,
12610 DispatchHandler inEventHandler,
12611 DispatchHandler inCancelHandler,
12612 void * inContext,
12613 dispatch_source_t * outSource )
12614 {
12615 OSStatus err;
12616 dispatch_source_t source;
12617
12618 source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() );
12619 require_action( source, exit, err = kUnknownErr );
12620
12621 dispatch_set_context( source, inContext );
12622 dispatch_source_set_event_handler_f( source, inEventHandler );
12623 dispatch_source_set_cancel_handler_f( source, inCancelHandler );
12624
12625 *outSource = source;
12626 err = kNoErr;
12627
12628 exit:
12629 return( err );
12630 }
12631
12632 //===========================================================================================================================
12633 // DispatchTimerCreate
12634 //===========================================================================================================================
12635
12636 static OSStatus
12637 DispatchTimerCreate(
12638 dispatch_time_t inStart,
12639 uint64_t inIntervalNs,
12640 uint64_t inLeewayNs,
12641 dispatch_queue_t inQueue,
12642 DispatchHandler inEventHandler,
12643 DispatchHandler inCancelHandler,
12644 void * inContext,
12645 dispatch_source_t * outTimer )
12646 {
12647 OSStatus err;
12648 dispatch_source_t timer;
12649
12650 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() );
12651 require_action( timer, exit, err = kUnknownErr );
12652
12653 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
12654 dispatch_set_context( timer, inContext );
12655 dispatch_source_set_event_handler_f( timer, inEventHandler );
12656 dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
12657
12658 *outTimer = timer;
12659 err = kNoErr;
12660
12661 exit:
12662 return( err );
12663 }
12664
12665 //===========================================================================================================================
12666 // DispatchProcessMonitorCreate
12667 //===========================================================================================================================
12668
12669 static OSStatus
12670 DispatchProcessMonitorCreate(
12671 pid_t inPID,
12672 unsigned long inFlags,
12673 dispatch_queue_t inQueue,
12674 DispatchHandler inEventHandler,
12675 DispatchHandler inCancelHandler,
12676 void * inContext,
12677 dispatch_source_t * outMonitor )
12678 {
12679 OSStatus err;
12680 dispatch_source_t monitor;
12681
12682 monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags,
12683 inQueue ? inQueue : dispatch_get_main_queue() );
12684 require_action( monitor, exit, err = kUnknownErr );
12685
12686 dispatch_set_context( monitor, inContext );
12687 dispatch_source_set_event_handler_f( monitor, inEventHandler );
12688 dispatch_source_set_cancel_handler_f( monitor, inCancelHandler );
12689
12690 *outMonitor = monitor;
12691 err = kNoErr;
12692
12693 exit:
12694 return( err );
12695 }
12696
12697 //===========================================================================================================================
12698 // ServiceTypeDescription
12699 //===========================================================================================================================
12700
12701 typedef struct
12702 {
12703 const char * name; // Name of the service type in two-label "_service._proto" format.
12704 const char * description; // Description of the service type.
12705
12706 } ServiceType;
12707
12708 // A Non-comprehensive table of DNS-SD service types
12709
12710 static const ServiceType kServiceTypes[] =
12711 {
12712 { "_acp-sync._tcp", "AirPort Base Station Sync" },
12713 { "_adisk._tcp", "Automatic Disk Discovery" },
12714 { "_afpovertcp._tcp", "Apple File Sharing" },
12715 { "_airdrop._tcp", "AirDrop" },
12716 { "_airplay._tcp", "AirPlay" },
12717 { "_airport._tcp", "AirPort Base Station" },
12718 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
12719 { "_eppc._tcp", "Remote AppleEvents" },
12720 { "_ftp._tcp", "File Transfer Protocol" },
12721 { "_home-sharing._tcp", "Home Sharing" },
12722 { "_homekit._tcp", "HomeKit" },
12723 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
12724 { "_https._tcp", "HTTP over SSL/TLS" },
12725 { "_ipp._tcp", "Internet Printing Protocol" },
12726 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
12727 { "_mediaremotetv._tcp", "Media Remote" },
12728 { "_net-assistant._tcp", "Apple Remote Desktop" },
12729 { "_od-master._tcp", "OpenDirectory Master" },
12730 { "_nfs._tcp", "Network File System" },
12731 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
12732 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
12733 { "_raop._tcp", "Remote Audio Output Protocol" },
12734 { "_rfb._tcp", "Remote Frame Buffer" },
12735 { "_scanner._tcp", "Bonjour Scanning" },
12736 { "_smb._tcp", "Server Message Block over TCP/IP" },
12737 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
12738 { "_sleep-proxy._udp", "Sleep Proxy Server" },
12739 { "_ssh._tcp", "SSH Remote Login Protocol" },
12740 { "_teleport._tcp", "teleport" },
12741 { "_tftp._tcp", "Trivial File Transfer Protocol" },
12742 { "_workstation._tcp", "Workgroup Manager" },
12743 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
12744 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
12745 };
12746
12747 static const char * ServiceTypeDescription( const char *inName )
12748 {
12749 const ServiceType * serviceType;
12750 const ServiceType * const end = kServiceTypes + countof( kServiceTypes );
12751
12752 for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
12753 {
12754 if( strcasecmp( inName, serviceType->name ) == 0 ) return( serviceType->description );
12755 }
12756 return( NULL );
12757 }
12758
12759 //===========================================================================================================================
12760 // SocketContextCreate
12761 //===========================================================================================================================
12762
12763 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext )
12764 {
12765 OSStatus err;
12766 SocketContext * context;
12767
12768 context = (SocketContext *) calloc( 1, sizeof( *context ) );
12769 require_action( context, exit, err = kNoMemoryErr );
12770
12771 context->refCount = 1;
12772 context->sock = inSock;
12773 context->userContext = inUserContext;
12774
12775 *outContext = context;
12776 err = kNoErr;
12777
12778 exit:
12779 return( err );
12780 }
12781
12782 //===========================================================================================================================
12783 // SocketContextRetain
12784 //===========================================================================================================================
12785
12786 static SocketContext * SocketContextRetain( SocketContext *inContext )
12787 {
12788 ++inContext->refCount;
12789 return( inContext );
12790 }
12791
12792 //===========================================================================================================================
12793 // SocketContextRelease
12794 //===========================================================================================================================
12795
12796 static void SocketContextRelease( SocketContext *inContext )
12797 {
12798 if( --inContext->refCount == 0 )
12799 {
12800 ForgetSocket( &inContext->sock );
12801 free( inContext );
12802 }
12803 }
12804
12805 //===========================================================================================================================
12806 // SocketContextCancelHandler
12807 //===========================================================================================================================
12808
12809 static void SocketContextCancelHandler( void *inContext )
12810 {
12811 SocketContextRelease( (SocketContext *) inContext );
12812 }
12813
12814 //===========================================================================================================================
12815 // StringToInt32
12816 //===========================================================================================================================
12817
12818 static OSStatus StringToInt32( const char *inString, int32_t *outValue )
12819 {
12820 OSStatus err;
12821 long value;
12822 char * endPtr;
12823
12824 value = strtol( inString, &endPtr, 0 );
12825 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
12826 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
12827
12828 *outValue = (int32_t) value;
12829 err = kNoErr;
12830
12831 exit:
12832 return( err );
12833 }
12834
12835 //===========================================================================================================================
12836 // StringToUInt32
12837 //===========================================================================================================================
12838
12839 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue )
12840 {
12841 OSStatus err;
12842 uint32_t value;
12843 char * endPtr;
12844
12845 value = (uint32_t) strtol( inString, &endPtr, 0 );
12846 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
12847
12848 *outValue = value;
12849 err = kNoErr;
12850
12851 exit:
12852 return( err );
12853 }
12854
12855 //===========================================================================================================================
12856 // StringToLongLong
12857 //===========================================================================================================================
12858
12859 static OSStatus StringToLongLong( const char *inString, long long *outValue )
12860 {
12861 OSStatus err;
12862 long long value;
12863 char * endPtr;
12864
12865 set_errno_compat( 0 );
12866 value = strtol( inString, &endPtr, 0 );
12867 err = errno_compat();
12868 if( ( ( value == LLONG_MIN ) || ( value == LLONG_MAX ) ) && ( err == ERANGE ) ) goto exit;
12869 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
12870
12871 *outValue = value;
12872 err = kNoErr;
12873
12874 exit:
12875 return( err );
12876 }
12877
12878 //===========================================================================================================================
12879 // StringToARecordData
12880 //===========================================================================================================================
12881
12882 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
12883 {
12884 OSStatus err;
12885 uint32_t * addrPtr;
12886 const size_t addrLen = sizeof( *addrPtr );
12887 const char * end;
12888
12889 addrPtr = (uint32_t *) malloc( addrLen );
12890 require_action( addrPtr, exit, err = kNoMemoryErr );
12891
12892 err = StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr,
12893 NULL, NULL, NULL, &end );
12894 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
12895 require_noerr_quiet( err, exit );
12896
12897 *addrPtr = HostToBig32( *addrPtr );
12898
12899 *outPtr = (uint8_t *) addrPtr;
12900 addrPtr = NULL;
12901 *outLen = addrLen;
12902
12903 exit:
12904 FreeNullSafe( addrPtr );
12905 return( err );
12906 }
12907
12908 //===========================================================================================================================
12909 // StringToAAAARecordData
12910 //===========================================================================================================================
12911
12912 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
12913 {
12914 OSStatus err;
12915 uint8_t * addrPtr;
12916 const size_t addrLen = 16;
12917 const char * end;
12918
12919 addrPtr = (uint8_t *) malloc( addrLen );
12920 require_action( addrPtr, exit, err = kNoMemoryErr );
12921
12922 err = StringToIPv6Address( inString,
12923 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
12924 addrPtr, NULL, NULL, NULL, &end );
12925 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
12926 require_noerr_quiet( err, exit );
12927
12928 *outPtr = addrPtr;
12929 addrPtr = NULL;
12930 *outLen = addrLen;
12931
12932 exit:
12933 FreeNullSafe( addrPtr );
12934 return( err );
12935 }
12936
12937 //===========================================================================================================================
12938 // StringToDomainName
12939 //===========================================================================================================================
12940
12941 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen )
12942 {
12943 OSStatus err;
12944 uint8_t * namePtr;
12945 size_t nameLen;
12946 uint8_t * end;
12947 uint8_t nameBuf[ kDomainNameLengthMax ];
12948
12949 err = DomainNameFromString( nameBuf, inString, &end );
12950 require_noerr_quiet( err, exit );
12951
12952 nameLen = (size_t)( end - nameBuf );
12953 namePtr = memdup( nameBuf, nameLen );
12954 require_action( namePtr, exit, err = kNoMemoryErr );
12955
12956 *outPtr = namePtr;
12957 namePtr = NULL;
12958 if( outLen ) *outLen = nameLen;
12959
12960 exit:
12961 return( err );
12962 }
12963
12964 #if( TARGET_OS_DARWIN )
12965 //===========================================================================================================================
12966 // GetDefaultDNSServer
12967 //===========================================================================================================================
12968
12969 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr )
12970 {
12971 OSStatus err;
12972 dns_config_t * config;
12973 struct sockaddr * addr;
12974 int32_t i;
12975
12976 config = dns_configuration_copy();
12977 require_action( config, exit, err = kUnknownErr );
12978
12979 addr = NULL;
12980 for( i = 0; i < config->n_resolver; ++i )
12981 {
12982 const dns_resolver_t * const resolver = config->resolver[ i ];
12983
12984 if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
12985 {
12986 addr = resolver->nameserver[ 0 ];
12987 break;
12988 }
12989 }
12990 require_action_quiet( addr, exit, err = kNotFoundErr );
12991
12992 SockAddrCopy( addr, outAddr );
12993 err = kNoErr;
12994
12995 exit:
12996 if( config ) dns_configuration_free( config );
12997 return( err );
12998 }
12999 #endif
13000
13001 //===========================================================================================================================
13002 // GetCurrentMicroTime
13003 //===========================================================================================================================
13004
13005 static MicroTime64 GetCurrentMicroTime( void )
13006 {
13007 struct timeval now;
13008
13009 TIMEVAL_ZERO( now );
13010 gettimeofday( &now, NULL );
13011
13012 return( (MicroTime64) TIMEVAL_USEC64( now ) );
13013 }
13014
13015 //===========================================================================================================================
13016 // SocketWriteAll
13017 //
13018 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
13019 //===========================================================================================================================
13020
13021 OSStatus SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
13022 {
13023 OSStatus err;
13024 const uint8_t * src;
13025 const uint8_t * end;
13026 fd_set writeSet;
13027 struct timeval timeout;
13028 ssize_t n;
13029
13030 FD_ZERO( &writeSet );
13031 src = (const uint8_t *) inData;
13032 end = src + inSize;
13033 while( src < end )
13034 {
13035 FD_SET( inSock, &writeSet );
13036 timeout.tv_sec = inTimeoutSecs;
13037 timeout.tv_usec = 0;
13038 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
13039 if( n == 0 ) { err = kTimeoutErr; goto exit; }
13040 err = map_socket_value_errno( inSock, n > 0, n );
13041 require_noerr( err, exit );
13042
13043 n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
13044 err = map_socket_value_errno( inSock, n >= 0, n );
13045 if( err == EINTR ) continue;
13046 require_noerr( err, exit );
13047
13048 src += n;
13049 }
13050 err = kNoErr;
13051
13052 exit:
13053 return( err );
13054 }
13055
13056 //===========================================================================================================================
13057 // ParseIPv4Address
13058 //
13059 // Warning: "inBuffer" may be modified even in error cases.
13060 //
13061 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
13062 //===========================================================================================================================
13063
13064 static OSStatus ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
13065 {
13066 OSStatus err;
13067 uint8_t * dst;
13068 int segments;
13069 int sawDigit;
13070 int c;
13071 int v;
13072
13073 check( inBuffer );
13074 check( outStr );
13075
13076 dst = inBuffer;
13077 *dst = 0;
13078 sawDigit = 0;
13079 segments = 0;
13080 for( ; ( c = *inStr ) != '\0'; ++inStr )
13081 {
13082 if( isdigit_safe( c ) )
13083 {
13084 v = ( *dst * 10 ) + ( c - '0' );
13085 require_action_quiet( v <= 255, exit, err = kRangeErr );
13086 *dst = (uint8_t) v;
13087 if( !sawDigit )
13088 {
13089 ++segments;
13090 require_action_quiet( segments <= 4, exit, err = kOverrunErr );
13091 sawDigit = 1;
13092 }
13093 }
13094 else if( ( c == '.' ) && sawDigit )
13095 {
13096 require_action_quiet( segments < 4, exit, err = kMalformedErr );
13097 *++dst = 0;
13098 sawDigit = 0;
13099 }
13100 else
13101 {
13102 break;
13103 }
13104 }
13105 require_action_quiet( segments == 4, exit, err = kUnderrunErr );
13106
13107 *outStr = inStr;
13108 err = kNoErr;
13109
13110 exit:
13111 return( err );
13112 }
13113
13114 //===========================================================================================================================
13115 // StringToIPv4Address
13116 //
13117 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
13118 //===========================================================================================================================
13119
13120 OSStatus
13121 StringToIPv4Address(
13122 const char * inStr,
13123 StringToIPAddressFlags inFlags,
13124 uint32_t * outIP,
13125 int * outPort,
13126 uint32_t * outSubnet,
13127 uint32_t * outRouter,
13128 const char ** outStr )
13129 {
13130 OSStatus err;
13131 uint8_t buf[ 4 ];
13132 int c;
13133 uint32_t ip;
13134 int hasPort;
13135 int port;
13136 int hasPrefix;
13137 int prefix;
13138 uint32_t subnetMask;
13139 uint32_t router;
13140
13141 require_action( inStr, exit, err = kParamErr );
13142
13143 // Parse the address-only part of the address (e.g. "1.2.3.4").
13144
13145 err = ParseIPv4Address( inStr, buf, &inStr );
13146 require_noerr_quiet( err, exit );
13147 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
13148 c = *inStr;
13149
13150 // Parse the port (if any).
13151
13152 hasPort = 0;
13153 port = 0;
13154 if( c == ':' )
13155 {
13156 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
13157 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
13158 require_action_quiet( port <= 65535, exit, err = kRangeErr );
13159 hasPort = 1;
13160 }
13161
13162 // Parse the prefix length (if any).
13163
13164 hasPrefix = 0;
13165 prefix = 0;
13166 subnetMask = 0;
13167 router = 0;
13168 if( c == '/' )
13169 {
13170 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
13171 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
13172 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
13173 hasPrefix = 1;
13174
13175 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
13176 router = ( ip & subnetMask ) | 1;
13177 }
13178
13179 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
13180
13181 if( outIP ) *outIP = ip;
13182 if( outPort && hasPort ) *outPort = port;
13183 if( outSubnet && hasPrefix ) *outSubnet = subnetMask;
13184 if( outRouter && hasPrefix ) *outRouter = router;
13185 if( outStr ) *outStr = inStr;
13186 err = kNoErr;
13187
13188 exit:
13189 return( err );
13190 }
13191
13192 //===========================================================================================================================
13193 // ParseIPv6Address
13194 //
13195 // Note: Parsed according to the rules specified in RFC 3513.
13196 // Warning: "inBuffer" may be modified even in error cases.
13197 //
13198 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
13199 //===========================================================================================================================
13200
13201 static OSStatus ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
13202 {
13203 // Table to map uppercase hex characters - '0' to their numeric values.
13204 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
13205 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 };
13206 OSStatus err;
13207 const char * ptr;
13208 uint8_t * dst;
13209 uint8_t * lim;
13210 uint8_t * colonPtr;
13211 int c;
13212 int sawDigit;
13213 unsigned int v;
13214 int i;
13215 int n;
13216
13217 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
13218
13219 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
13220
13221 // Special case leading :: (e.g. "::1") to simplify processing later.
13222
13223 if( *inStr == ':' )
13224 {
13225 ++inStr;
13226 require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
13227 }
13228
13229 // Parse the address.
13230
13231 ptr = inStr;
13232 dst = inBuffer;
13233 lim = dst + 16;
13234 colonPtr = NULL;
13235 sawDigit = 0;
13236 v = 0;
13237 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
13238 {
13239 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
13240 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
13241 {
13242 c -= '0';
13243 check( c < (int) countof( kASCIItoHexTable ) );
13244 v = ( v << 4 ) | kASCIItoHexTable[ c ];
13245 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
13246 sawDigit = 1;
13247 continue;
13248 }
13249 if( c == ':' )
13250 {
13251 ptr = inStr;
13252 if( !sawDigit )
13253 {
13254 require_action_quiet( !colonPtr, exit, err = kMalformedErr );
13255 colonPtr = dst;
13256 continue;
13257 }
13258 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
13259 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
13260 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
13261 *dst++ = (uint8_t)( v & 0xFF );
13262 sawDigit = 0;
13263 v = 0;
13264 continue;
13265 }
13266
13267 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
13268
13269 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
13270 {
13271 err = ParseIPv4Address( ptr, dst, &inStr );
13272 require_noerr_quiet( err, exit );
13273 dst += 4;
13274 sawDigit = 0;
13275 ++inStr; // Increment because the code below expects the end to be at "inStr - 1".
13276 }
13277 break;
13278 }
13279 if( sawDigit )
13280 {
13281 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
13282 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
13283 *dst++ = (uint8_t)( v & 0xFF );
13284 }
13285 check( dst <= lim );
13286 if( colonPtr )
13287 {
13288 require_action_quiet( dst < lim, exit, err = kOverrunErr );
13289 n = (int)( dst - colonPtr );
13290 for( i = 1; i <= n; ++i )
13291 {
13292 lim[ -i ] = colonPtr[ n - i ];
13293 colonPtr[ n - i ] = 0;
13294 }
13295 dst = lim;
13296 }
13297 require_action_quiet( dst == lim, exit, err = kUnderrunErr );
13298
13299 *outStr = inStr - 1;
13300 err = kNoErr;
13301
13302 exit:
13303 return( err );
13304 }
13305
13306 //===========================================================================================================================
13307 // ParseIPv6Scope
13308 //
13309 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
13310 //===========================================================================================================================
13311
13312 static OSStatus ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
13313 {
13314 #if( TARGET_OS_POSIX )
13315 OSStatus err;
13316 char scopeStr[ 64 ];
13317 char * dst;
13318 char * lim;
13319 int c;
13320 uint32_t scope;
13321 const char * ptr;
13322
13323 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
13324
13325 dst = scopeStr;
13326 lim = dst + ( countof( scopeStr ) - 1 );
13327 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
13328 {
13329 *dst++ = *inStr++;
13330 }
13331 *dst = '\0';
13332 check( dst <= lim );
13333
13334 // First try to map as a name and if that fails, treat it as a numeric scope.
13335
13336 scope = if_nametoindex( scopeStr );
13337 if( scope == 0 )
13338 {
13339 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
13340 {
13341 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
13342 }
13343 require_action_quiet( c == '\0', exit, err = kMalformedErr );
13344 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
13345 }
13346
13347 *outScope = scope;
13348 *outStr = inStr;
13349 err = kNoErr;
13350
13351 exit:
13352 return( err );
13353 #else
13354 OSStatus err;
13355 uint32_t scope;
13356 const char * start;
13357 int c;
13358
13359 scope = 0;
13360 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
13361 {
13362 scope = ( scope * 10 ) + ( c - '0' );
13363 }
13364 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
13365
13366 *outScope = scope;
13367 *outStr = inStr;
13368 err = kNoErr;
13369
13370 exit:
13371 return( err );
13372 #endif
13373 }
13374
13375 //===========================================================================================================================
13376 // StringToIPv6Address
13377 //
13378 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
13379 //===========================================================================================================================
13380
13381 OSStatus
13382 StringToIPv6Address(
13383 const char * inStr,
13384 StringToIPAddressFlags inFlags,
13385 uint8_t outIPv6[ 16 ],
13386 uint32_t * outScope,
13387 int * outPort,
13388 int * outPrefix,
13389 const char ** outStr )
13390 {
13391 OSStatus err;
13392 uint8_t ipv6[ 16 ];
13393 int c;
13394 int hasScope;
13395 uint32_t scope;
13396 int hasPort;
13397 int port;
13398 int hasPrefix;
13399 int prefix;
13400 int hasBracket;
13401 int i;
13402
13403 require_action( inStr, exit, err = kParamErr );
13404
13405 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
13406
13407 // Parse the address-only part of the address (e.g. "1::1").
13408
13409 err = ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
13410 require_noerr_quiet( err, exit );
13411 c = *inStr;
13412
13413 // Parse the scope, port, or prefix length.
13414
13415 hasScope = 0;
13416 scope = 0;
13417 hasPort = 0;
13418 port = 0;
13419 hasPrefix = 0;
13420 prefix = 0;
13421 hasBracket = 0;
13422 for( ;; )
13423 {
13424 if( c == '%' ) // Scope (e.g. "%en0" or "%5")
13425 {
13426 require_action_quiet( !hasScope, exit, err = kMalformedErr );
13427 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
13428 ++inStr;
13429 err = ParseIPv6Scope( inStr, &scope, &inStr );
13430 require_noerr_quiet( err, exit );
13431 hasScope = 1;
13432 c = *inStr;
13433 }
13434 else if( c == ':' ) // Port (e.g. ":80")
13435 {
13436 require_action_quiet( !hasPort, exit, err = kMalformedErr );
13437 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
13438 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
13439 require_action_quiet( port <= 65535, exit, err = kRangeErr );
13440 hasPort = 1;
13441 }
13442 else if( c == '/' ) // Prefix Length (e.g. "/64")
13443 {
13444 require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
13445 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
13446 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
13447 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
13448 hasPrefix = 1;
13449 }
13450 else if( c == ']' )
13451 {
13452 require_action_quiet( !hasBracket, exit, err = kMalformedErr );
13453 hasBracket = 1;
13454 c = *( ++inStr );
13455 }
13456 else
13457 {
13458 break;
13459 }
13460 }
13461
13462 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
13463
13464 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
13465 if( outScope && hasScope ) *outScope = scope;
13466 if( outPort && hasPort ) *outPort = port;
13467 if( outPrefix && hasPrefix ) *outPrefix = prefix;
13468 if( outStr ) *outStr = inStr;
13469 err = kNoErr;
13470
13471 exit:
13472 return( err );
13473 }
13474
13475 //===========================================================================================================================
13476 // StringArray_Free
13477 //
13478 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
13479 //===========================================================================================================================
13480
13481 void StringArray_Free( char **inArray, size_t inCount )
13482 {
13483 size_t i;
13484
13485 for( i = 0; i < inCount; ++i )
13486 {
13487 free( inArray[ i ] );
13488 }
13489 if( inCount > 0 ) free( inArray );
13490 }
13491
13492 //===========================================================================================================================
13493 // ParseQuotedEscapedString
13494 //
13495 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
13496 //===========================================================================================================================
13497
13498 Boolean
13499 ParseQuotedEscapedString(
13500 const char * inSrc,
13501 const char * inEnd,
13502 const char * inDelimiters,
13503 char * inBuf,
13504 size_t inMaxLen,
13505 size_t * outCopiedLen,
13506 size_t * outTotalLen,
13507 const char ** outSrc )
13508 {
13509 const unsigned char * src;
13510 const unsigned char * end;
13511 unsigned char * dst;
13512 unsigned char * lim;
13513 unsigned char c;
13514 unsigned char c2;
13515 size_t totalLen;
13516 Boolean singleQuote;
13517 Boolean doubleQuote;
13518
13519 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
13520 src = (const unsigned char *) inSrc;
13521 end = (const unsigned char *) inEnd;
13522 dst = (unsigned char *) inBuf;
13523 lim = dst + inMaxLen;
13524 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
13525 if( src >= end ) return( false );
13526
13527 // Parse each argument from the string.
13528 //
13529 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
13530
13531 totalLen = 0;
13532 singleQuote = false;
13533 doubleQuote = false;
13534 while( src < end )
13535 {
13536 c = *src++;
13537 if( singleQuote )
13538 {
13539 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
13540
13541 if( c == '\'' )
13542 {
13543 singleQuote = false;
13544 continue;
13545 }
13546 }
13547 else if( doubleQuote )
13548 {
13549 // Double quotes protect everything except double quotes and backslashes. A backslash can be
13550 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
13551 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
13552 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
13553 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
13554
13555 if( c == '"' )
13556 {
13557 doubleQuote = false;
13558 continue;
13559 }
13560 else if( c == '\\' )
13561 {
13562 if( src < end )
13563 {
13564 c2 = *src;
13565 if( ( c2 == '"' ) || ( c2 == '\\' ) )
13566 {
13567 ++src;
13568 c = c2;
13569 }
13570 else if( c2 == '\n' )
13571 {
13572 ++src;
13573 continue;
13574 }
13575 else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
13576 {
13577 ++src;
13578 c = c2;
13579 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
13580 {
13581 c = HexPairToByte( src );
13582 src += 2;
13583 }
13584 }
13585 else if( isoctal_safe( c2 ) )
13586 {
13587 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
13588 {
13589 c = OctalTripleToByte( src );
13590 src += 3;
13591 }
13592 }
13593 }
13594 }
13595 }
13596 else if( strchr( inDelimiters, c ) )
13597 {
13598 break;
13599 }
13600 else if( c == '\\' )
13601 {
13602 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
13603 // A backslash followed by a newline disappears completely.
13604 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
13605 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
13606
13607 if( src < end )
13608 {
13609 c = *src;
13610 if( c == '\n' )
13611 {
13612 ++src;
13613 continue;
13614 }
13615 else if( ( c == 'x' ) || ( c == 'X' ) )
13616 {
13617 ++src;
13618 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
13619 {
13620 c = HexPairToByte( src );
13621 src += 2;
13622 }
13623 }
13624 else if( isoctal_safe( c ) )
13625 {
13626 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
13627 {
13628 c = OctalTripleToByte( src );
13629 src += 3;
13630 }
13631 else
13632 {
13633 ++src;
13634 }
13635 }
13636 else
13637 {
13638 ++src;
13639 }
13640 }
13641 }
13642 else if( c == '\'' )
13643 {
13644 singleQuote = true;
13645 continue;
13646 }
13647 else if( c == '"' )
13648 {
13649 doubleQuote = true;
13650 continue;
13651 }
13652
13653 if( dst < lim )
13654 {
13655 if( inBuf ) *dst = c;
13656 ++dst;
13657 }
13658 ++totalLen;
13659 }
13660
13661 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) );
13662 if( outTotalLen ) *outTotalLen = totalLen;
13663 if( outSrc ) *outSrc = (const char *) src;
13664 return( true );
13665 }
13666
13667 //===========================================================================================================================
13668 // _ServerSocketOpenEx2
13669 //
13670 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
13671 //===========================================================================================================================
13672
13673 static OSStatus
13674 _ServerSocketOpenEx2(
13675 int inFamily,
13676 int inType,
13677 int inProtocol,
13678 const void * inAddr,
13679 int inPort,
13680 int * outPort,
13681 int inRcvBufSize,
13682 Boolean inNoPortReuse,
13683 SocketRef * outSock )
13684 {
13685 OSStatus err;
13686 int port;
13687 SocketRef sock;
13688 int name;
13689 int option;
13690 sockaddr_ip sip;
13691 socklen_t len;
13692
13693 port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic".
13694
13695 sock = socket( inFamily, inType, inProtocol );
13696 err = map_socket_creation_errno( sock );
13697 require_noerr_quiet( err, exit );
13698
13699 #if( defined( SO_NOSIGPIPE ) )
13700 setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
13701 #endif
13702
13703 err = SocketMakeNonBlocking( sock );
13704 require_noerr( err, exit );
13705
13706 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
13707 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
13708 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
13709 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
13710
13711 err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize );
13712 check_noerr( err );
13713
13714 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
13715
13716 if( ( inType != SOCK_DGRAM ) || !inNoPortReuse )
13717 {
13718 option = 1;
13719 name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR;
13720 err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) );
13721 err = map_socket_noerr_errno( sock, err );
13722 require_noerr( err, exit );
13723 }
13724
13725 if( inFamily == AF_INET )
13726 {
13727 // Bind to the port. If it fails, retry with a dynamic port.
13728
13729 memset( &sip.v4, 0, sizeof( sip.v4 ) );
13730 SIN_LEN_SET( &sip.v4 );
13731 sip.v4.sin_family = AF_INET;
13732 sip.v4.sin_port = htons( (uint16_t) port );
13733 sip.v4.sin_addr.s_addr = inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY );
13734 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
13735 err = map_socket_noerr_errno( sock, err );
13736 if( err && ( inPort < 0 ) )
13737 {
13738 sip.v4.sin_port = 0;
13739 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
13740 err = map_socket_noerr_errno( sock, err );
13741 }
13742 require_noerr( err, exit );
13743 }
13744 #if( defined( AF_INET6 ) )
13745 else if( inFamily == AF_INET6 )
13746 {
13747 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
13748
13749 option = 1;
13750 err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) );
13751 err = map_socket_noerr_errno( sock, err );
13752 require_noerr( err, exit );
13753
13754 // Bind to the port. If it fails, retry with a dynamic port.
13755
13756 memset( &sip.v6, 0, sizeof( sip.v6 ) );
13757 SIN6_LEN_SET( &sip.v6 );
13758 sip.v6.sin6_family = AF_INET6;
13759 sip.v6.sin6_port = htons( (uint16_t) port );
13760 sip.v6.sin6_addr = inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any;
13761 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
13762 err = map_socket_noerr_errno( sock, err );
13763 if( err && ( inPort < 0 ) )
13764 {
13765 sip.v6.sin6_port = 0;
13766 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
13767 err = map_socket_noerr_errno( sock, err );
13768 }
13769 require_noerr( err, exit );
13770 }
13771 #endif
13772 else
13773 {
13774 dlogassert( "Unsupported family: %d", inFamily );
13775 err = kUnsupportedErr;
13776 goto exit;
13777 }
13778
13779 if( inType == SOCK_STREAM )
13780 {
13781 err = listen( sock, SOMAXCONN );
13782 err = map_socket_noerr_errno( sock, err );
13783 if( err )
13784 {
13785 err = listen( sock, 5 );
13786 err = map_socket_noerr_errno( sock, err );
13787 require_noerr( err, exit );
13788 }
13789 }
13790
13791 if( outPort )
13792 {
13793 len = (socklen_t) sizeof( sip );
13794 err = getsockname( sock, &sip.sa, &len );
13795 err = map_socket_noerr_errno( sock, err );
13796 require_noerr( err, exit );
13797
13798 *outPort = SockAddrGetPort( &sip );
13799 }
13800 *outSock = sock;
13801 sock = kInvalidSocketRef;
13802
13803 exit:
13804 ForgetSocket( &sock );
13805 return( err );
13806 }
13807
13808 //===========================================================================================================================
13809 // memdup
13810 //
13811 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
13812 //===========================================================================================================================
13813
13814 void * memdup( const void *inPtr, size_t inLen )
13815 {
13816 void * mem;
13817
13818 mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
13819 require( mem, exit );
13820 if( inLen > 0 ) memcpy( mem, inPtr, inLen );
13821
13822 exit:
13823 return( mem );
13824 }
13825
13826 #if( !TARGET_OS_WINDOWS )
13827 //===========================================================================================================================
13828 // memicmp
13829 //
13830 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
13831 //===========================================================================================================================
13832
13833 int memicmp( const void *inP1, const void *inP2, size_t inLen )
13834 {
13835 const unsigned char * p1;
13836 const unsigned char * e1;
13837 const unsigned char * p2;
13838 int c1;
13839 int c2;
13840
13841 p1 = (const unsigned char *) inP1;
13842 e1 = p1 + inLen;
13843 p2 = (const unsigned char *) inP2;
13844 while( p1 < e1 )
13845 {
13846 c1 = *p1++;
13847 c2 = *p2++;
13848 c1 = tolower( c1 );
13849 c2 = tolower( c2 );
13850 if( c1 < c2 ) return( -1 );
13851 if( c1 > c2 ) return( 1 );
13852 }
13853 return( 0 );
13854 }
13855 #endif