1/* 2 Copyright (c) 2016-2017 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/CommandLineUtils.h> 10#include <CoreUtils/DataBufferUtils.h> 11#include <CoreUtils/DebugServices.h> 12#include <CoreUtils/MiscUtils.h> 13#include <CoreUtils/NetUtils.h> 14#include <CoreUtils/PrintFUtils.h> 15#include <CoreUtils/RandomNumberUtils.h> 16#include <CoreUtils/StringUtils.h> 17#include <CoreUtils/TickUtils.h> 18#include <dns_sd.h> 19#include <dns_sd_private.h> 20 21#if( TARGET_OS_DARWIN ) 22 #include <dnsinfo.h> 23 #include <libproc.h> 24 #include <sys/proc_info.h> 25#endif 26 27#if( TARGET_OS_POSIX ) 28 #include <sys/resource.h> 29#endif 30 31#if( DNSSDUTIL_INCLUDE_DNSCRYPT ) 32 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>. 33#endif 34 35//=========================================================================================================================== 36// Global Constants 37//=========================================================================================================================== 38 39// Versioning 40 41#define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 ) 42 43#if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) ) 44 #define DNSSDUTIL_SOURCE_VERSION "0.0.0" 45#endif 46 47// DNS-SD API flag descriptors 48 49#define kDNSServiceFlagsDescriptors \ 50 "\x00" "AutoTrigger\0" \ 51 "\x01" "Add\0" \ 52 "\x02" "Default\0" \ 53 "\x03" "NoAutoRename\0" \ 54 "\x04" "Shared\0" \ 55 "\x05" "Unique\0" \ 56 "\x06" "BrowseDomains\0" \ 57 "\x07" "RegistrationDomains\0" \ 58 "\x08" "LongLivedQuery\0" \ 59 "\x09" "AllowRemoteQuery\0" \ 60 "\x0A" "ForceMulticast\0" \ 61 "\x0B" "KnownUnique\0" \ 62 "\x0C" "ReturnIntermediates\0" \ 63 "\x0D" "NonBrowsable\0" \ 64 "\x0E" "ShareConnection\0" \ 65 "\x0F" "SuppressUnusable\0" \ 66 "\x10" "Timeout\0" \ 67 "\x11" "IncludeP2P\0" \ 68 "\x12" "WakeOnResolve\0" \ 69 "\x13" "BackgroundTrafficClass\0" \ 70 "\x14" "IncludeAWDL\0" \ 71 "\x15" "Validate\0" \ 72 "\x16" "UnicastResponse\0" \ 73 "\x17" "ValidateOptional\0" \ 74 "\x18" "WakeOnlyService\0" \ 75 "\x19" "ThresholdOne\0" \ 76 "\x1A" "ThresholdFinder\0" \ 77 "\x1B" "DenyCellular\0" \ 78 "\x1C" "ServiceIndex\0" \ 79 "\x1D" "DenyExpensive\0" \ 80 "\x1E" "PathEvaluationDone\0" \ 81 "\x00" 82 83#define kDNSServiceProtocolDescriptors \ 84 "\x00" "IPv4\0" \ 85 "\x01" "IPv6\0" \ 86 "\x00" 87 88// (m)DNS 89 90#define kDNSHeaderFlag_Response ( 1 << 15 ) 91#define kDNSHeaderFlag_AuthAnswer ( 1 << 10 ) 92#define kDNSHeaderFlag_Truncation ( 1 << 9 ) 93#define kDNSHeaderFlag_RecursionDesired ( 1 << 8 ) 94#define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 ) 95 96#define kDNSOpCode_Query 0 97#define kDNSOpCode_InverseQuery 1 98#define kDNSOpCode_Status 2 99#define kDNSOpCode_Notify 4 100#define kDNSOpCode_Update 5 101 102#define kDNSRCode_NoError 0 103#define kDNSRCode_FormatError 1 104#define kDNSRCode_ServerFailure 2 105#define kDNSRCode_NXDomain 3 106#define kDNSRCode_NotImplemented 4 107#define kDNSRCode_Refused 5 108 109#define kQClassUnicastResponseBit ( 1U << 15 ) 110#define kRRClassCacheFlushBit ( 1U << 15 ) 111 112#define kDomainLabelLengthMax 63 113#define kDomainNameLengthMax 256 114 115//=========================================================================================================================== 116// Gerneral Command Options 117//=========================================================================================================================== 118 119// Command option macros 120 121#define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \ 122 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \ 123 (SHORT_HELP), NULL ) 124 125#define kRequiredOptionSuffix " [REQUIRED]" 126 127#define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \ 128 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \ 129 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \ 130 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP ) 131 132#define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \ 133 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL ) 134 135#define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \ 136 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \ 137 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \ 138 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL ) 139 140#define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \ 141 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL ) 142 143#define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \ 144 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \ 145 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \ 146 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP ) 147 148#define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \ 149 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL ) 150 151// DNS-SD API flag options 152 153static int gDNSSDFlags = 0; 154static int gDNSSDFlag_BrowseDomains = false; 155static int gDNSSDFlag_DenyCellular = false; 156static int gDNSSDFlag_DenyExpensive = false; 157static int gDNSSDFlag_ForceMulticast = false; 158static int gDNSSDFlag_IncludeAWDL = false; 159static int gDNSSDFlag_NoAutoRename = false; 160static int gDNSSDFlag_PathEvaluationDone = false; 161static int gDNSSDFlag_RegistrationDomains = false; 162static int gDNSSDFlag_ReturnIntermediates = false; 163static int gDNSSDFlag_Shared = false; 164static int gDNSSDFlag_SuppressUnusable = false; 165static int gDNSSDFlag_Timeout = false; 166static int gDNSSDFlag_UnicastResponse = false; 167static int gDNSSDFlag_Unique = false; 168 169#define DNSSDFlagsOption() \ 170 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \ 171 "DNSServiceFlags to use. This value is bitwise ORed with other single flag options.", false ) 172 173#define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \ 174 BooleanOption( SHORT_CHAR, Stringify( FLAG_NAME ), &gDNSSDFlag_ ## FLAG_NAME, \ 175 "Use kDNSServiceFlags" Stringify( FLAG_NAME ) "." ) 176 177#define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular ) 178#define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive ) 179#define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast ) 180#define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL ) 181#define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename ) 182#define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone ) 183#define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates ) 184#define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared ) 185#define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable ) 186#define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout ) 187#define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse ) 188#define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique ) 189 190// Interface option 191 192static const char * gInterface = NULL; 193 194#define InterfaceOption() \ 195 StringOption( 'i', "interface", &gInterface, "interface", \ 196 "Network interface by name or index. Use index -1 for local-only.", false ) 197 198// Connection options 199 200#define kConnectionArg_Normal "" 201#define kConnectionArgPrefix_PID "pid:" 202#define kConnectionArgPrefix_UUID "uuid:" 203 204static const char * gConnectionOpt = kConnectionArg_Normal; 205 206#define ConnectionOptions() \ 207 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \ 208 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \ 209 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL } 210 211#define kConnectionSection_Name "Connection Option" 212#define kConnectionSection_Text \ 213 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \ 214 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \ 215 "specifying the connection option without an argument, i.e.,\n" \ 216 "\n" \ 217 " --connection\n" \ 218 "\n" \ 219 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \ 220 "\n" \ 221 " --connection=pid:<PID>\n" \ 222 "\n" \ 223 "to specify the delegator by PID, or use\n" \ 224 "\n" \ 225 " --connection=uuid:<UUID>\n" \ 226 "\n" \ 227 "to specify the delegator by UUID.\n" \ 228 "\n" \ 229 "To not use a main connection at all, but instead perform operations on their own connections, use\n" \ 230 "\n" \ 231 " --no-connection\n" 232 233#define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text ) 234 235// Help text for record data options 236 237#define kRDataArgPrefix_File "file:" 238#define kRDataArgPrefix_HexString "hex:" 239#define kRDataArgPrefix_String "string:" 240#define kRDataArgPrefix_TXT "txt:" 241 242#define kRecordDataSection_Name "Record Data Arguments" 243#define kRecordDataSection_Text \ 244 "A record data argument is specified in one of the following formats:\n" \ 245 "\n" \ 246 "Format Syntax Example\n" \ 247 "String string:<string> string:'\\x09color=red'\n" \ 248 "Hexadecimal string hex:<hex string> hex:c0a80101 or hex:'C0 A8 01 01'\n" \ 249 "TXT record keys and values txt:<comma-delimited keys and values> txt:'key1=x,key2=y\\,z,key3'\n" \ 250 "File containing raw record data file:<file path> file:dir/record_data.bin\n" 251 252#define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text ) 253 254//=========================================================================================================================== 255// Browse Command Options 256//=========================================================================================================================== 257 258static char ** gBrowse_ServiceTypes = NULL; 259static size_t gBrowse_ServiceTypesCount = 0; 260static const char * gBrowse_Domain = NULL; 261static int gBrowse_DoResolve = false; 262static int gBrowse_QueryTXT = false; 263static int gBrowse_TimeLimitSecs = 0; 264 265static CLIOption kBrowseOpts[] = 266{ 267 InterfaceOption(), 268 MultiStringOption( 't', "type", &gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ), 269 StringOption( 'd', "domain", &gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ), 270 271 CLI_OPTION_GROUP( "Flags" ), 272 DNSSDFlagsOption(), 273 DNSSDFlagsOption_IncludeAWDL(), 274 275 CLI_OPTION_GROUP( "Operation" ), 276 ConnectionOptions(), 277 BooleanOption( 0 , "resolve", &gBrowse_DoResolve, "Resolve service instances." ), 278 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT, "Query TXT records of service instances." ), 279 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ), 280 281 ConnectionSection(), 282 CLI_OPTION_END() 283}; 284 285//=========================================================================================================================== 286// GetAddrInfo Command Options 287//=========================================================================================================================== 288 289static const char * gGetAddrInfo_Name = NULL; 290static int gGetAddrInfo_ProtocolIPv4 = false; 291static int gGetAddrInfo_ProtocolIPv6 = false; 292static int gGetAddrInfo_OneShot = false; 293static int gGetAddrInfo_TimeLimitSecs = 0; 294 295static CLIOption kGetAddrInfoOpts[] = 296{ 297 InterfaceOption(), 298 StringOption( 'n', "name", &gGetAddrInfo_Name, "domain name", "Domain name to resolve.", true ), 299 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ), 300 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ), 301 302 CLI_OPTION_GROUP( "Flags" ), 303 DNSSDFlagsOption(), 304 DNSSDFlagsOption_DenyCellular(), 305 DNSSDFlagsOption_DenyExpensive(), 306 DNSSDFlagsOption_PathEvalDone(), 307 DNSSDFlagsOption_ReturnIntermediates(), 308 DNSSDFlagsOption_SuppressUnusable(), 309 DNSSDFlagsOption_Timeout(), 310 311 CLI_OPTION_GROUP( "Operation" ), 312 ConnectionOptions(), 313 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot, "Finish after first set of results." ), 314 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ), 315 316 ConnectionSection(), 317 CLI_OPTION_END() 318}; 319 320//=========================================================================================================================== 321// QueryRecord Command Options 322//=========================================================================================================================== 323 324static const char * gQueryRecord_Name = NULL; 325static const char * gQueryRecord_Type = NULL; 326static int gQueryRecord_OneShot = false; 327static int gQueryRecord_TimeLimitSecs = 0; 328static int gQueryRecord_RawRData = false; 329 330static CLIOption kQueryRecordOpts[] = 331{ 332 InterfaceOption(), 333 StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ), 334 StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ), 335 336 CLI_OPTION_GROUP( "Flags" ), 337 DNSSDFlagsOption(), 338 DNSSDFlagsOption_IncludeAWDL(), 339 DNSSDFlagsOption_ForceMulticast(), 340 DNSSDFlagsOption_Timeout(), 341 DNSSDFlagsOption_ReturnIntermediates(), 342 DNSSDFlagsOption_SuppressUnusable(), 343 DNSSDFlagsOption_UnicastResponse(), 344 DNSSDFlagsOption_DenyCellular(), 345 DNSSDFlagsOption_DenyExpensive(), 346 DNSSDFlagsOption_PathEvalDone(), 347 348 CLI_OPTION_GROUP( "Operation" ), 349 ConnectionOptions(), 350 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot, "Finish after first set of results." ), 351 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ), 352 BooleanOption( 0 , "raw", &gQueryRecord_RawRData, "Show record data as a hexdump." ), 353 354 ConnectionSection(), 355 CLI_OPTION_END() 356}; 357 358//=========================================================================================================================== 359// Register Command Options 360//=========================================================================================================================== 361 362static const char * gRegister_Name = NULL; 363static const char * gRegister_Type = NULL; 364static const char * gRegister_Domain = NULL; 365static int gRegister_Port = 0; 366static const char * gRegister_TXT = NULL; 367static int gRegister_LifetimeMs = -1; 368static const char ** gAddRecord_Types = NULL; 369static size_t gAddRecord_TypesCount = 0; 370static const char ** gAddRecord_Data = NULL; 371static size_t gAddRecord_DataCount = 0; 372static const char ** gAddRecord_TTLs = NULL; 373static size_t gAddRecord_TTLsCount = 0; 374static const char * gUpdateRecord_Data = NULL; 375static int gUpdateRecord_DelayMs = 0; 376static int gUpdateRecord_TTL = 0; 377 378static CLIOption kRegisterOpts[] = 379{ 380 InterfaceOption(), 381 StringOption( 'n', "name", &gRegister_Name, "service name", "Name of service.", false ), 382 StringOption( 't', "type", &gRegister_Type, "service type", "Service type, e.g., \"_ssh._tcp\".", true ), 383 StringOption( 'd', "domain", &gRegister_Domain, "domain", "Domain in which to advertise the service.", false ), 384 IntegerOption( 'p', "port", &gRegister_Port, "port number", "Service's port number.", true ), 385 StringOption( 0 , "txt", &gRegister_TXT, "record data", "The TXT record data. See " kRecordDataSection_Name " below.", false ), 386 387 CLI_OPTION_GROUP( "Flags" ), 388 DNSSDFlagsOption(), 389 DNSSDFlagsOption_IncludeAWDL(), 390 DNSSDFlagsOption_NoAutoRename(), 391 392 CLI_OPTION_GROUP( "Operation" ), 393 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ), 394 395 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ), 396 StringOption( 0 , "updateData", &gUpdateRecord_Data, "record data", "Record data for the record update. See " kRecordDataSection_Name " below.", false ), 397 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ), 398 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL, "seconds", "Time-to-live of the updated record.", false ), 399 400 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ), 401 MultiStringOption( 0 , "addType", &gAddRecord_Types, &gAddRecord_TypesCount, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ), 402 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data, &gAddRecord_DataCount, "record data", "Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ), 403 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs, &gAddRecord_TTLsCount, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ), 404 405 RecordDataSection(), 406 CLI_OPTION_END() 407}; 408 409//=========================================================================================================================== 410// RegisterRecord Command Options 411//=========================================================================================================================== 412 413static const char * gRegisterRecord_Name = NULL; 414static const char * gRegisterRecord_Type = NULL; 415static const char * gRegisterRecord_Data = NULL; 416static int gRegisterRecord_TTL = 0; 417static int gRegisterRecord_LifetimeMs = -1; 418static const char * gRegisterRecord_UpdateData = NULL; 419static int gRegisterRecord_UpdateDelayMs = 0; 420static int gRegisterRecord_UpdateTTL = 0; 421 422static CLIOption kRegisterRecordOpts[] = 423{ 424 InterfaceOption(), 425 StringOption( 'n', "name", &gRegisterRecord_Name, "record name", "Fully qualified domain name of record.", true ), 426 StringOption( 't', "type", &gRegisterRecord_Type, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ), 427 StringOption( 'd', "data", &gRegisterRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ), 428 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL, "seconds", "Time-to-live in seconds. Use '0' for default.", false ), 429 430 CLI_OPTION_GROUP( "Flags" ), 431 DNSSDFlagsOption(), 432 DNSSDFlagsOption_IncludeAWDL(), 433 DNSSDFlagsOption_Shared(), 434 DNSSDFlagsOption_Unique(), 435 436 CLI_OPTION_GROUP( "Operation" ), 437 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ), 438 439 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ), 440 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData, "record data", "Record data for the record update.", false ), 441 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ), 442 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL, "seconds", "Time-to-live of the updated record.", false ), 443 444 RecordDataSection(), 445 CLI_OPTION_END() 446}; 447 448//=========================================================================================================================== 449// Resolve Command Options 450//=========================================================================================================================== 451 452static char * gResolve_Name = NULL; 453static char * gResolve_Type = NULL; 454static char * gResolve_Domain = NULL; 455static int gResolve_TimeLimitSecs = 0; 456 457static CLIOption kResolveOpts[] = 458{ 459 InterfaceOption(), 460 StringOption( 'n', "name", &gResolve_Name, "service name", "Name of the service instance to resolve.", true ), 461 StringOption( 't', "type", &gResolve_Type, "service type", "Type of the service instance to resolve.", true ), 462 StringOption( 'd', "domain", &gResolve_Domain, "domain", "Domain of the service instance to resolve.", true ), 463 464 CLI_OPTION_GROUP( "Flags" ), 465 DNSSDFlagsOption(), 466 DNSSDFlagsOption_ForceMulticast(), 467 DNSSDFlagsOption_IncludeAWDL(), 468 DNSSDFlagsOption_ReturnIntermediates(), 469 470 CLI_OPTION_GROUP( "Operation" ), 471 ConnectionOptions(), 472 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ), 473 474 ConnectionSection(), 475 CLI_OPTION_END() 476}; 477 478//=========================================================================================================================== 479// Reconfirm Command Options 480//=========================================================================================================================== 481 482static const char * gReconfirmRecord_Name = NULL; 483static const char * gReconfirmRecord_Type = NULL; 484static const char * gReconfirmRecord_Class = NULL; 485static const char * gReconfirmRecord_Data = NULL; 486 487static CLIOption kReconfirmOpts[] = 488{ 489 InterfaceOption(), 490 StringOption( 'n', "name", &gReconfirmRecord_Name, "record name", "Full name of the record to reconfirm.", true ), 491 StringOption( 't', "type", &gReconfirmRecord_Type, "record type", "Type of the record to reconfirm.", true ), 492 StringOption( 'c', "class", &gReconfirmRecord_Class, "record class", "Class of the record to reconfirm. Default class is IN.", false ), 493 StringOption( 'd', "data", &gReconfirmRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ), 494 495 CLI_OPTION_GROUP( "Flags" ), 496 DNSSDFlagsOption(), 497 498 RecordDataSection(), 499 CLI_OPTION_END() 500}; 501 502//=========================================================================================================================== 503// getaddrinfo-POSIX Command Options 504//=========================================================================================================================== 505 506static const char * gGAIPOSIX_HostName = NULL; 507static const char * gGAIPOSIX_ServName = NULL; 508static const char * gGAIPOSIX_Family = NULL; 509static int gGAIPOSIXFlag_AddrConfig = false; 510static int gGAIPOSIXFlag_All = false; 511static int gGAIPOSIXFlag_CanonName = false; 512static int gGAIPOSIXFlag_NumericHost = false; 513static int gGAIPOSIXFlag_NumericServ = false; 514static int gGAIPOSIXFlag_Passive = false; 515static int gGAIPOSIXFlag_V4Mapped = false; 516#if( defined( AI_V4MAPPED_CFG ) ) 517static int gGAIPOSIXFlag_V4MappedCFG = false; 518#endif 519#if( defined( AI_DEFAULT ) ) 520static int gGAIPOSIXFlag_Default = false; 521#endif 522 523static CLIOption kGetAddrInfoPOSIXOpts[] = 524{ 525 StringOption( 'n', "hostname", &gGAIPOSIX_HostName, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ), 526 StringOption( 's', "servname", &gGAIPOSIX_ServName, "servname", "Port number in decimal or service name from services(5).", false ), 527 528 CLI_OPTION_GROUP( "Hints " ), 529 StringOptionEx( 'f', "family", &gGAIPOSIX_Family, "address family", "Address family to use for hints ai_family field.", false, 530 "\n" 531 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n" 532 "address family is specified, then AF_UNSPEC is used.\n" 533 "\n" ), 534 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig, "In hints ai_flags field, set AI_ADDRCONFIG." ), 535 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All, "In hints ai_flags field, set AI_ALL." ), 536 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName, "In hints ai_flags field, set AI_CANONNAME." ), 537 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost, "In hints ai_flags field, set AI_NUMERICHOST." ), 538 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ, "In hints ai_flags field, set AI_NUMERICSERV." ), 539 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive, "In hints ai_flags field, set AI_PASSIVE." ), 540 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped, "In hints ai_flags field, set AI_V4MAPPED." ), 541#if( defined( AI_V4MAPPED_CFG ) ) 542 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG, "In hints ai_flags field, set AI_V4MAPPED_CFG." ), 543#endif 544#if( defined( AI_DEFAULT ) ) 545 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ), 546#endif 547 548 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ), 549 CLI_OPTION_END() 550}; 551 552//=========================================================================================================================== 553// ReverseLookup Command Options 554//=========================================================================================================================== 555 556static const char * gReverseLookup_IPAddr = NULL; 557static int gReverseLookup_OneShot = false; 558static int gReverseLookup_TimeLimitSecs = 0; 559 560static CLIOption kReverseLookupOpts[] = 561{ 562 InterfaceOption(), 563 StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ), 564 565 CLI_OPTION_GROUP( "Flags" ), 566 DNSSDFlagsOption(), 567 DNSSDFlagsOption_ForceMulticast(), 568 DNSSDFlagsOption_ReturnIntermediates(), 569 DNSSDFlagsOption_SuppressUnusable(), 570 571 CLI_OPTION_GROUP( "Operation" ), 572 ConnectionOptions(), 573 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot, "Finish after first set of results." ), 574 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ), 575 576 ConnectionSection(), 577 CLI_OPTION_END() 578}; 579 580//=========================================================================================================================== 581// BrowseAll Command Options 582//=========================================================================================================================== 583 584static const char * gBrowseAll_Domain = NULL; 585static char ** gBrowseAll_ServiceTypes = NULL; 586static size_t gBrowseAll_ServiceTypesCount = 0; 587static int gBrowseAll_IncludeAWDL = false; 588static int gBrowseAll_BrowseTimeSecs = 5; 589static int gBrowseAll_ConnectTimeLimitSecs = 5; 590 591static CLIOption kBrowseAllOpts[] = 592{ 593 InterfaceOption(), 594 StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ), 595 MultiStringOption( 't', "type", &gBrowseAll_ServiceTypes, &gBrowseAll_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", false ), 596 597 CLI_OPTION_GROUP( "Flags" ), 598 DNSSDFlagsOption_IncludeAWDL(), 599 600 CLI_OPTION_GROUP( "Operation" ), 601 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Specifies the duration of the browse.", false ), 602 IntegerOption( 'c', "connectTimeLimit", &gBrowseAll_ConnectTimeLimitSecs, "seconds", "Specifies the max duration of the connect operations.", false ), 603 CLI_OPTION_END() 604}; 605 606//=========================================================================================================================== 607// GetAddrInfoStress Command Options 608//=========================================================================================================================== 609 610static int gGAIStress_TestDurationSecs = 0; 611static int gGAIStress_ConnectionCount = 0; 612static int gGAIStress_DurationMinMs = 0; 613static int gGAIStress_DurationMaxMs = 0; 614static int gGAIStress_RequestCountMax = 0; 615 616static CLIOption kGetAddrInfoStressOpts[] = 617{ 618 InterfaceOption(), 619 620 CLI_OPTION_GROUP( "Flags" ), 621 DNSSDFlagsOption_ReturnIntermediates(), 622 DNSSDFlagsOption_SuppressUnusable(), 623 624 CLI_OPTION_GROUP( "Operation" ), 625 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ), 626 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount, "integer", "Number of simultaneous DNS-SD connections.", true ), 627 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ), 628 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ), 629 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax, "integer", "Maximum number of requests on a connection before restarting it.", true ), 630 CLI_OPTION_END() 631}; 632 633//=========================================================================================================================== 634// DNSQuery Command Options 635//=========================================================================================================================== 636 637static char * gDNSQuery_Name = NULL; 638static char * gDNSQuery_Type = "A"; 639static char * gDNSQuery_Server = NULL; 640static int gDNSQuery_TimeLimitSecs = 5; 641static int gDNSQuery_UseTCP = false; 642static int gDNSQuery_Flags = kDNSHeaderFlag_RecursionDesired; 643static int gDNSQuery_RawRData = false; 644static int gDNSQuery_Verbose = false; 645 646#if( TARGET_OS_DARWIN ) 647 #define kDNSQueryServerOptionIsRequired false 648#else 649 #define kDNSQueryServerOptionIsRequired true 650#endif 651 652static CLIOption kDNSQueryOpts[] = 653{ 654 StringOption( 'n', "name", &gDNSQuery_Name, "name", "Question name (QNAME) to put in DNS query message.", true ), 655 StringOption( 't', "type", &gDNSQuery_Type, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ), 656 StringOption( 's', "server", &gDNSQuery_Server, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ), 657 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ), 658 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP, "Send the DNS query via TCP instead of UDP." ), 659 IntegerOption( 'f', "flags", &gDNSQuery_Flags, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ), 660 BooleanOption( 0 , "raw", &gDNSQuery_RawRData, "Present record data as a hexdump." ), 661 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose, "Prints the DNS message to be sent to the server." ), 662 CLI_OPTION_END() 663}; 664 665#if( DNSSDUTIL_INCLUDE_DNSCRYPT ) 666//=========================================================================================================================== 667// DNSCrypt Command Options 668//=========================================================================================================================== 669 670static char * gDNSCrypt_ProviderName = NULL; 671static char * gDNSCrypt_ProviderKey = NULL; 672static char * gDNSCrypt_Name = NULL; 673static char * gDNSCrypt_Type = NULL; 674static char * gDNSCrypt_Server = NULL; 675static int gDNSCrypt_TimeLimitSecs = 5; 676static int gDNSCrypt_RawRData = false; 677static int gDNSCrypt_Verbose = false; 678 679static CLIOption kDNSCryptOpts[] = 680{ 681 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName, "name", "The DNSCrypt provider name.", true ), 682 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey, "hex string", "The DNSCrypt provider's public signing key.", true ), 683 StringOption( 'n', "name", &gDNSCrypt_Name, "name", "Question name (QNAME) to put in DNS query message.", true ), 684 StringOption( 't', "type", &gDNSCrypt_Type, "type", "Question type (QTYPE) to put in DNS query message.", true ), 685 StringOption( 's', "server", &gDNSCrypt_Server, "IP address", "DNS server's IPv4 or IPv6 address.", true ), 686 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ), 687 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData, "Present record data as a hexdump." ), 688 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose, "Prints the DNS message to be sent to the server." ), 689 CLI_OPTION_END() 690}; 691#endif 692 693//=========================================================================================================================== 694// MDNSQuery Command Options 695//=========================================================================================================================== 696 697static char * gMDNSQuery_Name = NULL; 698static char * gMDNSQuery_Type = NULL; 699static int gMDNSQuery_SourcePort = 0; 700static int gMDNSQuery_IsQU = false; 701static int gMDNSQuery_RawRData = false; 702static int gMDNSQuery_UseIPv4 = false; 703static int gMDNSQuery_UseIPv6 = false; 704static int gMDNSQuery_AllResponses = false; 705static int gMDNSQuery_ReceiveSecs = 1; 706 707static CLIOption kMDNSQueryOpts[] = 708{ 709 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ), 710 StringOption( 'n', "name", &gMDNSQuery_Name, "name", "Question name (QNAME) to put in mDNS message.", true ), 711 StringOption( 't', "type", &gMDNSQuery_Type, "type", "Question type (QTYPE) to put in mDNS message.", true ), 712 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ), 713 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU, "Set the unicast-response bit, i.e., send a QU question." ), 714 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData, "Present record data as a hexdump." ), 715 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4, "Use IPv4." ), 716 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6, "Use IPv6." ), 717 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses, "Print all received mDNS messages, not just those containing answers." ), 718 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 ), 719 CLI_OPTION_END() 720}; 721 722//=========================================================================================================================== 723// PIDToUUID Command Options 724//=========================================================================================================================== 725 726static int gPIDToUUID_PID = 0; 727 728static CLIOption kPIDToUUIDOpts[] = 729{ 730 IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ), 731 CLI_OPTION_END() 732}; 733 734//=========================================================================================================================== 735// Command Table 736//=========================================================================================================================== 737 738static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset ); 739 740static void BrowseCmd( void ); 741static void GetAddrInfoCmd( void ); 742static void QueryRecordCmd( void ); 743static void RegisterCmd( void ); 744static void RegisterRecordCmd( void ); 745static void ResolveCmd( void ); 746static void ReconfirmCmd( void ); 747static void GetAddrInfoPOSIXCmd( void ); 748static void ReverseLookupCmd( void ); 749static void BrowseAllCmd( void ); 750static void GetAddrInfoStressCmd( void ); 751static void DNSQueryCmd( void ); 752#if( DNSSDUTIL_INCLUDE_DNSCRYPT ) 753static void DNSCryptCmd( void ); 754#endif 755static void MDNSQueryCmd( void ); 756static void PIDToUUIDCmd( void ); 757static void DaemonVersionCmd( void ); 758 759static CLIOption kGlobalOpts[] = 760{ 761 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL, 762 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ), 763 CLI_OPTION_HELP(), 764 765 // Common commands. 766 767 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ), 768 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ), 769 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ), 770 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ), 771 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ), 772 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ), 773 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ), 774 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ), 775 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ), 776 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all, or just some, services.", false ), 777 778 // Uncommon commands. 779 780 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ), 781 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ), 782#if( DNSSDUTIL_INCLUDE_DNSCRYPT ) 783 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ), 784#endif 785 Command( "mDNSQuery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ), 786 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ), 787 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ), 788 789 CLI_COMMAND_HELP(), 790 CLI_OPTION_END() 791}; 792 793//=========================================================================================================================== 794// Helper Prototypes 795//=========================================================================================================================== 796 797#define kExitReason_OneShotDone "one-shot done" 798#define kExitReason_ReceivedResponse "received response" 799#define kExitReason_SIGINT "interrupt signal" 800#define kExitReason_Timeout "timeout" 801#define kExitReason_TimeLimit "time limit" 802 803static void Exit( void *inContext ) ATTRIBUTE_NORETURN; 804 805#define kTimestampBufLen 27 806 807static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] ); 808static DNSServiceFlags GetDNSSDFlagsFromOpts( void ); 809 810typedef enum 811{ 812 kConnectionType_None = 0, 813 kConnectionType_Normal = 1, 814 kConnectionType_DelegatePID = 2, 815 kConnectionType_DelegateUUID = 3 816 817} ConnectionType; 818 819typedef struct 820{ 821 ConnectionType type; 822 union 823 { 824 int32_t pid; 825 uint8_t uuid[ 16 ]; 826 827 } delegate; 828 829} ConnectionDesc; 830 831static OSStatus 832 CreateConnectionFromArgString( 833 const char * inString, 834 dispatch_queue_t inQueue, 835 DNSServiceRef * outSDRef, 836 ConnectionDesc * outDesc ); 837static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex ); 838static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen ); 839static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue ); 840static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue ); 841 842#define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 ) 843 844static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] ); 845static const char * RecordTypeToString( unsigned int inValue ); 846 847// DNS message helpers 848 849typedef struct 850{ 851 uint8_t id[ 2 ]; 852 uint8_t flags[ 2 ]; 853 uint8_t questionCount[ 2 ]; 854 uint8_t answerCount[ 2 ]; 855 uint8_t authorityCount[ 2 ]; 856 uint8_t additionalCount[ 2 ]; 857 858} DNSHeader; 859 860#define kDNSHeaderLength 12 861check_compile_time( sizeof( DNSHeader ) == kDNSHeaderLength ); 862 863#define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id ) 864#define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags ) 865#define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount ) 866#define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount ) 867#define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount ) 868#define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount ) 869 870static OSStatus 871 DNSMessageExtractDomainName( 872 const uint8_t * inMsgPtr, 873 size_t inMsgLen, 874 const uint8_t * inNamePtr, 875 uint8_t inBuf[ kDomainNameLengthMax ], 876 const uint8_t ** outNextPtr ); 877static OSStatus 878 DNSMessageExtractDomainNameString( 879 const void * inMsgPtr, 880 size_t inMsgLen, 881 const void * inNamePtr, 882 char inBuf[ kDNSServiceMaxDomainName ], 883 const uint8_t ** outNextPtr ); 884static OSStatus 885 DNSMessageExtractRecord( 886 const uint8_t * inMsgPtr, 887 size_t inMsgLen, 888 const uint8_t * inPtr, 889 uint8_t inNameBuf[ kDomainNameLengthMax ], 890 uint16_t * outType, 891 uint16_t * outClass, 892 uint32_t * outTTL, 893 const uint8_t ** outRDataPtr, 894 size_t * outRDataLen, 895 const uint8_t ** outPtr ); 896static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr ); 897static OSStatus 898 DNSRecordDataToString( 899 const void * inRDataPtr, 900 size_t inRDataLen, 901 unsigned int inRDataType, 902 const void * inMsgPtr, 903 size_t inMsgLen, 904 char ** outString ); 905static OSStatus 906 DomainNameAppendString( 907 uint8_t inDomainName[ kDomainNameLengthMax ], 908 const char * inString, 909 uint8_t ** outEndPtr ); 910static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 ); 911static OSStatus 912 DomainNameToString( 913 const uint8_t * inDomainName, 914 const uint8_t * inEnd, 915 char inBuf[ kDNSServiceMaxDomainName ], 916 const uint8_t ** outNextPtr ); 917 918static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, Boolean inIsMDNS, Boolean inPrintRaw ); 919 920#define PrintMDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, true, RAW ) 921#define PrintUDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, false, RAW ) 922 923#define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 ) 924 925static OSStatus 926 WriteDNSQueryMessage( 927 uint8_t inMsg[ kDNSQueryMessageMaxLen ], 928 uint16_t inMsgID, 929 uint16_t inFlags, 930 const char * inQName, 931 uint16_t inQType, 932 uint16_t inQClass, 933 size_t * outMsgLen ); 934 935// Dispatch helpers 936 937typedef void ( *DispatchHandler )( void *inContext ); 938 939static OSStatus 940 DispatchSignalSourceCreate( 941 int inSignal, 942 DispatchHandler inEventHandler, 943 void * inContext, 944 dispatch_source_t * outSource ); 945static OSStatus 946 DispatchReadSourceCreate( 947 SocketRef inSock, 948 DispatchHandler inEventHandler, 949 DispatchHandler inCancelHandler, 950 void * inContext, 951 dispatch_source_t * outSource ); 952static OSStatus 953 DispatchTimerCreate( 954 dispatch_time_t inStart, 955 uint64_t inIntervalNs, 956 uint64_t inLeewayNs, 957 DispatchHandler inEventHandler, 958 DispatchHandler inCancelHandler, 959 void * inContext, 960 dispatch_source_t * outTimer ); 961 962static const char * ServiceTypeDescription( const char *inName ); 963 964typedef struct 965{ 966 SocketRef sock; 967 void * context; 968 969} SocketContext; 970 971static void SocketContextCancelHandler( void *inContext ); 972static OSStatus StringToInt32( const char *inString, int32_t *outValue ); 973static OSStatus StringToUInt32( const char *inString, uint32_t *outValue ); 974#if( TARGET_OS_DARWIN ) 975static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr ); 976#endif 977 978#define AddRmvString( X ) ( ( (X) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" ) 979#define Unused( X ) (void)(X) 980 981//=========================================================================================================================== 982// main 983//=========================================================================================================================== 984 985int main( int argc, const char **argv ) 986{ 987 // Route DebugServices logging output to stderr. 988 989 dlog_control( "DebugServices:output=file;stderr" ); 990 991 CLIInit( argc, argv ); 992 CLIParse( kGlobalOpts, kCLIFlags_None ); 993 994 return( gExitCode ); 995} 996 997//=========================================================================================================================== 998// VersionOptionCallback 999//=========================================================================================================================== 1000 1001static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset ) 1002{ 1003 const char * srcVers; 1004#if( MDNSRESPONDER_PROJECT ) 1005 char srcStr[ 16 ]; 1006#endif 1007 1008 Unused( inOption ); 1009 Unused( inArg ); 1010 Unused( inUnset ); 1011 1012#if( MDNSRESPONDER_PROJECT ) 1013 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr ); 1014#else 1015 srcVers = DNSSDUTIL_SOURCE_VERSION; 1016#endif 1017 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers ); 1018 1019 return( kEndingErr ); 1020} 1021 1022//=========================================================================================================================== 1023// BrowseCmd 1024//=========================================================================================================================== 1025 1026typedef struct BrowseResolveOp BrowseResolveOp; 1027 1028struct BrowseResolveOp 1029{ 1030 BrowseResolveOp * next; // Next resolve operation in list. 1031 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation. 1032 char * fullName; // Full name of the service to resolve. 1033 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation. 1034}; 1035 1036typedef struct 1037{ 1038 DNSServiceRef mainRef; // Main sdRef for shared connection. 1039 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions. 1040 size_t opRefsCount; // Count of array of sdRefs for non-shared connections. 1041 const char * domain; // Domain for DNSServiceBrowse operation(s). 1042 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s). 1043 char ** serviceTypes; // Array of service types to browse for. 1044 size_t serviceTypesCount; // Count of array of service types to browse for. 1045 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds. 1046 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations. 1047 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s). 1048 Boolean printedHeader; // True if results header has been printed. 1049 Boolean doResolve; // True if service instances are to be resolved. 1050 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried. 1051 1052} BrowseContext; 1053 1054static void BrowsePrintPrologue( const BrowseContext *inContext ); 1055static void BrowseContextFree( BrowseContext *inContext ); 1056static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp ); 1057static void BrowseResolveOpFree( BrowseResolveOp *inOp ); 1058static void DNSSD_API 1059 BrowseCallback( 1060 DNSServiceRef inSDRef, 1061 DNSServiceFlags inFlags, 1062 uint32_t inInterfaceIndex, 1063 DNSServiceErrorType inError, 1064 const char * inName, 1065 const char * inRegType, 1066 const char * inDomain, 1067 void * inContext ); 1068static void DNSSD_API 1069 BrowseResolveCallback( 1070 DNSServiceRef inSDRef, 1071 DNSServiceFlags inFlags, 1072 uint32_t inInterfaceIndex, 1073 DNSServiceErrorType inError, 1074 const char * inFullName, 1075 const char * inHostname, 1076 uint16_t inPort, 1077 uint16_t inTXTLen, 1078 const unsigned char * inTXTPtr, 1079 void * inContext ); 1080static void DNSSD_API 1081 BrowseQueryRecordCallback( 1082 DNSServiceRef inSDRef, 1083 DNSServiceFlags inFlags, 1084 uint32_t inInterfaceIndex, 1085 DNSServiceErrorType inError, 1086 const char * inFullName, 1087 uint16_t inType, 1088 uint16_t inClass, 1089 uint16_t inRDataLen, 1090 const void * inRDataPtr, 1091 uint32_t inTTL, 1092 void * inContext ); 1093 1094static void BrowseCmd( void ) 1095{ 1096 OSStatus err; 1097 size_t i; 1098 BrowseContext * context = NULL; 1099 dispatch_source_t signalSource = NULL; 1100 int useMainConnection; 1101 1102 // Set up SIGINT handler. 1103 1104 signal( SIGINT, SIG_IGN ); 1105 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); 1106 require_noerr( err, exit ); 1107 dispatch_resume( signalSource ); 1108 1109 // Create context. 1110 1111 context = (BrowseContext *) calloc( 1, sizeof( *context ) ); 1112 require_action( context, exit, err = kNoMemoryErr ); 1113 1114 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) ); 1115 require_action( context->opRefs, exit, err = kNoMemoryErr ); 1116 context->opRefsCount = gBrowse_ServiceTypesCount; 1117 1118 // Check command parameters. 1119 1120 if( gBrowse_TimeLimitSecs < 0 ) 1121 { 1122 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs ); 1123 err = kParamErr; 1124 goto exit; 1125 } 1126 1127 // Create main connection. 1128 1129 if( gConnectionOpt ) 1130 { 1131 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL ); 1132 require_noerr_quiet( err, exit ); 1133 useMainConnection = true; 1134 } 1135 else 1136 { 1137 useMainConnection = false; 1138 } 1139 1140 // Get flags. 1141 1142 context->flags = GetDNSSDFlagsFromOpts(); 1143 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection; 1144 1145 // Get interface. 1146 1147 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); 1148 require_noerr_quiet( err, exit ); 1149 1150 // Set remaining parameters. 1151 1152 context->serviceTypes = gBrowse_ServiceTypes; 1153 context->serviceTypesCount = gBrowse_ServiceTypesCount; 1154 context->domain = gBrowse_Domain; 1155 context->doResolve = gBrowse_DoResolve ? true : false; 1156 context->timeLimitSecs = gBrowse_TimeLimitSecs; 1157 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false; 1158 1159 // Print prologue. 1160 1161 BrowsePrintPrologue( context ); 1162 1163 // Start operation(s). 1164 1165 for( i = 0; i < context->serviceTypesCount; ++i ) 1166 { 1167 DNSServiceRef sdRef; 1168 1169 if( useMainConnection ) sdRef = context->mainRef; 1170 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain, 1171 BrowseCallback, context ); 1172 require_noerr( err, exit ); 1173 1174 context->opRefs[ i ] = sdRef; 1175 if( !useMainConnection ) 1176 { 1177 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() ); 1178 require_noerr( err, exit ); 1179 } 1180 } 1181 1182 // Set time limit. 1183 1184 if( context->timeLimitSecs > 0 ) 1185 { 1186 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), 1187 kExitReason_TimeLimit, Exit ); 1188 } 1189 dispatch_main(); 1190 1191exit: 1192 dispatch_source_forget( &signalSource ); 1193 if( context ) BrowseContextFree( context ); 1194 if( err ) exit( 1 ); 1195} 1196 1197//=========================================================================================================================== 1198// BrowsePrintPrologue 1199//=========================================================================================================================== 1200 1201static void BrowsePrintPrologue( const BrowseContext *inContext ) 1202{ 1203 const int timeLimitSecs = inContext->timeLimitSecs; 1204 const char * const * serviceType = (const char **) inContext->serviceTypes; 1205 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount; 1206 char time[ kTimestampBufLen ]; 1207 char ifName[ kInterfaceNameBufLen ]; 1208 1209 InterfaceIndexToName( inContext->ifIndex, ifName ); 1210 1211 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); 1212 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); 1213 FPrintF( stdout, "Service types: %s", *serviceType++ ); 1214 while( serviceType < end ) FPrintF( stdout, ", %s", *serviceType++ ); 1215 FPrintF( stdout, "\n" ); 1216 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" ); 1217 FPrintF( stdout, "Time limit: " ); 1218 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); 1219 else FPrintF( stdout, "���\n" ); 1220 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 1221 FPrintF( stdout, "---\n" ); 1222} 1223 1224//=========================================================================================================================== 1225// BrowseContextFree 1226//=========================================================================================================================== 1227 1228static void BrowseContextFree( BrowseContext *inContext ) 1229{ 1230 size_t i; 1231 1232 for( i = 0; i < inContext->opRefsCount; ++i ) 1233 { 1234 DNSServiceForget( &inContext->opRefs[ i ] ); 1235 } 1236 if( inContext->serviceTypes ) 1237 { 1238 StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount ); 1239 inContext->serviceTypes = NULL; 1240 inContext->serviceTypesCount = 0; 1241 } 1242 DNSServiceForget( &inContext->mainRef ); 1243 free( inContext ); 1244} 1245 1246//=========================================================================================================================== 1247// BrowseResolveOpCreate 1248//=========================================================================================================================== 1249 1250static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp ) 1251{ 1252 OSStatus err; 1253 BrowseResolveOp * resolveOp; 1254 1255 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) ); 1256 require_action( resolveOp, exit, err = kNoMemoryErr ); 1257 1258 resolveOp->fullName = strdup( inFullName ); 1259 require_action( resolveOp->fullName, exit, err = kNoMemoryErr ); 1260 1261 resolveOp->interfaceIndex = inInterfaceIndex; 1262 1263 *outOp = resolveOp; 1264 resolveOp = NULL; 1265 err = kNoErr; 1266 1267exit: 1268 if( resolveOp ) BrowseResolveOpFree( resolveOp ); 1269 return( err ); 1270} 1271 1272//=========================================================================================================================== 1273// BrowseResolveOpFree 1274//=========================================================================================================================== 1275 1276static void BrowseResolveOpFree( BrowseResolveOp *inOp ) 1277{ 1278 DNSServiceForget( &inOp->sdRef ); 1279 ForgetMem( &inOp->fullName ); 1280 free( inOp ); 1281} 1282 1283//=========================================================================================================================== 1284// BrowseCallback 1285//=========================================================================================================================== 1286 1287static void DNSSD_API 1288 BrowseCallback( 1289 DNSServiceRef inSDRef, 1290 DNSServiceFlags inFlags, 1291 uint32_t inInterfaceIndex, 1292 DNSServiceErrorType inError, 1293 const char * inName, 1294 const char * inRegType, 1295 const char * inDomain, 1296 void * inContext ) 1297{ 1298 BrowseContext * const context = (BrowseContext *) inContext; 1299 OSStatus err; 1300 BrowseResolveOp * newOp = NULL; 1301 BrowseResolveOp ** p; 1302 char fullName[ kDNSServiceMaxDomainName ]; 1303 char time[ kTimestampBufLen ]; 1304 1305 Unused( inSDRef ); 1306 1307 GetTimestampStr( time ); 1308 1309 err = inError; 1310 require_noerr( err, exit ); 1311 1312 if( !context->printedHeader ) 1313 { 1314 FPrintF( stdout, "%-26s A/R Flags IF %-20s %-20s Instance Name\n", "Timestamp", "Domain", "Service Type" ); 1315 context->printedHeader = true; 1316 } 1317 FPrintF( stdout, "%-26s %-3s %5X %2d %-20s %-20s %s\n", 1318 time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName ); 1319 1320 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit; 1321 1322 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain ); 1323 require_noerr( err, exit ); 1324 1325 if( inFlags & kDNSServiceFlagsAdd ) 1326 { 1327 DNSServiceRef sdRef; 1328 DNSServiceFlags flags; 1329 1330 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp ); 1331 require_noerr( err, exit ); 1332 1333 if( context->mainRef ) 1334 { 1335 sdRef = context->mainRef; 1336 flags = kDNSServiceFlagsShareConnection; 1337 } 1338 else 1339 { 1340 flags = 0; 1341 } 1342 if( context->doResolve ) 1343 { 1344 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback, 1345 NULL ); 1346 require_noerr( err, exit ); 1347 } 1348 else 1349 { 1350 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN, 1351 BrowseQueryRecordCallback, NULL ); 1352 require_noerr( err, exit ); 1353 } 1354 1355 newOp->sdRef = sdRef; 1356 if( !context->mainRef ) 1357 { 1358 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() ); 1359 require_noerr( err, exit ); 1360 } 1361 for( p = &context->resolveList; *p; p = &( *p )->next ) {} 1362 *p = newOp; 1363 newOp = NULL; 1364 } 1365 else 1366 { 1367 BrowseResolveOp * resolveOp; 1368 1369 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next ) 1370 { 1371 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) ) 1372 { 1373 break; 1374 } 1375 } 1376 if( resolveOp ) 1377 { 1378 *p = resolveOp->next; 1379 BrowseResolveOpFree( resolveOp ); 1380 } 1381 } 1382 1383exit: 1384 if( newOp ) BrowseResolveOpFree( newOp ); 1385 if( err ) exit( 1 ); 1386} 1387 1388//=========================================================================================================================== 1389// BrowseQueryRecordCallback 1390//=========================================================================================================================== 1391 1392static void DNSSD_API 1393 BrowseQueryRecordCallback( 1394 DNSServiceRef inSDRef, 1395 DNSServiceFlags inFlags, 1396 uint32_t inInterfaceIndex, 1397 DNSServiceErrorType inError, 1398 const char * inFullName, 1399 uint16_t inType, 1400 uint16_t inClass, 1401 uint16_t inRDataLen, 1402 const void * inRDataPtr, 1403 uint32_t inTTL, 1404 void * inContext ) 1405{ 1406 OSStatus err; 1407 char time[ kTimestampBufLen ]; 1408 1409 Unused( inSDRef ); 1410 Unused( inClass ); 1411 Unused( inTTL ); 1412 Unused( inContext ); 1413 1414 GetTimestampStr( time ); 1415 1416 err = inError; 1417 require_noerr( err, exit ); 1418 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr ); 1419 1420 FPrintF( stdout, "%s %s %s TXT on interface %d\n TXT: %#{txt}\n", 1421 time, AddRmvString( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr, (size_t) inRDataLen ); 1422 1423exit: 1424 if( err ) exit( 1 ); 1425} 1426 1427//=========================================================================================================================== 1428// BrowseResolveCallback 1429//=========================================================================================================================== 1430 1431static void DNSSD_API 1432 BrowseResolveCallback( 1433 DNSServiceRef inSDRef, 1434 DNSServiceFlags inFlags, 1435 uint32_t inInterfaceIndex, 1436 DNSServiceErrorType inError, 1437 const char * inFullName, 1438 const char * inHostname, 1439 uint16_t inPort, 1440 uint16_t inTXTLen, 1441 const unsigned char * inTXTPtr, 1442 void * inContext ) 1443{ 1444 char time[ kTimestampBufLen ]; 1445 char errorStr[ 64 ]; 1446 1447 Unused( inSDRef ); 1448 Unused( inFlags ); 1449 Unused( inContext ); 1450 1451 GetTimestampStr( time ); 1452 1453 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError ); 1454 1455 FPrintF( stdout, "%s %s can be reached at %s:%u (interface %d)%?s\n", 1456 time, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr ); 1457 if( inTXTLen == 1 ) 1458 { 1459 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX ); 1460 } 1461 else 1462 { 1463 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen ); 1464 } 1465} 1466 1467//=========================================================================================================================== 1468// GetAddrInfoCmd 1469//=========================================================================================================================== 1470 1471typedef struct 1472{ 1473 DNSServiceRef mainRef; // Main sdRef for shared connection. 1474 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation. 1475 const char * name; // Hostname to resolve. 1476 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo(). 1477 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo(). 1478 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo(). 1479 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds. 1480 Boolean printedHeader; // True if the results header has been printed. 1481 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode). 1482 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed. 1483 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed. 1484 1485} GetAddrInfoContext; 1486 1487static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext ); 1488static void GetAddrInfoContextFree( GetAddrInfoContext *inContext ); 1489static void DNSSD_API 1490 GetAddrInfoCallback( 1491 DNSServiceRef inSDRef, 1492 DNSServiceFlags inFlags, 1493 uint32_t inInterfaceIndex, 1494 DNSServiceErrorType inError, 1495 const char * inHostname, 1496 const struct sockaddr * inSockAddr, 1497 uint32_t inTTL, 1498 void * inContext ); 1499 1500static void GetAddrInfoCmd( void ) 1501{ 1502 OSStatus err; 1503 DNSServiceRef sdRef; 1504 GetAddrInfoContext * context = NULL; 1505 dispatch_source_t signalSource = NULL; 1506 int useMainConnection; 1507 1508 // Set up SIGINT handler. 1509 1510 signal( SIGINT, SIG_IGN ); 1511 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); 1512 require_noerr( err, exit ); 1513 dispatch_resume( signalSource ); 1514 1515 // Check command parameters. 1516 1517 if( gGetAddrInfo_TimeLimitSecs < 0 ) 1518 { 1519 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs ); 1520 err = kParamErr; 1521 goto exit; 1522 } 1523 1524 // Create context. 1525 1526 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) ); 1527 require_action( context, exit, err = kNoMemoryErr ); 1528 1529 // Create main connection. 1530 1531 if( gConnectionOpt ) 1532 { 1533 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL ); 1534 require_noerr_quiet( err, exit ); 1535 useMainConnection = true; 1536 } 1537 else 1538 { 1539 useMainConnection = false; 1540 } 1541 1542 // Get flags. 1543 1544 context->flags = GetDNSSDFlagsFromOpts(); 1545 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection; 1546 1547 // Get interface. 1548 1549 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); 1550 require_noerr_quiet( err, exit ); 1551 1552 // Set remaining parameters. 1553 1554 context->name = gGetAddrInfo_Name; 1555 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs; 1556 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4; 1557 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6; 1558 if( gGetAddrInfo_OneShot ) 1559 { 1560 context->oneShotMode = true; 1561 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false; 1562 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false; 1563 } 1564 1565 // Print prologue. 1566 1567 GetAddrInfoPrintPrologue( context ); 1568 1569 // Start operation. 1570 1571 if( useMainConnection ) sdRef = context->mainRef; 1572 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name, 1573 GetAddrInfoCallback, context ); 1574 require_noerr( err, exit ); 1575 1576 context->opRef = sdRef; 1577 if( !useMainConnection ) 1578 { 1579 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() ); 1580 require_noerr( err, exit ); 1581 } 1582 1583 // Set time limit. 1584 1585 if( context->timeLimitSecs > 0 ) 1586 { 1587 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), 1588 kExitReason_TimeLimit, Exit ); 1589 } 1590 dispatch_main(); 1591 1592exit: 1593 dispatch_source_forget( &signalSource ); 1594 if( context ) GetAddrInfoContextFree( context ); 1595 if( err ) exit( 1 ); 1596} 1597 1598//=========================================================================================================================== 1599// GetAddrInfoPrintPrologue 1600//=========================================================================================================================== 1601 1602static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext ) 1603{ 1604 const int timeLimitSecs = inContext->timeLimitSecs; 1605 char ifName[ kInterfaceNameBufLen ]; 1606 char time[ kTimestampBufLen ]; 1607 1608 InterfaceIndexToName( inContext->ifIndex, ifName ); 1609 1610 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); 1611 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); 1612 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors ); 1613 FPrintF( stdout, "Name: %s\n", inContext->name ); 1614 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" ); 1615 FPrintF( stdout, "Time limit: " ); 1616 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); 1617 else FPrintF( stdout, "���\n" ); 1618 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 1619 FPrintF( stdout, "---\n" ); 1620} 1621 1622//=========================================================================================================================== 1623// GetAddrInfoContextFree 1624//=========================================================================================================================== 1625 1626static void GetAddrInfoContextFree( GetAddrInfoContext *inContext ) 1627{ 1628 DNSServiceForget( &inContext->opRef ); 1629 DNSServiceForget( &inContext->mainRef ); 1630 free( inContext ); 1631} 1632 1633//=========================================================================================================================== 1634// GetAddrInfoCallback 1635//=========================================================================================================================== 1636 1637static void DNSSD_API 1638 GetAddrInfoCallback( 1639 DNSServiceRef inSDRef, 1640 DNSServiceFlags inFlags, 1641 uint32_t inInterfaceIndex, 1642 DNSServiceErrorType inError, 1643 const char * inHostname, 1644 const struct sockaddr * inSockAddr, 1645 uint32_t inTTL, 1646 void * inContext ) 1647{ 1648 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext; 1649 OSStatus err; 1650 const char * addrStr; 1651 char addrStrBuf[ kSockAddrStringMaxSize ]; 1652 char time[ kTimestampBufLen ]; 1653 1654 Unused( inSDRef ); 1655 1656 GetTimestampStr( time ); 1657 1658 switch( inError ) 1659 { 1660 case kDNSServiceErr_NoError: 1661 case kDNSServiceErr_NoSuchRecord: 1662 err = kNoErr; 1663 break; 1664 1665 case kDNSServiceErr_Timeout: 1666 Exit( kExitReason_Timeout ); 1667 1668 default: 1669 err = inError; 1670 goto exit; 1671 } 1672 1673 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) ) 1674 { 1675 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family ); 1676 err = kTypeErr; 1677 goto exit; 1678 } 1679 1680 if( !inError ) 1681 { 1682 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf ); 1683 require_noerr( err, exit ); 1684 addrStr = addrStrBuf; 1685 } 1686 else 1687 { 1688 addrStr = ( inSockAddr->sa_family == AF_INET ) ? "No Such Record (A)" : "No Such Record (AAAA)"; 1689 } 1690 1691 if( !context->printedHeader ) 1692 { 1693 FPrintF( stdout, "%-26s A/R Flags IF %-32s %-38s %6s\n", "Timestamp", "Hostname", "Address", "TTL" ); 1694 context->printedHeader = true; 1695 } 1696 FPrintF( stdout, "%-26s %s %5X %2d %-32s %-38s %6u\n", 1697 time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL ); 1698 1699 if( context->oneShotMode ) 1700 { 1701 if( inFlags & kDNSServiceFlagsAdd ) 1702 { 1703 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false; 1704 else context->needIPv6 = false; 1705 } 1706 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 ) 1707 { 1708 Exit( kExitReason_OneShotDone ); 1709 } 1710 } 1711 1712exit: 1713 if( err ) exit( 1 ); 1714} 1715 1716//=========================================================================================================================== 1717// QueryRecordCmd 1718//=========================================================================================================================== 1719 1720typedef struct 1721{ 1722 DNSServiceRef mainRef; // Main sdRef for shared connection. 1723 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation. 1724 const char * recordName; // Resource record name argument for DNSServiceQueryRecord(). 1725 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord(). 1726 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord(). 1727 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds. 1728 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord(). 1729 Boolean printedHeader; // True if the results header was printed. 1730 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode). 1731 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type. 1732 Boolean printRawRData; // True if RDATA results are not to be formatted when printed. 1733 1734} QueryRecordContext; 1735 1736static void QueryRecordPrintPrologue( const QueryRecordContext *inContext ); 1737static void QueryRecordContextFree( QueryRecordContext *inContext ); 1738static void DNSSD_API 1739 QueryRecordCallback( 1740 DNSServiceRef inSDRef, 1741 DNSServiceFlags inFlags, 1742 uint32_t inInterfaceIndex, 1743 DNSServiceErrorType inError, 1744 const char * inFullName, 1745 uint16_t inType, 1746 uint16_t inClass, 1747 uint16_t inRDataLen, 1748 const void * inRDataPtr, 1749 uint32_t inTTL, 1750 void * inContext ); 1751 1752static void QueryRecordCmd( void ) 1753{ 1754 OSStatus err; 1755 DNSServiceRef sdRef; 1756 QueryRecordContext * context = NULL; 1757 dispatch_source_t signalSource = NULL; 1758 int useMainConnection; 1759 1760 // Set up SIGINT handler. 1761 1762 signal( SIGINT, SIG_IGN ); 1763 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); 1764 require_noerr( err, exit ); 1765 dispatch_resume( signalSource ); 1766 1767 // Create context. 1768 1769 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) ); 1770 require_action( context, exit, err = kNoMemoryErr ); 1771 1772 // Check command parameters. 1773 1774 if( gQueryRecord_TimeLimitSecs < 0 ) 1775 { 1776 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs ); 1777 err = kParamErr; 1778 goto exit; 1779 } 1780 1781 // Create main connection. 1782 1783 if( gConnectionOpt ) 1784 { 1785 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL ); 1786 require_noerr_quiet( err, exit ); 1787 useMainConnection = true; 1788 } 1789 else 1790 { 1791 useMainConnection = false; 1792 } 1793 1794 // Get flags. 1795 1796 context->flags = GetDNSSDFlagsFromOpts(); 1797 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection; 1798 1799 // Get interface. 1800 1801 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); 1802 require_noerr_quiet( err, exit ); 1803 1804 // Get record type. 1805 1806 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType ); 1807 require_noerr( err, exit ); 1808 1809 // Set remaining parameters. 1810 1811 context->recordName = gQueryRecord_Name; 1812 context->timeLimitSecs = gQueryRecord_TimeLimitSecs; 1813 context->oneShotMode = gQueryRecord_OneShot ? true : false; 1814 context->printRawRData = gQueryRecord_RawRData ? true : false; 1815 1816 // Print prologue. 1817 1818 QueryRecordPrintPrologue( context ); 1819 1820 // Start operation. 1821 1822 if( useMainConnection ) sdRef = context->mainRef; 1823 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType, 1824 kDNSServiceClass_IN, QueryRecordCallback, context ); 1825 require_noerr( err, exit ); 1826 1827 context->opRef = sdRef; 1828 if( !useMainConnection ) 1829 { 1830 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() ); 1831 require_noerr( err, exit ); 1832 } 1833 1834 // Set time limit. 1835 1836 if( context->timeLimitSecs > 0 ) 1837 { 1838 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit, 1839 Exit ); 1840 } 1841 dispatch_main(); 1842 1843exit: 1844 dispatch_source_forget( &signalSource ); 1845 if( context ) QueryRecordContextFree( context ); 1846 if( err ) exit( 1 ); 1847} 1848 1849//=========================================================================================================================== 1850// QueryRecordContextFree 1851//=========================================================================================================================== 1852 1853static void QueryRecordContextFree( QueryRecordContext *inContext ) 1854{ 1855 DNSServiceForget( &inContext->opRef ); 1856 DNSServiceForget( &inContext->mainRef ); 1857 free( inContext ); 1858} 1859 1860//=========================================================================================================================== 1861// QueryRecordPrintPrologue 1862//=========================================================================================================================== 1863 1864static void QueryRecordPrintPrologue( const QueryRecordContext *inContext ) 1865{ 1866 const int timeLimitSecs = inContext->timeLimitSecs; 1867 char ifName[ kInterfaceNameBufLen ]; 1868 char time[ kTimestampBufLen ]; 1869 1870 InterfaceIndexToName( inContext->ifIndex, ifName ); 1871 1872 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); 1873 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); 1874 FPrintF( stdout, "Name: %s\n", inContext->recordName ); 1875 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType ); 1876 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" ); 1877 FPrintF( stdout, "Time limit: " ); 1878 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); 1879 else FPrintF( stdout, "���\n" ); 1880 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 1881 FPrintF( stdout, "---\n" ); 1882 1883} 1884 1885//=========================================================================================================================== 1886// QueryRecordCallback 1887//=========================================================================================================================== 1888 1889static void DNSSD_API 1890 QueryRecordCallback( 1891 DNSServiceRef inSDRef, 1892 DNSServiceFlags inFlags, 1893 uint32_t inInterfaceIndex, 1894 DNSServiceErrorType inError, 1895 const char * inFullName, 1896 uint16_t inType, 1897 uint16_t inClass, 1898 uint16_t inRDataLen, 1899 const void * inRDataPtr, 1900 uint32_t inTTL, 1901 void * inContext ) 1902{ 1903 QueryRecordContext * const context = (QueryRecordContext *) inContext; 1904 OSStatus err; 1905 char * rdataStr = NULL; 1906 char time[ kTimestampBufLen ]; 1907 1908 Unused( inSDRef ); 1909 1910 GetTimestampStr( time ); 1911 1912 switch( inError ) 1913 { 1914 case kDNSServiceErr_NoError: 1915 case kDNSServiceErr_NoSuchRecord: 1916 err = kNoErr; 1917 break; 1918 1919 case kDNSServiceErr_Timeout: 1920 Exit( kExitReason_Timeout ); 1921 1922 default: 1923 err = inError; 1924 goto exit; 1925 } 1926 1927 if( inError == kDNSServiceErr_NoSuchRecord ) 1928 { 1929 ASPrintF( &rdataStr, "No Such Record" ); 1930 } 1931 else 1932 { 1933 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr ); 1934 if( !rdataStr ) 1935 { 1936 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, INT_MAX ); 1937 require_action( rdataStr, exit, err = kNoMemoryErr ); 1938 } 1939 } 1940 1941 if( !context->printedHeader ) 1942 { 1943 FPrintF( stdout, "%-26s A/R Flags IF %-32s %-5s %-5s %6s RData\n", "Timestamp", "Name", "Type", "Class", "TTL" ); 1944 context->printedHeader = true; 1945 } 1946 FPrintF( stdout, "%-26s %-3s %5X %2d %-32s %-5s %?-5s%?5u %6u %s\n", 1947 time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ), 1948 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL, rdataStr ); 1949 1950 if( context->oneShotMode ) 1951 { 1952 if( ( inFlags & kDNSServiceFlagsAdd ) && 1953 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) ) 1954 { 1955 context->gotRecord = true; 1956 } 1957 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone ); 1958 } 1959 1960exit: 1961 FreeNullSafe( rdataStr ); 1962 if( err ) exit( 1 ); 1963} 1964 1965//=========================================================================================================================== 1966// RegisterCmd 1967//=========================================================================================================================== 1968 1969typedef struct 1970{ 1971 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord(). 1972 uint8_t * dataPtr; // Record data. 1973 size_t dataLen; // Record data length. 1974 uint32_t ttl; // Record TTL value. 1975 uint16_t type; // Record type. 1976 1977} ExtraRecord; 1978 1979typedef struct 1980{ 1981 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation. 1982 const char * name; // Service name argument for DNSServiceRegister(). 1983 const char * type; // Service type argument for DNSServiceRegister(). 1984 const char * domain; // Domain in which advertise the service. 1985 uint8_t * txtPtr; // Service TXT record data. (malloc'd) 1986 size_t txtLen; // Service TXT record data len. 1987 ExtraRecord * extraRecords; // Array of extra records to add to registered service. 1988 size_t extraRecordsCount; // Number of extra records. 1989 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd) 1990 size_t updateTXTLen; // Length of record data for TXT record update. 1991 uint32_t updateTTL; // TTL of updated TXT record. 1992 int updateDelayMs; // Post-registration TXT record update delay in milliseconds. 1993 DNSServiceFlags flags; // Flags argument for DNSServiceRegister(). 1994 uint32_t ifIndex; // Interface index argument for DNSServiceRegister(). 1995 int lifetimeMs; // Lifetime of the record registration in milliseconds. 1996 uint16_t port; // Service instance's port number. 1997 Boolean printedHeader; // True if results header was printed. 1998 Boolean didRegister; // True if service was registered. 1999 2000} RegisterContext; 2001 2002static void RegisterPrintPrologue( const RegisterContext *inContext ); 2003static void RegisterContextFree( RegisterContext *inContext ); 2004static void DNSSD_API 2005 RegisterCallback( 2006 DNSServiceRef inSDRef, 2007 DNSServiceFlags inFlags, 2008 DNSServiceErrorType inError, 2009 const char * inName, 2010 const char * inType, 2011 const char * inDomain, 2012 void * inContext ); 2013static void RegisterUpdate( void *inContext ); 2014 2015static void RegisterCmd( void ) 2016{ 2017 OSStatus err; 2018 RegisterContext * context = NULL; 2019 dispatch_source_t signalSource = NULL; 2020 2021 // Set up SIGINT handler. 2022 2023 signal( SIGINT, SIG_IGN ); 2024 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); 2025 require_noerr( err, exit ); 2026 dispatch_resume( signalSource ); 2027 2028 // Create context. 2029 2030 context = (RegisterContext *) calloc( 1, sizeof( *context ) ); 2031 require_action( context, exit, err = kNoMemoryErr ); 2032 2033 // Check command parameters. 2034 2035 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) ) 2036 { 2037 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port ); 2038 err = kParamErr; 2039 goto exit; 2040 } 2041 2042 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) ) 2043 { 2044 FPrintF( stderr, "There are missing additional record parameters.\n" ); 2045 err = kParamErr; 2046 goto exit; 2047 } 2048 2049 // Get flags. 2050 2051 context->flags = GetDNSSDFlagsFromOpts(); 2052 2053 // Get interface index. 2054 2055 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); 2056 require_noerr_quiet( err, exit ); 2057 2058 // Get TXT record data. 2059 2060 if( gRegister_TXT ) 2061 { 2062 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen ); 2063 require_noerr_quiet( err, exit ); 2064 } 2065 2066 // Set remaining parameters. 2067 2068 context->name = gRegister_Name; 2069 context->type = gRegister_Type; 2070 context->domain = gRegister_Domain; 2071 context->port = (uint16_t) gRegister_Port; 2072 context->lifetimeMs = gRegister_LifetimeMs; 2073 2074 if( gAddRecord_TypesCount > 0 ) 2075 { 2076 size_t i; 2077 2078 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) ); 2079 require_action( context, exit, err = kNoMemoryErr ); 2080 context->extraRecordsCount = gAddRecord_TypesCount; 2081 2082 for( i = 0; i < gAddRecord_TypesCount; ++i ) 2083 { 2084 ExtraRecord * const extraRecord = &context->extraRecords[ i ]; 2085 2086 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type ); 2087 require_noerr( err, exit ); 2088 2089 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl ); 2090 if( err ) 2091 { 2092 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] ); 2093 err = kParamErr; 2094 goto exit; 2095 } 2096 2097 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen ); 2098 require_noerr_quiet( err, exit ); 2099 } 2100 } 2101 2102 if( gUpdateRecord_Data ) 2103 { 2104 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen ); 2105 require_noerr_quiet( err, exit ); 2106 2107 context->updateTTL = (uint32_t) gUpdateRecord_TTL; 2108 context->updateDelayMs = gUpdateRecord_DelayMs; 2109 } 2110 2111 // Print prologue. 2112 2113 RegisterPrintPrologue( context ); 2114 2115 // Start operation. 2116 2117 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type, 2118 context->domain, NULL, ntohs( context->port ), (uint16_t) context->txtLen, context->txtPtr, 2119 RegisterCallback, context ); 2120 ForgetMem( &context->txtPtr ); 2121 require_noerr( err, exit ); 2122 2123 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() ); 2124 require_noerr( err, exit ); 2125 2126 dispatch_main(); 2127 2128exit: 2129 dispatch_source_forget( &signalSource ); 2130 if( context ) RegisterContextFree( context ); 2131 if( err ) exit( 1 ); 2132} 2133 2134//=========================================================================================================================== 2135// RegisterPrintPrologue 2136//=========================================================================================================================== 2137 2138static void RegisterPrintPrologue( const RegisterContext *inContext ) 2139{ 2140 size_t i; 2141 int infinite; 2142 char ifName[ kInterfaceNameBufLen ]; 2143 char time[ kTimestampBufLen ]; 2144 2145 InterfaceIndexToName( inContext->ifIndex, ifName ); 2146 2147 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); 2148 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); 2149 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" ); 2150 FPrintF( stdout, "Type: %s\n", inContext->type ); 2151 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" ); 2152 FPrintF( stdout, "Port: %u\n", inContext->port ); 2153 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen ); 2154 infinite = ( inContext->lifetimeMs < 0 ) ? true : false; 2155 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "���", !infinite, inContext->lifetimeMs ); 2156 if( inContext->updateTXTPtr ) 2157 { 2158 FPrintF( stdout, "\nUpdate record:\n" ); 2159 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 ); 2160 FPrintF( stdout, " TTL: %u%?s\n", 2161 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" ); 2162 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen ); 2163 } 2164 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" ); 2165 for( i = 0; i < inContext->extraRecordsCount; ++i ) 2166 { 2167 const ExtraRecord * record = &inContext->extraRecords[ i ]; 2168 2169 FPrintF( stdout, "Extra record %zu:\n", i + 1 ); 2170 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type ); 2171 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" ); 2172 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX ); 2173 } 2174 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 2175 FPrintF( stdout, "---\n" ); 2176} 2177 2178//=========================================================================================================================== 2179// RegisterContextFree 2180//=========================================================================================================================== 2181 2182static void RegisterContextFree( RegisterContext *inContext ) 2183{ 2184 ExtraRecord * record; 2185 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount; 2186 2187 DNSServiceForget( &inContext->opRef ); 2188 ForgetMem( &inContext->txtPtr ); 2189 for( record = inContext->extraRecords; record < end; ++record ) 2190 { 2191 check( !record->recordRef ); 2192 ForgetMem( &record->dataPtr ); 2193 } 2194 ForgetMem( &inContext->extraRecords ); 2195 ForgetMem( &inContext->updateTXTPtr ); 2196 free( inContext ); 2197} 2198 2199//=========================================================================================================================== 2200// RegisterCallback 2201//=========================================================================================================================== 2202 2203static void DNSSD_API 2204 RegisterCallback( 2205 DNSServiceRef inSDRef, 2206 DNSServiceFlags inFlags, 2207 DNSServiceErrorType inError, 2208 const char * inName, 2209 const char * inType, 2210 const char * inDomain, 2211 void * inContext ) 2212{ 2213 RegisterContext * const context = (RegisterContext *) inContext; 2214 OSStatus err; 2215 char time[ kTimestampBufLen ]; 2216 2217 Unused( inSDRef ); 2218 2219 GetTimestampStr( time ); 2220 2221 if( !context->printedHeader ) 2222 { 2223 FPrintF( stdout, "%-26s A/R Flags Service\n", "Timestamp" ); 2224 context->printedHeader = true; 2225 } 2226 FPrintF( stdout, "%-26s %-3s %5X %s.%s%s %?#m\n", 2227 time, AddRmvString( inFlags ), inFlags, inName, inType, inDomain, inError, inError ); 2228 2229 require_noerr_action_quiet( inError, exit, err = inError ); 2230 2231 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) ) 2232 { 2233 context->didRegister = true; 2234 if( context->updateTXTPtr ) 2235 { 2236 if( context->updateDelayMs > 0 ) 2237 { 2238 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(), 2239 context, RegisterUpdate ); 2240 } 2241 else 2242 { 2243 RegisterUpdate( context ); 2244 } 2245 } 2246 if( context->extraRecordsCount > 0 ) 2247 { 2248 ExtraRecord * record; 2249 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount; 2250 2251 for( record = context->extraRecords; record < end; ++record ) 2252 { 2253 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type, 2254 (uint16_t) record->dataLen, record->dataPtr, record->ttl ); 2255 require_noerr( err, exit ); 2256 } 2257 } 2258 if( context->lifetimeMs == 0 ) 2259 { 2260 Exit( kExitReason_TimeLimit ); 2261 } 2262 else if( context->lifetimeMs > 0 ) 2263 { 2264 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(), 2265 kExitReason_TimeLimit, Exit ); 2266 } 2267 } 2268 err = kNoErr; 2269 2270exit: 2271 if( err ) exit( 1 ); 2272} 2273 2274//=========================================================================================================================== 2275// RegisterUpdate 2276//=========================================================================================================================== 2277 2278static void RegisterUpdate( void *inContext ) 2279{ 2280 OSStatus err; 2281 RegisterContext * const context = (RegisterContext *) inContext; 2282 2283 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr, 2284 context->updateTTL ); 2285 require_noerr( err, exit ); 2286 2287exit: 2288 if( err ) exit( 1 ); 2289} 2290 2291//=========================================================================================================================== 2292// RegisterRecordCmd 2293//=========================================================================================================================== 2294 2295typedef struct 2296{ 2297 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection(). 2298 DNSRecordRef recordRef; // Registered record reference. 2299 const char * recordName; // Name of resource record. 2300 uint8_t * dataPtr; // Pointer to resource record data. 2301 size_t dataLen; // Length of resource record data. 2302 uint32_t ttl; // TTL value of resource record in seconds. 2303 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord(). 2304 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord(). 2305 int lifetimeMs; // Lifetime of the record registration in milliseconds. 2306 uint16_t recordType; // Resource record type. 2307 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd) 2308 size_t updateDataLen; // Length of data for record update. 2309 uint32_t updateTTL; // TTL for updated record. 2310 int updateDelayMs; // Post-registration record update delay in milliseconds. 2311 Boolean didRegister; // True if the record was registered. 2312 2313} RegisterRecordContext; 2314 2315static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext ); 2316static void RegisterRecordContextFree( RegisterRecordContext *inContext ); 2317static void DNSSD_API 2318 RegisterRecordCallback( 2319 DNSServiceRef inSDRef, 2320 DNSRecordRef inRecordRef, 2321 DNSServiceFlags inFlags, 2322 DNSServiceErrorType inError, 2323 void * inContext ); 2324static void RegisterRecordUpdate( void *inContext ); 2325 2326static void RegisterRecordCmd( void ) 2327{ 2328 OSStatus err; 2329 RegisterRecordContext * context = NULL; 2330 dispatch_source_t signalSource = NULL; 2331 2332 // Set up SIGINT handler. 2333 2334 signal( SIGINT, SIG_IGN ); 2335 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); 2336 require_noerr( err, exit ); 2337 dispatch_resume( signalSource ); 2338 2339 // Create context. 2340 2341 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) ); 2342 require_action( context, exit, err = kNoMemoryErr ); 2343 2344 // Create connection. 2345 2346 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL ); 2347 require_noerr_quiet( err, exit ); 2348 2349 // Get flags. 2350 2351 context->flags = GetDNSSDFlagsFromOpts(); 2352 2353 // Get interface. 2354 2355 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); 2356 require_noerr_quiet( err, exit ); 2357 2358 // Get record type. 2359 2360 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType ); 2361 require_noerr( err, exit ); 2362 2363 // Get record data. 2364 2365 if( gRegisterRecord_Data ) 2366 { 2367 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen ); 2368 require_noerr_quiet( err, exit ); 2369 } 2370 2371 // Set remaining parameters. 2372 2373 context->recordName = gRegisterRecord_Name; 2374 context->ttl = (uint32_t) gRegisterRecord_TTL; 2375 context->lifetimeMs = gRegisterRecord_LifetimeMs; 2376 2377 // Get update data. 2378 2379 if( gRegisterRecord_UpdateData ) 2380 { 2381 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen ); 2382 require_noerr_quiet( err, exit ); 2383 2384 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL; 2385 context->updateDelayMs = gRegisterRecord_UpdateDelayMs; 2386 } 2387 2388 // Print prologue. 2389 2390 RegisterRecordPrintPrologue( context ); 2391 2392 // Start operation. 2393 2394 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex, 2395 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr, 2396 context->ttl, RegisterRecordCallback, context ); 2397 if( err ) 2398 { 2399 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err ); 2400 goto exit; 2401 } 2402 2403 dispatch_main(); 2404 2405exit: 2406 dispatch_source_forget( &signalSource ); 2407 if( context ) RegisterRecordContextFree( context ); 2408 if( err ) exit( 1 ); 2409} 2410 2411//=========================================================================================================================== 2412// RegisterRecordPrintPrologue 2413//=========================================================================================================================== 2414 2415static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext ) 2416{ 2417 int infinite; 2418 char time[ kTimestampBufLen ]; 2419 char ifName[ kInterfaceNameBufLen ]; 2420 2421 InterfaceIndexToName( inContext->ifIndex, ifName ); 2422 2423 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); 2424 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); 2425 FPrintF( stdout, "Name: %s\n", inContext->recordName ); 2426 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType ); 2427 FPrintF( stdout, "TTL: %u\n", inContext->ttl ); 2428 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX ); 2429 infinite = ( inContext->lifetimeMs < 0 ) ? true : false; 2430 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "���", !infinite, inContext->lifetimeMs ); 2431 if( inContext->updateDataPtr ) 2432 { 2433 FPrintF( stdout, "\nUpdate record:\n" ); 2434 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 ); 2435 FPrintF( stdout, " TTL: %u%?s\n", 2436 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" ); 2437 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX ); 2438 } 2439 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 2440 FPrintF( stdout, "---\n" ); 2441} 2442 2443//=========================================================================================================================== 2444// RegisterRecordContextFree 2445//=========================================================================================================================== 2446 2447static void RegisterRecordContextFree( RegisterRecordContext *inContext ) 2448{ 2449 DNSServiceForget( &inContext->conRef ); 2450 ForgetMem( &inContext->dataPtr ); 2451 ForgetMem( &inContext->updateDataPtr ); 2452 free( inContext ); 2453} 2454 2455//=========================================================================================================================== 2456// RegisterRecordCallback 2457//=========================================================================================================================== 2458 2459static void 2460 RegisterRecordCallback( 2461 DNSServiceRef inSDRef, 2462 DNSRecordRef inRecordRef, 2463 DNSServiceFlags inFlags, 2464 DNSServiceErrorType inError, 2465 void * inContext ) 2466{ 2467 RegisterRecordContext * context = (RegisterRecordContext *) inContext; 2468 char time[ kTimestampBufLen ]; 2469 2470 Unused( inSDRef ); 2471 Unused( inRecordRef ); 2472 Unused( inFlags ); 2473 Unused( context ); 2474 2475 GetTimestampStr( time ); 2476 FPrintF( stdout, "%s Record registration result (error %#m)\n", time, inError ); 2477 2478 if( !context->didRegister && !inError ) 2479 { 2480 context->didRegister = true; 2481 if( context->updateDataPtr ) 2482 { 2483 if( context->updateDelayMs > 0 ) 2484 { 2485 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(), 2486 context, RegisterRecordUpdate ); 2487 } 2488 else 2489 { 2490 RegisterRecordUpdate( context ); 2491 } 2492 } 2493 if( context->lifetimeMs == 0 ) 2494 { 2495 Exit( kExitReason_TimeLimit ); 2496 } 2497 else if( context->lifetimeMs > 0 ) 2498 { 2499 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(), 2500 kExitReason_TimeLimit, Exit ); 2501 } 2502 } 2503} 2504 2505//=========================================================================================================================== 2506// RegisterRecordUpdate 2507//=========================================================================================================================== 2508 2509static void RegisterRecordUpdate( void *inContext ) 2510{ 2511 OSStatus err; 2512 RegisterRecordContext * const context = (RegisterRecordContext *) inContext; 2513 2514 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen, 2515 context->updateDataPtr, context->updateTTL ); 2516 require_noerr( err, exit ); 2517 2518exit: 2519 if( err ) exit( 1 ); 2520} 2521 2522//=========================================================================================================================== 2523// ResolveCmd 2524//=========================================================================================================================== 2525 2526typedef struct 2527{ 2528 DNSServiceRef mainRef; // Main sdRef for shared connections. 2529 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation. 2530 DNSServiceFlags flags; // Flags argument for DNSServiceResolve(). 2531 const char * name; // Service name argument for DNSServiceResolve(). 2532 const char * type; // Service type argument for DNSServiceResolve(). 2533 const char * domain; // Domain argument for DNSServiceResolve(). 2534 uint32_t ifIndex; // Interface index argument for DNSServiceResolve(). 2535 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds. 2536 2537} ResolveContext; 2538 2539static void ResolvePrintPrologue( const ResolveContext *inContext ); 2540static void ResolveContextFree( ResolveContext *inContext ); 2541static void DNSSD_API 2542 ResolveCallback( 2543 DNSServiceRef inSDRef, 2544 DNSServiceFlags inFlags, 2545 uint32_t inInterfaceIndex, 2546 DNSServiceErrorType inError, 2547 const char * inFullName, 2548 const char * inHostname, 2549 uint16_t inPort, 2550 uint16_t inTXTLen, 2551 const unsigned char * inTXTPtr, 2552 void * inContext ); 2553 2554static void ResolveCmd( void ) 2555{ 2556 OSStatus err; 2557 DNSServiceRef sdRef; 2558 ResolveContext * context = NULL; 2559 dispatch_source_t signalSource = NULL; 2560 int useMainConnection; 2561 2562 // Set up SIGINT handler. 2563 2564 signal( SIGINT, SIG_IGN ); 2565 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); 2566 require_noerr( err, exit ); 2567 dispatch_resume( signalSource ); 2568 2569 // Create context. 2570 2571 context = (ResolveContext *) calloc( 1, sizeof( *context ) ); 2572 require_action( context, exit, err = kNoMemoryErr ); 2573 2574 // Check command parameters. 2575 2576 if( gResolve_TimeLimitSecs < 0 ) 2577 { 2578 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs ); 2579 err = kParamErr; 2580 goto exit; 2581 } 2582 2583 // Create main connection. 2584 2585 if( gConnectionOpt ) 2586 { 2587 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL ); 2588 require_noerr_quiet( err, exit ); 2589 useMainConnection = true; 2590 } 2591 else 2592 { 2593 useMainConnection = false; 2594 } 2595 2596 // Get flags. 2597 2598 context->flags = GetDNSSDFlagsFromOpts(); 2599 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection; 2600 2601 // Get interface index. 2602 2603 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); 2604 require_noerr_quiet( err, exit ); 2605 2606 // Set remaining parameters. 2607 2608 context->name = gResolve_Name; 2609 context->type = gResolve_Type; 2610 context->domain = gResolve_Domain; 2611 context->timeLimitSecs = gResolve_TimeLimitSecs; 2612 2613 // Print prologue. 2614 2615 ResolvePrintPrologue( context ); 2616 2617 // Start operation. 2618 2619 if( useMainConnection ) sdRef = context->mainRef; 2620 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain, 2621 ResolveCallback, NULL ); 2622 require_noerr( err, exit ); 2623 2624 context->opRef = sdRef; 2625 if( !useMainConnection ) 2626 { 2627 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() ); 2628 require_noerr( err, exit ); 2629 } 2630 2631 // Set time limit. 2632 2633 if( context->timeLimitSecs > 0 ) 2634 { 2635 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), 2636 kExitReason_TimeLimit, Exit ); 2637 } 2638 dispatch_main(); 2639 2640exit: 2641 dispatch_source_forget( &signalSource ); 2642 if( context ) ResolveContextFree( context ); 2643 if( err ) exit( 1 ); 2644} 2645 2646//=========================================================================================================================== 2647// ReconfirmCmd 2648//=========================================================================================================================== 2649 2650static void ReconfirmCmd( void ) 2651{ 2652 OSStatus err; 2653 uint8_t * rdataPtr = NULL; 2654 size_t rdataLen = 0; 2655 DNSServiceFlags flags; 2656 uint32_t ifIndex; 2657 uint16_t type, class; 2658 char ifName[ kInterfaceNameBufLen ]; 2659 2660 // Get flags. 2661 2662 flags = GetDNSSDFlagsFromOpts(); 2663 2664 // Get interface index. 2665 2666 err = InterfaceIndexFromArgString( gInterface, &ifIndex ); 2667 require_noerr_quiet( err, exit ); 2668 2669 // Get record type. 2670 2671 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type ); 2672 require_noerr( err, exit ); 2673 2674 // Get record data. 2675 2676 if( gReconfirmRecord_Data ) 2677 { 2678 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen ); 2679 require_noerr_quiet( err, exit ); 2680 } 2681 2682 // Get record data. 2683 2684 if( gReconfirmRecord_Class ) 2685 { 2686 err = RecordClassFromArgString( gReconfirmRecord_Class, &class ); 2687 require_noerr( err, exit ); 2688 } 2689 else 2690 { 2691 class = kDNSServiceClass_IN; 2692 } 2693 2694 // Print prologue. 2695 2696 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors ); 2697 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) ); 2698 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name ); 2699 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type ); 2700 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class ); 2701 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX ); 2702 FPrintF( stdout, "---\n" ); 2703 2704 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr ); 2705 FPrintF( stdout, "Error: %#m\n", err ); 2706 2707exit: 2708 FreeNullSafe( rdataPtr ); 2709 if( err ) exit( 1 ); 2710} 2711 2712//=========================================================================================================================== 2713// ResolvePrintPrologue 2714//=========================================================================================================================== 2715 2716static void ResolvePrintPrologue( const ResolveContext *inContext ) 2717{ 2718 const int timeLimitSecs = inContext->timeLimitSecs; 2719 char ifName[ kInterfaceNameBufLen ]; 2720 char time[ kTimestampBufLen ]; 2721 2722 InterfaceIndexToName( inContext->ifIndex, ifName ); 2723 2724 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); 2725 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); 2726 FPrintF( stdout, "Name: %s\n", inContext->name ); 2727 FPrintF( stdout, "Type: %s\n", inContext->type ); 2728 FPrintF( stdout, "Domain: %s\n", inContext->domain ); 2729 FPrintF( stdout, "Time limit: " ); 2730 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); 2731 else FPrintF( stdout, "���\n" ); 2732 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 2733 FPrintF( stdout, "---\n" ); 2734} 2735 2736//=========================================================================================================================== 2737// ResolveContextFree 2738//=========================================================================================================================== 2739 2740static void ResolveContextFree( ResolveContext *inContext ) 2741{ 2742 DNSServiceForget( &inContext->opRef ); 2743 DNSServiceForget( &inContext->mainRef ); 2744 free( inContext ); 2745} 2746 2747//=========================================================================================================================== 2748// ResolveCallback 2749//=========================================================================================================================== 2750 2751static void DNSSD_API 2752 ResolveCallback( 2753 DNSServiceRef inSDRef, 2754 DNSServiceFlags inFlags, 2755 uint32_t inInterfaceIndex, 2756 DNSServiceErrorType inError, 2757 const char * inFullName, 2758 const char * inHostname, 2759 uint16_t inPort, 2760 uint16_t inTXTLen, 2761 const unsigned char * inTXTPtr, 2762 void * inContext ) 2763{ 2764 char time[ kTimestampBufLen ]; 2765 char errorStr[ 64 ]; 2766 2767 Unused( inSDRef ); 2768 Unused( inFlags ); 2769 Unused( inContext ); 2770 2771 GetTimestampStr( time ); 2772 2773 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError ); 2774 2775 FPrintF( stdout, "%s: %s can be reached at %s:%u (interface %d)%?s\n", 2776 time, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr ); 2777 if( inTXTLen == 1 ) 2778 { 2779 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX ); 2780 } 2781 else 2782 { 2783 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen ); 2784 } 2785} 2786 2787//=========================================================================================================================== 2788// GetAddrInfoPOSIXCmd 2789//=========================================================================================================================== 2790 2791#define AddressFamilyStr( X ) ( \ 2792 ( (X) == AF_INET ) ? "inet" : \ 2793 ( (X) == AF_INET6 ) ? "inet6" : \ 2794 ( (X) == AF_UNSPEC ) ? "unspec" : \ 2795 "???" ) 2796 2797static void GetAddrInfoPOSIXCmd( void ) 2798{ 2799 OSStatus err; 2800 struct addrinfo hints; 2801 const struct addrinfo * addrInfo; 2802 struct addrinfo * addrInfoList = NULL; 2803 char time[ kTimestampBufLen ]; 2804 2805 memset( &hints, 0, sizeof( hints ) ); 2806 hints.ai_socktype = SOCK_STREAM; 2807 2808 // Set hints address family. 2809 2810 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC; 2811 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET; 2812 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6; 2813 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC; 2814 else 2815 { 2816 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family ); 2817 err = kParamErr; 2818 goto exit; 2819 } 2820 2821 // Set hints flags. 2822 2823 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG; 2824 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL; 2825 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME; 2826 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST; 2827 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV; 2828 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE; 2829 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED; 2830#if( defined( AI_V4MAPPED_CFG ) ) 2831 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG; 2832#endif 2833#if( defined( AI_DEFAULT ) ) 2834 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT; 2835#endif 2836 2837 // Print prologue. 2838 2839 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName ); 2840 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName ); 2841 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) ); 2842 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags ); 2843 if( hints.ai_flags & AI_NUMERICSERV ) FPrintF( stdout, "AI_NUMERICSERV " ); 2844 if( hints.ai_flags & AI_V4MAPPED ) FPrintF( stdout, "AI_V4MAPPED " ); 2845 if( hints.ai_flags & AI_ADDRCONFIG ) FPrintF( stdout, "AI_ADDRCONFIG " ); 2846#if( defined( AI_V4MAPPED_CFG ) ) 2847 if( hints.ai_flags & AI_V4MAPPED_CFG ) FPrintF( stdout, "AI_V4MAPPED_CFG " ); 2848#endif 2849 if( hints.ai_flags & AI_ALL ) FPrintF( stdout, "AI_ALL " ); 2850 if( hints.ai_flags & AI_NUMERICHOST ) FPrintF( stdout, "AI_NUMERICHOST " ); 2851 if( hints.ai_flags & AI_CANONNAME ) FPrintF( stdout, "AI_CANONNAME " ); 2852 if( hints.ai_flags & AI_PASSIVE ) FPrintF( stdout, "AI_PASSIVE " ); 2853 FPrintF( stdout, ">\n" ); 2854 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 2855 FPrintF( stdout, "---\n" ); 2856 2857 // Call getaddrinfo(). 2858 2859 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList ); 2860 GetTimestampStr( time ); 2861 if( err ) 2862 { 2863 FPrintF( stderr, "Error %#m: %s.\n", err, gai_strerror( err ) ); 2864 } 2865 else 2866 { 2867 int addrCount = 0; 2868 2869 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; } 2870 2871 FPrintF( stdout, "Addresses (%d total):\n", addrCount ); 2872 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) 2873 { 2874 FPrintF( stdout, "%##a\n", addrInfo->ai_addr ); 2875 } 2876 } 2877 FPrintF( stdout, "---\n" ); 2878 FPrintF( stdout, "End time: %s\n", time ); 2879 2880exit: 2881 if( addrInfoList ) freeaddrinfo( addrInfoList ); 2882 if( err ) exit( 1 ); 2883} 2884 2885//=========================================================================================================================== 2886// ReverseLookupCmd 2887//=========================================================================================================================== 2888 2889static void ReverseLookupCmd( void ) 2890{ 2891 OSStatus err; 2892 QueryRecordContext * context = NULL; 2893 DNSServiceRef sdRef; 2894 dispatch_source_t signalSource = NULL; 2895 uint32_t ipv4Addr; 2896 uint8_t ipv6Addr[ 16 ]; 2897 char recordName[ ( 16 * 4 ) + 9 + 1 ]; 2898 int useMainConnection; 2899 2900 // Set up SIGINT handler. 2901 2902 signal( SIGINT, SIG_IGN ); 2903 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); 2904 require_noerr( err, exit ); 2905 dispatch_resume( signalSource ); 2906 2907 // Create context. 2908 2909 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) ); 2910 require_action( context, exit, err = kNoMemoryErr ); 2911 2912 // Check command parameters. 2913 2914 if( gReverseLookup_TimeLimitSecs < 0 ) 2915 { 2916 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs ); 2917 err = kParamErr; 2918 goto exit; 2919 } 2920 2921 // Create main connection. 2922 2923 if( gConnectionOpt ) 2924 { 2925 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL ); 2926 require_noerr_quiet( err, exit ); 2927 useMainConnection = true; 2928 } 2929 else 2930 { 2931 useMainConnection = false; 2932 } 2933 2934 // Get flags. 2935 2936 context->flags = GetDNSSDFlagsFromOpts(); 2937 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection; 2938 2939 // Get interface index. 2940 2941 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); 2942 require_noerr_quiet( err, exit ); 2943 2944 // Create reverse lookup record name. 2945 2946 err = StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, 2947 &ipv4Addr, NULL, NULL, NULL, NULL ); 2948 if( err ) 2949 { 2950 char * dst; 2951 int i; 2952 2953 err = StringToIPv6Address( gReverseLookup_IPAddr, 2954 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope, 2955 ipv6Addr, NULL, NULL, NULL, NULL ); 2956 if( err ) 2957 { 2958 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr ); 2959 err = kParamErr; 2960 goto exit; 2961 } 2962 dst = recordName; 2963 for( i = 15; i >= 0; --i ) 2964 { 2965 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] & 0x0F ]; 2966 *dst++ = '.'; 2967 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] >> 4 ]; 2968 *dst++ = '.'; 2969 } 2970 strcpy( dst, "ip6.arpa." ); 2971 check( ( strlen( recordName ) + 1 ) <= sizeof( recordName ) ); 2972 } 2973 else 2974 { 2975 SNPrintF( recordName, sizeof( recordName ), "%u.%u.%u.%u.in-addr.arpa.", 2976 ipv4Addr & 0xFF, 2977 ( ipv4Addr >> 8 ) & 0xFF, 2978 ( ipv4Addr >> 16 ) & 0xFF, 2979 ( ipv4Addr >> 24 ) & 0xFF ); 2980 } 2981 2982 // Set remaining parameters. 2983 2984 context->recordName = recordName; 2985 context->recordType = kDNSServiceType_PTR; 2986 context->timeLimitSecs = gReverseLookup_TimeLimitSecs; 2987 context->oneShotMode = gReverseLookup_OneShot ? true : false; 2988 2989 // Print prologue. 2990 2991 QueryRecordPrintPrologue( context ); 2992 2993 // Start operation. 2994 2995 if( useMainConnection ) sdRef = context->mainRef; 2996 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType, 2997 kDNSServiceClass_IN, QueryRecordCallback, context ); 2998 require_noerr( err, exit ); 2999 3000 context->opRef = sdRef; 3001 if( !useMainConnection ) 3002 { 3003 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() ); 3004 require_noerr( err, exit ); 3005 } 3006 3007 // Set time limit. 3008 3009 if( context->timeLimitSecs > 0 ) 3010 { 3011 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), 3012 kExitReason_TimeLimit, Exit ); 3013 } 3014 dispatch_main(); 3015 3016exit: 3017 dispatch_source_forget( &signalSource ); 3018 if( context ) QueryRecordContextFree( context ); 3019 if( err ) exit( 1 ); 3020} 3021 3022//=========================================================================================================================== 3023// BrowseAllCmd 3024//=========================================================================================================================== 3025 3026typedef struct BrowseDomain BrowseDomain; 3027typedef struct BrowseType BrowseType; 3028typedef struct BrowseOp BrowseOp; 3029typedef struct BrowseInstance BrowseInstance; 3030typedef struct BrowseIPAddr BrowseIPAddr; 3031 3032typedef struct 3033{ 3034 int refCount; 3035 DNSServiceRef mainRef; 3036 DNSServiceRef domainsQuery; 3037 const char * domain; 3038 BrowseDomain * domainList; 3039 char ** serviceTypes; 3040 size_t serviceTypesCount; 3041 dispatch_source_t exitTimer; 3042 uint32_t ifIndex; 3043 int pendingConnectCount; 3044 int browseTimeSecs; 3045 int connectTimeLimitSecs; 3046 Boolean includeAWDL; 3047 Boolean useColoredText; 3048 3049} BrowseAllContext; 3050 3051struct BrowseDomain 3052{ 3053 BrowseDomain * next; 3054 char * name; 3055 DNSServiceRef servicesQuery; 3056 BrowseAllContext * context; 3057 BrowseType * typeList; 3058}; 3059 3060struct BrowseType 3061{ 3062 BrowseType * next; 3063 char * name; 3064 BrowseOp * browseList; 3065}; 3066 3067struct BrowseOp 3068{ 3069 BrowseOp * next; 3070 BrowseAllContext * context; 3071 DNSServiceRef browse; 3072 uint64_t startTicks; 3073 BrowseInstance * instanceList; 3074 uint32_t ifIndex; 3075 Boolean isTCP; 3076}; 3077 3078struct BrowseInstance 3079{ 3080 BrowseInstance * next; 3081 BrowseAllContext * context; 3082 char * name; 3083 uint64_t foundTicks; 3084 DNSServiceRef resolve; 3085 uint64_t resolveStartTicks; 3086 uint64_t resolveDoneTicks; 3087 DNSServiceRef getAddr; 3088 uint64_t getAddrStartTicks; 3089 BrowseIPAddr * addrList; 3090 uint8_t * txtPtr; 3091 size_t txtLen; 3092 char * hostname; 3093 uint32_t ifIndex; 3094 uint16_t port; 3095 Boolean isTCP; 3096}; 3097 3098typedef enum 3099{ 3100 kConnectStatus_None = 0, 3101 kConnectStatus_Pending = 1, 3102 kConnectStatus_Succeeded = 2, 3103 kConnectStatus_Failed = 3 3104 3105} ConnectStatus; 3106 3107struct BrowseIPAddr 3108{ 3109 BrowseIPAddr * next; 3110 sockaddr_ip sip; 3111 int refCount; 3112 BrowseAllContext * context; 3113 uint64_t foundTicks; 3114 AsyncConnectionRef connection; 3115 ConnectStatus connectStatus; 3116 CFTimeInterval connectTimeSecs; 3117 OSStatus connectError; 3118}; 3119 3120static void BrowseAllPrintPrologue( const BrowseAllContext *inContext ); 3121static void DNSSD_API 3122 BrowseAllQueryDomainsCallback( 3123 DNSServiceRef inSDRef, 3124 DNSServiceFlags inFlags, 3125 uint32_t inInterfaceIndex, 3126 DNSServiceErrorType inError, 3127 const char * inFullName, 3128 uint16_t inType, 3129 uint16_t inClass, 3130 uint16_t inRDataLen, 3131 const void * inRDataPtr, 3132 uint32_t inTTL, 3133 void * inContext ); 3134static void DNSSD_API 3135 BrowseAllQueryCallback( 3136 DNSServiceRef inSDRef, 3137 DNSServiceFlags inFlags, 3138 uint32_t inInterfaceIndex, 3139 DNSServiceErrorType inError, 3140 const char * inFullName, 3141 uint16_t inType, 3142 uint16_t inClass, 3143 uint16_t inRDataLen, 3144 const void * inRDataPtr, 3145 uint32_t inTTL, 3146 void * inContext ); 3147static void DNSSD_API 3148 BrowseAllBrowseCallback( 3149 DNSServiceRef inSDRef, 3150 DNSServiceFlags inFlags, 3151 uint32_t inInterfaceIndex, 3152 DNSServiceErrorType inError, 3153 const char * inName, 3154 const char * inRegType, 3155 const char * inDomain, 3156 void * inContext ); 3157static void DNSSD_API 3158 BrowseAllResolveCallback( 3159 DNSServiceRef inSDRef, 3160 DNSServiceFlags inFlags, 3161 uint32_t inInterfaceIndex, 3162 DNSServiceErrorType inError, 3163 const char * inFullName, 3164 const char * inHostname, 3165 uint16_t inPort, 3166 uint16_t inTXTLen, 3167 const unsigned char * inTXTPtr, 3168 void * inContext ); 3169static void DNSSD_API 3170 BrowseAllGAICallback( 3171 DNSServiceRef inSDRef, 3172 DNSServiceFlags inFlags, 3173 uint32_t inInterfaceIndex, 3174 DNSServiceErrorType inError, 3175 const char * inHostname, 3176 const struct sockaddr * inSockAddr, 3177 uint32_t inTTL, 3178 void * inContext ); 3179static void BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg ); 3180static void BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg ); 3181static void BrowseAllStop( void *inContext ); 3182static void BrowseAllExit( void *inContext ); 3183static OSStatus BrowseAllAddDomain( BrowseAllContext *inContext, const char *inName ); 3184static OSStatus BrowseAllRemoveDomain( BrowseAllContext *inContext, const char *inName ); 3185static void BrowseAllContextRelease( BrowseAllContext *inContext ); 3186static OSStatus 3187 BrowseAllAddServiceType( 3188 BrowseAllContext * inContext, 3189 BrowseDomain * inDomain, 3190 const char * inName, 3191 uint32_t inIfIndex, 3192 Boolean inIncludeAWDL ); 3193static OSStatus 3194 BrowseAllRemoveServiceType( 3195 BrowseAllContext * inContext, 3196 BrowseDomain * inDomain, 3197 const char * inName, 3198 uint32_t inIfIndex ); 3199static OSStatus 3200 BrowseAllAddServiceInstance( 3201 BrowseAllContext * inContext, 3202 BrowseOp * inBrowse, 3203 const char * inName, 3204 const char * inRegType, 3205 const char * inDomain, 3206 uint32_t inIfIndex ); 3207static OSStatus 3208 BrowseAllRemoveServiceInstance( 3209 BrowseAllContext * inContext, 3210 BrowseOp * inBrowse, 3211 const char * inName, 3212 uint32_t inIfIndex ); 3213static OSStatus 3214 BrowseAllAddIPAddress( 3215 BrowseAllContext * inContext, 3216 BrowseInstance * inInstance, 3217 const struct sockaddr * inSockAddr ); 3218static OSStatus 3219 BrowseAllRemoveIPAddress( 3220 BrowseAllContext * inContext, 3221 BrowseInstance * inInstance, 3222 const struct sockaddr * inSockAddr ); 3223static void BrowseDomainFree( BrowseDomain *inDomain ); 3224static void BrowseTypeFree( BrowseType *inType ); 3225static void BrowseOpFree( BrowseOp *inBrowse ); 3226static void BrowseInstanceFree( BrowseInstance *inInstance ); 3227static void BrowseIPAddrRelease( BrowseIPAddr *inAddr ); 3228static void BrowseIPAddrReleaseList( BrowseIPAddr *inList ); 3229 3230#define ForgetIPAddressList( X ) ForgetCustom( X, BrowseIPAddrReleaseList ) 3231#define ForgetBrowseAllContext( X ) ForgetCustom( X, BrowseAllContextRelease ) 3232 3233#define kBrowseAllOpenFileMin 4096 3234 3235static void BrowseAllCmd( void ) 3236{ 3237 OSStatus err; 3238 BrowseAllContext * context = NULL; 3239 3240 // Check command parameters. 3241 3242 if( gBrowseAll_BrowseTimeSecs <= 0 ) 3243 { 3244 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs ); 3245 err = kParamErr; 3246 goto exit; 3247 } 3248 3249#if( TARGET_OS_POSIX ) 3250 // Set open file minimum. 3251 3252 { 3253 struct rlimit fdLimits; 3254 3255 err = getrlimit( RLIMIT_NOFILE, &fdLimits ); 3256 err = map_global_noerr_errno( err ); 3257 require_noerr( err, exit ); 3258 3259 if( fdLimits.rlim_cur < kBrowseAllOpenFileMin ) 3260 { 3261 fdLimits.rlim_cur = kBrowseAllOpenFileMin; 3262 err = setrlimit( RLIMIT_NOFILE, &fdLimits ); 3263 err = map_global_noerr_errno( err ); 3264 require_noerr( err, exit ); 3265 } 3266 } 3267#endif 3268 3269 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) ); 3270 require_action( context, exit, err = kNoMemoryErr ); 3271 3272 context->refCount = 1; 3273 context->domain = gBrowseAll_Domain; 3274 context->serviceTypes = gBrowseAll_ServiceTypes; 3275 context->serviceTypesCount = gBrowseAll_ServiceTypesCount; 3276 gBrowseAll_ServiceTypes = NULL; 3277 gBrowseAll_ServiceTypesCount = 0; 3278 context->browseTimeSecs = gBrowseAll_BrowseTimeSecs; 3279 context->connectTimeLimitSecs = gBrowseAll_ConnectTimeLimitSecs; 3280 context->includeAWDL = gBrowseAll_IncludeAWDL ? true : false; 3281#if( TARGET_OS_POSIX ) 3282 context->useColoredText = isatty( STDOUT_FILENO ) ? true : false; 3283#endif 3284 3285 err = DNSServiceCreateConnection( &context->mainRef ); 3286 require_noerr( err, exit ); 3287 3288 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() ); 3289 require_noerr( err, exit ); 3290 3291 // Set interface index. 3292 3293 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); 3294 require_noerr_quiet( err, exit ); 3295 3296 BrowseAllPrintPrologue( context ); 3297 3298 if( context->domain ) 3299 { 3300 err = BrowseAllAddDomain( context, context->domain ); 3301 require_noerr( err, exit ); 3302 } 3303 else 3304 { 3305 DNSServiceRef sdRef; 3306 3307 sdRef = context->mainRef; 3308 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, 3309 "b._dns-sd._udp.local.", kDNSServiceType_PTR, kDNSServiceClass_IN, BrowseAllQueryDomainsCallback, context ); 3310 require_noerr( err, exit ); 3311 3312 context->domainsQuery = sdRef; 3313 } 3314 3315 dispatch_after_f( dispatch_time_seconds( context->browseTimeSecs ), dispatch_get_main_queue(), context, BrowseAllStop ); 3316 dispatch_main(); 3317 3318exit: 3319 if( context ) BrowseAllContextRelease( context ); 3320 if( err ) exit( 1 ); 3321} 3322 3323//=========================================================================================================================== 3324// BrowseAllPrintPrologue 3325//=========================================================================================================================== 3326 3327static void BrowseAllPrintPrologue( const BrowseAllContext *inContext ) 3328{ 3329 size_t i; 3330 char ifName[ kInterfaceNameBufLen ]; 3331 char time[ kTimestampBufLen ]; 3332 3333 InterfaceIndexToName( inContext->ifIndex, ifName ); 3334 3335 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); 3336 FPrintF( stdout, "Service types: "); 3337 if( inContext->serviceTypesCount > 0 ) 3338 { 3339 FPrintF( stdout, "%s", inContext->serviceTypes[ 0 ] ); 3340 for( i = 1; i < inContext->serviceTypesCount; ++i ) FPrintF( stdout, ", %s", inContext->serviceTypes[ i ] ); 3341 FPrintF( stdout, "\n" ); 3342 } 3343 else 3344 { 3345 FPrintF( stdout, "all services\n" ); 3346 } 3347 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "default domains" ); 3348 FPrintF( stdout, "Browse time: %d second%?c\n", inContext->browseTimeSecs, inContext->browseTimeSecs != 1, 's' ); 3349 FPrintF( stdout, "Connect time limit: %d second%?c\n", 3350 inContext->connectTimeLimitSecs, inContext->connectTimeLimitSecs != 1, 's' ); 3351 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 3352 FPrintF( stdout, "---\n" ); 3353} 3354 3355//=========================================================================================================================== 3356// BrowseAllQueryDomainsCallback 3357//=========================================================================================================================== 3358 3359static void DNSSD_API 3360 BrowseAllQueryDomainsCallback( 3361 DNSServiceRef inSDRef, 3362 DNSServiceFlags inFlags, 3363 uint32_t inInterfaceIndex, 3364 DNSServiceErrorType inError, 3365 const char * inFullName, 3366 uint16_t inType, 3367 uint16_t inClass, 3368 uint16_t inRDataLen, 3369 const void * inRDataPtr, 3370 uint32_t inTTL, 3371 void * inContext ) 3372{ 3373 OSStatus err; 3374 BrowseAllContext * const context = (BrowseAllContext *) inContext; 3375 char domainStr[ kDNSServiceMaxDomainName ]; 3376 3377 Unused( inSDRef ); 3378 Unused( inInterfaceIndex ); 3379 Unused( inFullName ); 3380 Unused( inType ); 3381 Unused( inClass ); 3382 Unused( inTTL ); 3383 3384 err = inError; 3385 require_noerr( err, exit ); 3386 3387 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL ); 3388 require_noerr( err, exit ); 3389 3390 if( inFlags & kDNSServiceFlagsAdd ) 3391 { 3392 err = BrowseAllAddDomain( context, domainStr ); 3393 if( err == kDuplicateErr ) err = kNoErr; 3394 require_noerr( err, exit ); 3395 } 3396 else 3397 { 3398 err = BrowseAllRemoveDomain( context, domainStr ); 3399 if( err == kNotFoundErr ) err = kNoErr; 3400 require_noerr( err, exit ); 3401 } 3402 3403exit: 3404 if( err ) exit( 1 ); 3405} 3406 3407//=========================================================================================================================== 3408// BrowseAllQueryCallback 3409//=========================================================================================================================== 3410 3411static void DNSSD_API 3412 BrowseAllQueryCallback( 3413 DNSServiceRef inSDRef, 3414 DNSServiceFlags inFlags, 3415 uint32_t inInterfaceIndex, 3416 DNSServiceErrorType inError, 3417 const char * inFullName, 3418 uint16_t inType, 3419 uint16_t inClass, 3420 uint16_t inRDataLen, 3421 const void * inRDataPtr, 3422 uint32_t inTTL, 3423 void * inContext ) 3424{ 3425 OSStatus err; 3426 BrowseDomain * const domain = (BrowseDomain *) inContext; 3427 const uint8_t * firstLabel; 3428 const uint8_t * secondLabel; 3429 char * serviceTypeStr = NULL; 3430 const uint8_t * const end = ( (uint8_t * ) inRDataPtr ) + inRDataLen; 3431 3432 Unused( inSDRef ); 3433 Unused( inFullName ); 3434 Unused( inTTL ); 3435 Unused( inType ); 3436 Unused( inClass ); 3437 3438 err = inError; 3439 require_noerr( err, exit ); 3440 3441 check( inType == kDNSServiceType_PTR ); 3442 check( inClass == kDNSServiceClass_IN ); 3443 require_action( inRDataLen > 0, exit, err = kSizeErr ); 3444 3445 firstLabel = inRDataPtr; 3446 require_action_quiet( ( firstLabel + 1 + firstLabel[ 0 ] ) < end , exit, err = kUnderrunErr ); 3447 3448 secondLabel = firstLabel + 1 + firstLabel[ 0 ]; 3449 require_action_quiet( ( secondLabel + 1 + secondLabel[ 0 ] ) < end , exit, err = kUnderrunErr ); 3450 3451 ASPrintF( &serviceTypeStr, "%#s.%#s", firstLabel, secondLabel ); 3452 require_action( serviceTypeStr, exit, err = kNoMemoryErr ); 3453 3454 if( inFlags & kDNSServiceFlagsAdd ) 3455 { 3456 err = BrowseAllAddServiceType( domain->context, domain, serviceTypeStr, inInterfaceIndex, false ); 3457 if( err == kDuplicateErr ) err = kNoErr; 3458 require_noerr( err, exit ); 3459 } 3460 else 3461 { 3462 err = BrowseAllRemoveServiceType( domain->context, domain, serviceTypeStr, inInterfaceIndex ); 3463 if( err == kNotFoundErr ) err = kNoErr; 3464 require_noerr( err, exit ); 3465 } 3466 3467exit: 3468 FreeNullSafe( serviceTypeStr ); 3469} 3470 3471//=========================================================================================================================== 3472// BrowseAllBrowseCallback 3473//=========================================================================================================================== 3474 3475static void DNSSD_API 3476 BrowseAllBrowseCallback( 3477 DNSServiceRef inSDRef, 3478 DNSServiceFlags inFlags, 3479 uint32_t inInterfaceIndex, 3480 DNSServiceErrorType inError, 3481 const char * inName, 3482 const char * inRegType, 3483 const char * inDomain, 3484 void * inContext ) 3485{ 3486 OSStatus err; 3487 BrowseOp * const browse = (BrowseOp *) inContext; 3488 3489 Unused( inSDRef ); 3490 3491 err = inError; 3492 require_noerr( err, exit ); 3493 3494 if( inFlags & kDNSServiceFlagsAdd ) 3495 { 3496 err = BrowseAllAddServiceInstance( browse->context, browse, inName, inRegType, inDomain, inInterfaceIndex ); 3497 if( err == kDuplicateErr ) err = kNoErr; 3498 require_noerr( err, exit ); 3499 } 3500 else 3501 { 3502 err = BrowseAllRemoveServiceInstance( browse->context, browse, inName, inInterfaceIndex ); 3503 if( err == kNotFoundErr ) err = kNoErr; 3504 require_noerr( err, exit ); 3505 } 3506 3507exit: 3508 return; 3509} 3510 3511//=========================================================================================================================== 3512// BrowseAllResolveCallback 3513//=========================================================================================================================== 3514 3515static void DNSSD_API 3516 BrowseAllResolveCallback( 3517 DNSServiceRef inSDRef, 3518 DNSServiceFlags inFlags, 3519 uint32_t inInterfaceIndex, 3520 DNSServiceErrorType inError, 3521 const char * inFullName, 3522 const char * inHostname, 3523 uint16_t inPort, 3524 uint16_t inTXTLen, 3525 const unsigned char * inTXTPtr, 3526 void * inContext ) 3527{ 3528 OSStatus err; 3529 const uint64_t nowTicks = UpTicks(); 3530 BrowseInstance * const instance = (BrowseInstance *) inContext; 3531 3532 Unused( inSDRef ); 3533 Unused( inFlags ); 3534 Unused( inInterfaceIndex ); 3535 Unused( inFullName ); 3536 3537 err = inError; 3538 require_noerr( err, exit ); 3539 3540 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) ) 3541 { 3542 FreeNullSafe( instance->txtPtr ); 3543 instance->txtPtr = malloc( inTXTLen ); 3544 require_action( instance->txtPtr, exit, err = kNoMemoryErr ); 3545 3546 memcpy( instance->txtPtr, inTXTPtr, inTXTLen ); 3547 instance->txtLen = inTXTLen; 3548 } 3549 3550 instance->port = ntohs( inPort ); 3551 3552 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) ) 3553 { 3554 DNSServiceRef sdRef; 3555 3556 if( !instance->hostname ) instance->resolveDoneTicks = nowTicks; 3557 FreeNullSafe( instance->hostname ); 3558 instance->hostname = strdup( inHostname ); 3559 require_action( instance->hostname, exit, err = kNoMemoryErr ); 3560 3561 DNSServiceForget( &instance->getAddr ); 3562 ForgetIPAddressList( &instance->addrList ); 3563 3564 sdRef = instance->context->mainRef; 3565 instance->getAddrStartTicks = UpTicks(); 3566 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex, 3567 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, BrowseAllGAICallback, instance ); 3568 require_noerr( err, exit ); 3569 3570 instance->getAddr = sdRef; 3571 } 3572 3573exit: 3574 if( err ) exit( 1 ); 3575} 3576 3577//=========================================================================================================================== 3578// BrowseAllGAICallback 3579//=========================================================================================================================== 3580 3581static void DNSSD_API 3582 BrowseAllGAICallback( 3583 DNSServiceRef inSDRef, 3584 DNSServiceFlags inFlags, 3585 uint32_t inInterfaceIndex, 3586 DNSServiceErrorType inError, 3587 const char * inHostname, 3588 const struct sockaddr * inSockAddr, 3589 uint32_t inTTL, 3590 void * inContext ) 3591{ 3592 OSStatus err; 3593 BrowseInstance * const instance = (BrowseInstance *) inContext; 3594 3595 Unused( inSDRef ); 3596 Unused( inInterfaceIndex ); 3597 Unused( inHostname ); 3598 Unused( inTTL ); 3599 3600 err = inError; 3601 require_noerr( err, exit ); 3602 3603 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) ) 3604 { 3605 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family ); 3606 goto exit; 3607 } 3608 3609 if( inFlags & kDNSServiceFlagsAdd ) 3610 { 3611 err = BrowseAllAddIPAddress( instance->context, instance, inSockAddr ); 3612 if( err == kDuplicateErr ) err = kNoErr; 3613 require_noerr( err, exit ); 3614 } 3615 else 3616 { 3617 err = BrowseAllRemoveIPAddress( instance->context, instance, inSockAddr ); 3618 if( err == kNotFoundErr ) err = kNoErr; 3619 require_noerr( err, exit ); 3620 } 3621 3622exit: 3623 return; 3624} 3625 3626//=========================================================================================================================== 3627// BrowseAllConnectionProgress 3628//=========================================================================================================================== 3629 3630static void BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg ) 3631{ 3632 BrowseIPAddr * const addr = (BrowseIPAddr *) inArg; 3633 3634 if( inPhase == kAsyncConnectionPhase_Connected ) 3635 { 3636 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails; 3637 3638 addr->connectTimeSecs = info->connectSecs; 3639 } 3640} 3641 3642//=========================================================================================================================== 3643// BrowseAllConnectionHandler 3644//=========================================================================================================================== 3645 3646static void BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg ) 3647{ 3648 BrowseIPAddr * const addr = (BrowseIPAddr *) inArg; 3649 BrowseAllContext * const context = addr->context; 3650 3651 if( inError ) 3652 { 3653 addr->connectStatus = kConnectStatus_Failed; 3654 addr->connectError = inError; 3655 } 3656 else 3657 { 3658 addr->connectStatus = kConnectStatus_Succeeded; 3659 } 3660 3661 check( context->pendingConnectCount > 0 ); 3662 if( --context->pendingConnectCount == 0 ) 3663 { 3664 if( context->exitTimer ) 3665 { 3666 dispatch_source_forget( &context->exitTimer ); 3667 dispatch_async_f( dispatch_get_main_queue(), context, BrowseAllExit ); 3668 } 3669 } 3670 3671 ForgetSocket( &inSock ); 3672 BrowseIPAddrRelease( addr ); 3673} 3674 3675//=========================================================================================================================== 3676// BrowseAllStop 3677//=========================================================================================================================== 3678 3679static void BrowseAllStop( void *inContext ) 3680{ 3681 OSStatus err; 3682 BrowseAllContext * const context = (BrowseAllContext *) inContext; 3683 BrowseDomain * domain; 3684 BrowseType * type; 3685 BrowseOp * browse; 3686 BrowseInstance * instance; 3687 3688 DNSServiceForget( &context->domainsQuery ); 3689 for( domain = context->domainList; domain; domain = domain->next ) 3690 { 3691 DNSServiceForget( &domain->servicesQuery ); 3692 for( type = domain->typeList; type; type = type->next ) 3693 { 3694 for( browse = type->browseList; browse; browse = browse->next ) 3695 { 3696 DNSServiceForget( &browse->browse ); 3697 for( instance = browse->instanceList; instance; instance = instance->next ) 3698 { 3699 DNSServiceForget( &instance->resolve ); 3700 DNSServiceForget( &instance->getAddr ); 3701 } 3702 } 3703 } 3704 } 3705 DNSServiceForget( &context->mainRef ); 3706 3707 if( ( context->pendingConnectCount > 0 ) && ( context->connectTimeLimitSecs > 0 ) ) 3708 { 3709 check( !context->exitTimer ); 3710 err = DispatchTimerCreate( dispatch_time_seconds( context->connectTimeLimitSecs ), DISPATCH_TIME_FOREVER, 3711 100 * kNanosecondsPerMillisecond, BrowseAllExit, NULL, context, &context->exitTimer ); 3712 require_noerr( err, exit ); 3713 dispatch_resume( context->exitTimer ); 3714 } 3715 else 3716 { 3717 dispatch_async_f( dispatch_get_main_queue(), context, BrowseAllExit ); 3718 } 3719 err = kNoErr; 3720 3721exit: 3722 if( err ) exit( 1 ); 3723} 3724 3725//=========================================================================================================================== 3726// BrowseAllExit 3727//=========================================================================================================================== 3728 3729#define kStatusStr_CouldConnect "connected" 3730#define kStatusStr_CouldConnectColored kANSIGreen kStatusStr_CouldConnect kANSINormal 3731#define kStatusStr_CouldNotConnect "could not connect" 3732#define kStatusStr_CouldNotConnectColored kANSIRed kStatusStr_CouldNotConnect kANSINormal 3733#define kStatusStr_NoConnectionAttempted "no connection attempted" 3734#define kStatusStr_Unknown "unknown" 3735 3736#define Indent( X ) ( (X) * 4 ), "" 3737 3738static void BrowseAllExit( void *inContext ) 3739{ 3740 BrowseAllContext * const context = (BrowseAllContext *) inContext; 3741 BrowseDomain * domain; 3742 BrowseType * type; 3743 BrowseOp * browse; 3744 BrowseInstance * instance; 3745 BrowseIPAddr * addr; 3746 3747 dispatch_source_forget( &context->exitTimer ); 3748 3749 for( domain = context->domainList; domain; domain = domain->next ) 3750 { 3751 FPrintF( stdout, "%s\n\n", domain->name ); 3752 3753 for( type = domain->typeList; type; type = type->next ) 3754 { 3755 const char * desc; 3756 3757 desc = ServiceTypeDescription( type->name ); 3758 if( desc ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), desc, type->name ); 3759 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name ); 3760 3761 for( browse = type->browseList; browse; browse = browse->next ) 3762 { 3763 for( instance = browse->instanceList; instance; instance = instance->next ) 3764 { 3765 char ifname[ IF_NAMESIZE + 1 ]; 3766 3767 FPrintF( stdout, "%*s" "%s via ", Indent( 2 ), instance->name ); 3768 FPrintF( stdout, "%*s" "%s via ", Indent( 2 ), instance->name ); 3769 if( instance->ifIndex == 0 ) 3770 { 3771 FPrintF( stdout, "the Internet" ); 3772 } 3773 else if( if_indextoname( instance->ifIndex, ifname ) ) 3774 { 3775 NetTransportType netType; 3776 3777 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3778 &netType ); 3779 FPrintF( stdout, "%s (%s)", 3780 ( netType == kNetTransportType_Ethernet ) ? "ethernet" : NetTransportTypeToString( netType ), 3781 ifname ); 3782 } 3783 else 3784 { 3785 FPrintF( stdout, "interface index %u", instance->ifIndex ); 3786 } 3787 FPrintF( stdout, "\n\n" ); 3788 3789 if( instance->hostname ) 3790 { 3791 char buffer[ 256 ]; 3792 3793 SNPrintF( buffer, sizeof( buffer ), "%s:%u", instance->hostname, instance->port ); 3794 FPrintF( stdout, "%*s" "%-51s %4llu ms\n", Indent( 3 ), buffer, 3795 UpTicksToMilliseconds( instance->resolveDoneTicks - instance->resolveStartTicks ) ); 3796 } 3797 else 3798 { 3799 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port ); 3800 } 3801 3802 for( addr = instance->addrList; addr; addr = addr->next ) 3803 { 3804 AsyncConnection_Forget( &addr->connection ); 3805 3806 if( addr->connectStatus == kConnectStatus_Pending ) 3807 { 3808 addr->connectStatus = kConnectStatus_Failed; 3809 addr->connectError = kTimeoutErr; 3810 } 3811 3812 FPrintF( stdout, "%*s" "%-##47a %4llu ms (", Indent( 4 ), 3813 &addr->sip.sa, UpTicksToMilliseconds( addr->foundTicks - instance->getAddrStartTicks ) ); 3814 switch( addr->connectStatus ) 3815 { 3816 case kConnectStatus_None: 3817 FPrintF( stdout, "%s", kStatusStr_NoConnectionAttempted ); 3818 break; 3819 3820 case kConnectStatus_Succeeded: 3821 FPrintF( stdout, "%s in %.2f ms", 3822 context->useColoredText ? kStatusStr_CouldConnectColored : kStatusStr_CouldConnect, 3823 addr->connectTimeSecs * 1000 ); 3824 break; 3825 3826 case kConnectStatus_Failed: 3827 FPrintF( stdout, "%s: %m", 3828 context->useColoredText ? kStatusStr_CouldNotConnectColored : kStatusStr_CouldNotConnect, 3829 addr->connectError ); 3830 break; 3831 3832 default: 3833 FPrintF( stdout, "%s", kStatusStr_Unknown ); 3834 break; 3835 } 3836 FPrintF( stdout, ")\n" ); 3837 } 3838 3839 FPrintF( stdout, "\n" ); 3840 if( instance->txtLen == 0 ) continue; 3841 3842 FPrintF( stdout, "%*s" "TXT record:\n", Indent( 3 ) ); 3843 if( instance->txtLen > 1 ) 3844 { 3845 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen ); 3846 } 3847 else 3848 { 3849 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX ); 3850 } 3851 FPrintF( stdout, "\n" ); 3852 } 3853 } 3854 FPrintF( stdout, "\n" ); 3855 } 3856 } 3857 3858 while( ( domain = context->domainList ) != NULL ) 3859 { 3860 context->domainList = domain->next; 3861 BrowseDomainFree( domain ); 3862 } 3863 3864 BrowseAllContextRelease( context ); 3865 Exit( NULL ); 3866} 3867 3868//=========================================================================================================================== 3869// BrowseAllAddDomain 3870//=========================================================================================================================== 3871 3872static OSStatus BrowseAllAddDomain( BrowseAllContext *inContext, const char *inName ) 3873{ 3874 OSStatus err; 3875 BrowseDomain * domain; 3876 BrowseDomain ** p; 3877 BrowseDomain * newDomain = NULL; 3878 3879 for( p = &inContext->domainList; ( domain = *p ) != NULL; p = &domain->next ) 3880 { 3881 if( strcasecmp( domain->name, inName ) == 0 ) break; 3882 } 3883 require_action_quiet( !domain, exit, err = kDuplicateErr ); 3884 3885 newDomain = (BrowseDomain *) calloc( 1, sizeof( *newDomain ) ); 3886 require_action( newDomain, exit, err = kNoMemoryErr ); 3887 3888 ++inContext->refCount; 3889 newDomain->context = inContext; 3890 3891 newDomain->name = strdup( inName ); 3892 require_action( newDomain->name, exit, err = kNoMemoryErr ); 3893 3894 if( inContext->serviceTypesCount > 0 ) 3895 { 3896 size_t i; 3897 3898 for( i = 0; i < inContext->serviceTypesCount; ++i ) 3899 { 3900 err = BrowseAllAddServiceType( inContext, newDomain, inContext->serviceTypes[ i ], inContext->ifIndex, 3901 inContext->includeAWDL ); 3902 if( err == kDuplicateErr ) err = kNoErr; 3903 require_noerr( err, exit ); 3904 } 3905 } 3906 else 3907 { 3908 char * recordName; 3909 DNSServiceFlags flags; 3910 DNSServiceRef sdRef; 3911 3912 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name ); 3913 require_action( recordName, exit, err = kNoMemoryErr ); 3914 3915 flags = kDNSServiceFlagsShareConnection; 3916 if( inContext->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL; 3917 3918 sdRef = newDomain->context->mainRef; 3919 err = DNSServiceQueryRecord( &sdRef, flags, inContext->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN, 3920 BrowseAllQueryCallback, newDomain ); 3921 free( recordName ); 3922 require_noerr( err, exit ); 3923 3924 newDomain->servicesQuery = sdRef; 3925 } 3926 3927 *p = newDomain; 3928 newDomain = NULL; 3929 err = kNoErr; 3930 3931exit: 3932 if( newDomain ) BrowseDomainFree( newDomain ); 3933 return( err ); 3934} 3935 3936//=========================================================================================================================== 3937// BrowseAllRemoveDomain 3938//=========================================================================================================================== 3939 3940static OSStatus BrowseAllRemoveDomain( BrowseAllContext *inContext, const char *inName ) 3941{ 3942 OSStatus err; 3943 BrowseDomain * domain; 3944 BrowseDomain ** p; 3945 3946 for( p = &inContext->domainList; ( domain = *p ) != NULL; p = &domain->next ) 3947 { 3948 if( strcasecmp( domain->name, inName ) == 0 ) break; 3949 } 3950 3951 if( domain ) 3952 { 3953 *p = domain->next; 3954 BrowseDomainFree( domain ); 3955 err = kNoErr; 3956 } 3957 else 3958 { 3959 err = kNotFoundErr; 3960 } 3961 3962 return( err ); 3963} 3964 3965//=========================================================================================================================== 3966// BrowseAllContextRelease 3967//=========================================================================================================================== 3968 3969static void BrowseAllContextRelease( BrowseAllContext *inContext ) 3970{ 3971 if( --inContext->refCount == 0 ) 3972 { 3973 check( !inContext->domainsQuery ); 3974 check( !inContext->domainList ); 3975 check( !inContext->exitTimer ); 3976 check( !inContext->pendingConnectCount ); 3977 DNSServiceForget( &inContext->mainRef ); 3978 if( inContext->serviceTypes ) 3979 { 3980 StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount ); 3981 inContext->serviceTypes = NULL; 3982 inContext->serviceTypesCount = 0; 3983 } 3984 free( inContext ); 3985 } 3986} 3987 3988//=========================================================================================================================== 3989// BrowseAllAddServiceType 3990//=========================================================================================================================== 3991 3992static OSStatus 3993 BrowseAllAddServiceType( 3994 BrowseAllContext * inContext, 3995 BrowseDomain * inDomain, 3996 const char * inName, 3997 uint32_t inIfIndex, 3998 Boolean inIncludeAWDL ) 3999{ 4000 OSStatus err; 4001 DNSServiceRef sdRef; 4002 DNSServiceFlags flags; 4003 BrowseType * type; 4004 BrowseType ** typePtr; 4005 BrowseType * newType = NULL; 4006 BrowseOp * browse; 4007 BrowseOp ** browsePtr; 4008 BrowseOp * newBrowse = NULL; 4009 4010 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next ) 4011 { 4012 if( strcasecmp( type->name, inName ) == 0 ) break; 4013 } 4014 if( !type ) 4015 { 4016 newType = (BrowseType *) calloc( 1, sizeof( *newType ) ); 4017 require_action( newType, exit, err = kNoMemoryErr ); 4018 4019 newType->name = strdup( inName ); 4020 require_action( newType->name, exit, err = kNoMemoryErr ); 4021 4022 type = newType; 4023 } 4024 4025 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next ) 4026 { 4027 if( browse->ifIndex == inIfIndex ) break; 4028 } 4029 require_action_quiet( !browse, exit, err = kDuplicateErr ); 4030 4031 newBrowse = (BrowseOp *) calloc( 1, sizeof( *newBrowse ) ); 4032 require_action( newBrowse, exit, err = kNoMemoryErr ); 4033 4034 ++inContext->refCount; 4035 newBrowse->context = inContext; 4036 newBrowse->ifIndex = inIfIndex; 4037 if( stricmp_suffix( inName, "._tcp" ) == 0 ) newBrowse->isTCP = true; 4038 4039 flags = kDNSServiceFlagsShareConnection; 4040 if( inIncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL; 4041 4042 newBrowse->startTicks = UpTicks(); 4043 4044 sdRef = inContext->mainRef; 4045 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, BrowseAllBrowseCallback, 4046 newBrowse ); 4047 require_noerr( err, exit ); 4048 4049 newBrowse->browse = sdRef; 4050 *browsePtr = newBrowse; 4051 newBrowse = NULL; 4052 4053 if( newType ) 4054 { 4055 *typePtr = newType; 4056 newType = NULL; 4057 } 4058 4059exit: 4060 if( newBrowse ) BrowseOpFree( newBrowse ); 4061 if( newType ) BrowseTypeFree( newType ); 4062 return( err ); 4063} 4064 4065//=========================================================================================================================== 4066// BrowseAllRemoveServiceType 4067//=========================================================================================================================== 4068 4069static OSStatus 4070 BrowseAllRemoveServiceType( 4071 BrowseAllContext * inContext, 4072 BrowseDomain * inDomain, 4073 const char * inName, 4074 uint32_t inIfIndex ) 4075{ 4076 OSStatus err; 4077 BrowseType * type; 4078 BrowseType ** typePtr; 4079 BrowseOp * browse; 4080 BrowseOp ** browsePtr; 4081 4082 Unused( inContext ); 4083 4084 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next ) 4085 { 4086 if( strcasecmp( type->name, inName ) == 0 ) break; 4087 } 4088 require_action_quiet( type, exit, err = kNotFoundErr ); 4089 4090 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next ) 4091 { 4092 if( browse->ifIndex == inIfIndex ) break; 4093 } 4094 require_action_quiet( browse, exit, err = kNotFoundErr ); 4095 4096 *browsePtr = browse->next; 4097 BrowseOpFree( browse ); 4098 if( !type->browseList ) 4099 { 4100 *typePtr = type->next; 4101 BrowseTypeFree( type ); 4102 } 4103 err = kNoErr; 4104 4105exit: 4106 return( err ); 4107} 4108 4109//=========================================================================================================================== 4110// BrowseAllAddServiceInstance 4111//=========================================================================================================================== 4112 4113static OSStatus 4114 BrowseAllAddServiceInstance( 4115 BrowseAllContext * inContext, 4116 BrowseOp * inBrowse, 4117 const char * inName, 4118 const char * inRegType, 4119 const char * inDomain, 4120 uint32_t inIfIndex ) 4121{ 4122 OSStatus err; 4123 DNSServiceRef sdRef; 4124 BrowseInstance * instance; 4125 BrowseInstance ** p; 4126 const uint64_t nowTicks = UpTicks(); 4127 BrowseInstance * newInstance = NULL; 4128 4129 for( p = &inBrowse->instanceList; ( instance = *p ) != NULL; p = &instance->next ) 4130 { 4131 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break; 4132 } 4133 require_action_quiet( !instance, exit, err = kDuplicateErr ); 4134 4135 newInstance = (BrowseInstance *) calloc( 1, sizeof( *newInstance ) ); 4136 require_action( newInstance, exit, err = kNoMemoryErr ); 4137 4138 ++inContext->refCount; 4139 newInstance->context = inContext; 4140 newInstance->foundTicks = nowTicks; 4141 newInstance->ifIndex = inIfIndex; 4142 newInstance->isTCP = inBrowse->isTCP; 4143 4144 newInstance->name = strdup( inName ); 4145 require_action( newInstance->name, exit, err = kNoMemoryErr ); 4146 4147 sdRef = inContext->mainRef; 4148 newInstance->resolveStartTicks = UpTicks(); 4149 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain, 4150 BrowseAllResolveCallback, newInstance ); 4151 require_noerr( err, exit ); 4152 4153 newInstance->resolve = sdRef; 4154 *p = newInstance; 4155 newInstance = NULL; 4156 4157exit: 4158 if( newInstance ) BrowseInstanceFree( newInstance ); 4159 return( err ); 4160} 4161 4162//=========================================================================================================================== 4163// BrowseAllRemoveServiceInstance 4164//=========================================================================================================================== 4165 4166static OSStatus 4167 BrowseAllRemoveServiceInstance( 4168 BrowseAllContext * inContext, 4169 BrowseOp * inBrowse, 4170 const char * inName, 4171 uint32_t inIfIndex ) 4172{ 4173 OSStatus err; 4174 BrowseInstance * instance; 4175 BrowseInstance ** p; 4176 4177 Unused( inContext ); 4178 4179 for( p = &inBrowse->instanceList; ( instance = *p ) != NULL; p = &instance->next ) 4180 { 4181 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break; 4182 } 4183 require_action_quiet( instance, exit, err = kNotFoundErr ); 4184 4185 *p = instance->next; 4186 BrowseInstanceFree( instance ); 4187 err = kNoErr; 4188 4189exit: 4190 return( err ); 4191} 4192 4193//=========================================================================================================================== 4194// BrowseAllAddIPAddress 4195//=========================================================================================================================== 4196 4197#define kDiscardProtocolPort 9 4198 4199static OSStatus 4200 BrowseAllAddIPAddress( 4201 BrowseAllContext * inContext, 4202 BrowseInstance * inInstance, 4203 const struct sockaddr * inSockAddr ) 4204{ 4205 OSStatus err; 4206 BrowseIPAddr * addr; 4207 BrowseIPAddr ** p; 4208 const uint64_t nowTicks = UpTicks(); 4209 BrowseIPAddr * newAddr = NULL; 4210 4211 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) ) 4212 { 4213 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family ); 4214 err = kTypeErr; 4215 goto exit; 4216 } 4217 4218 for( p = &inInstance->addrList; ( addr = *p ) != NULL; p = &addr->next ) 4219 { 4220 if( SockAddrCompareAddr( &addr->sip, inSockAddr ) == 0 ) break; 4221 } 4222 require_action_quiet( !addr, exit, err = kDuplicateErr ); 4223 4224 newAddr = (BrowseIPAddr *) calloc( 1, sizeof( *newAddr ) ); 4225 require_action( newAddr, exit, err = kNoMemoryErr ); 4226 4227 ++inContext->refCount; 4228 newAddr->refCount = 1; 4229 newAddr->context = inContext; 4230 newAddr->foundTicks = nowTicks; 4231 SockAddrCopy( inSockAddr, &newAddr->sip.sa ); 4232 4233 if( inInstance->isTCP && ( inInstance->port != kDiscardProtocolPort ) ) 4234 { 4235 char destination[ kSockAddrStringMaxSize ]; 4236 4237 err = SockAddrToString( &newAddr->sip, kSockAddrStringFlagsNoPort, destination ); 4238 require_noerr( err, exit ); 4239 4240 err = AsyncConnection_Connect( &newAddr->connection, destination, -inInstance->port, kAsyncConnectionFlag_P2P, 4241 kAsyncConnectionNoTimeout, kSocketBufferSize_DontSet, kSocketBufferSize_DontSet, 4242 BrowseAllConnectionProgress, newAddr, BrowseAllConnectionHandler, newAddr, dispatch_get_main_queue() ); 4243 require_noerr( err, exit ); 4244 4245 ++newAddr->refCount; 4246 newAddr->connectStatus = kConnectStatus_Pending; 4247 ++inContext->pendingConnectCount; 4248 } 4249 4250 *p = newAddr; 4251 newAddr = NULL; 4252 err = kNoErr; 4253 4254exit: 4255 if( newAddr ) BrowseIPAddrRelease( newAddr ); 4256 return( err ); 4257} 4258 4259//=========================================================================================================================== 4260// BrowseAllRemoveIPAddress 4261//=========================================================================================================================== 4262 4263static OSStatus 4264 BrowseAllRemoveIPAddress( 4265 BrowseAllContext * inContext, 4266 BrowseInstance * inInstance, 4267 const struct sockaddr * inSockAddr ) 4268{ 4269 OSStatus err; 4270 BrowseIPAddr * addr; 4271 BrowseIPAddr ** p; 4272 4273 Unused( inContext ); 4274 4275 for( p = &inInstance->addrList; ( addr = *p ) != NULL; p = &addr->next ) 4276 { 4277 if( SockAddrCompareAddr( &addr->sip.sa, inSockAddr ) == 0 ) break; 4278 } 4279 require_action_quiet( addr, exit, err = kNotFoundErr ); 4280 4281 *p = addr->next; 4282 BrowseIPAddrRelease( addr ); 4283 err = kNoErr; 4284 4285exit: 4286 return( err ); 4287} 4288 4289//=========================================================================================================================== 4290// BrowseDomainFree 4291//=========================================================================================================================== 4292 4293static void BrowseDomainFree( BrowseDomain *inDomain ) 4294{ 4295 BrowseType * type; 4296 4297 ForgetBrowseAllContext( &inDomain->context ); 4298 ForgetMem( &inDomain->name ); 4299 DNSServiceForget( &inDomain->servicesQuery ); 4300 while( ( type = inDomain->typeList ) != NULL ) 4301 { 4302 inDomain->typeList = type->next; 4303 BrowseTypeFree( type ); 4304 } 4305 free( inDomain ); 4306} 4307 4308//=========================================================================================================================== 4309// BrowseTypeFree 4310//=========================================================================================================================== 4311 4312static void BrowseTypeFree( BrowseType *inType ) 4313{ 4314 BrowseOp * browse; 4315 4316 ForgetMem( &inType->name ); 4317 while( ( browse = inType->browseList ) != NULL ) 4318 { 4319 inType->browseList = browse->next; 4320 BrowseOpFree( browse ); 4321 } 4322 free( inType ); 4323} 4324 4325//=========================================================================================================================== 4326// BrowseOpFree 4327//=========================================================================================================================== 4328 4329static void BrowseOpFree( BrowseOp *inBrowse ) 4330{ 4331 BrowseInstance * instance; 4332 4333 ForgetBrowseAllContext( &inBrowse->context ); 4334 DNSServiceForget( &inBrowse->browse ); 4335 while( ( instance = inBrowse->instanceList ) != NULL ) 4336 { 4337 inBrowse->instanceList = instance->next; 4338 BrowseInstanceFree( instance ); 4339 } 4340 free( inBrowse ); 4341} 4342 4343//=========================================================================================================================== 4344// BrowseInstanceFree 4345//=========================================================================================================================== 4346 4347static void BrowseInstanceFree( BrowseInstance *inInstance ) 4348{ 4349 ForgetBrowseAllContext( &inInstance->context ); 4350 ForgetMem( &inInstance->name ); 4351 DNSServiceForget( &inInstance->resolve ); 4352 DNSServiceForget( &inInstance->getAddr ); 4353 ForgetMem( &inInstance->txtPtr ); 4354 ForgetMem( &inInstance->hostname ); 4355 ForgetIPAddressList( &inInstance->addrList ); 4356 free( inInstance ); 4357} 4358 4359//=========================================================================================================================== 4360// BrowseIPAddrRelease 4361//=========================================================================================================================== 4362 4363static void BrowseIPAddrRelease( BrowseIPAddr *inAddr ) 4364{ 4365 AsyncConnection_Forget( &inAddr->connection ); 4366 if( --inAddr->refCount == 0 ) 4367 { 4368 ForgetBrowseAllContext( &inAddr->context ); 4369 free( inAddr ); 4370 } 4371} 4372 4373//=========================================================================================================================== 4374// BrowseIPAddrReleaseList 4375//=========================================================================================================================== 4376 4377static void BrowseIPAddrReleaseList( BrowseIPAddr *inList ) 4378{ 4379 BrowseIPAddr * addr; 4380 4381 while( ( addr = inList ) != NULL ) 4382 { 4383 inList = addr->next; 4384 BrowseIPAddrRelease( addr ); 4385 } 4386} 4387 4388//=========================================================================================================================== 4389// GetAddrInfoStressCmd 4390//=========================================================================================================================== 4391 4392typedef struct 4393{ 4394 DNSServiceRef mainRef; 4395 DNSServiceRef sdRef; 4396 DNSServiceFlags flags; 4397 unsigned int interfaceIndex; 4398 unsigned int connectionNumber; 4399 unsigned int requestCount; 4400 unsigned int requestCountMax; 4401 unsigned int requestCountLimit; 4402 unsigned int durationMinMs; 4403 unsigned int durationMaxMs; 4404 4405} GAIStressContext; 4406 4407static void GetAddrInfoStressEvent( void *inContext ); 4408static void DNSSD_API 4409 GetAddrInfoStressCallback( 4410 DNSServiceRef inSDRef, 4411 DNSServiceFlags inFlags, 4412 uint32_t inInterfaceIndex, 4413 DNSServiceErrorType inError, 4414 const char * inHostname, 4415 const struct sockaddr * inSockAddr, 4416 uint32_t inTTL, 4417 void * inContext ); 4418 4419static void GetAddrInfoStressCmd( void ) 4420{ 4421 OSStatus err; 4422 GAIStressContext * context = NULL; 4423 int i; 4424 DNSServiceFlags flags; 4425 uint32_t ifIndex; 4426 char ifName[ kInterfaceNameBufLen ]; 4427 char time[ kTimestampBufLen ]; 4428 4429 if( gGAIStress_TestDurationSecs < 0 ) 4430 { 4431 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs ); 4432 err = kParamErr; 4433 goto exit; 4434 } 4435 if( gGAIStress_ConnectionCount <= 0 ) 4436 { 4437 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount ); 4438 err = kParamErr; 4439 goto exit; 4440 } 4441 if( gGAIStress_DurationMinMs <= 0 ) 4442 { 4443 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs ); 4444 err = kParamErr; 4445 goto exit; 4446 } 4447 if( gGAIStress_DurationMaxMs <= 0 ) 4448 { 4449 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs ); 4450 err = kParamErr; 4451 goto exit; 4452 } 4453 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs ) 4454 { 4455 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n", 4456 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs ); 4457 err = kParamErr; 4458 goto exit; 4459 } 4460 if( gGAIStress_RequestCountMax <= 0 ) 4461 { 4462 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax ); 4463 err = kParamErr; 4464 goto exit; 4465 } 4466 4467 // Set flags. 4468 4469 flags = GetDNSSDFlagsFromOpts(); 4470 4471 // Set interface index. 4472 4473 err = InterfaceIndexFromArgString( gInterface, &ifIndex ); 4474 require_noerr_quiet( err, exit ); 4475 4476 for( i = 0; i < gGAIStress_ConnectionCount; ++i ) 4477 { 4478 context = (GAIStressContext *) calloc( 1, sizeof( *context ) ); 4479 require_action( context, exit, err = kNoMemoryErr ); 4480 4481 context->flags = flags; 4482 context->interfaceIndex = ifIndex; 4483 context->connectionNumber = (unsigned int)( i + 1 ); 4484 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax; 4485 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs; 4486 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs; 4487 4488 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent ); 4489 context = NULL; 4490 } 4491 4492 if( gGAIStress_TestDurationSecs > 0 ) 4493 { 4494 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit ); 4495 } 4496 4497 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors ); 4498 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) ); 4499 FPrintF( stdout, "Test duration: " ); 4500 if( gGAIStress_TestDurationSecs == 0 ) 4501 { 4502 FPrintF( stdout, "���\n" ); 4503 } 4504 else 4505 { 4506 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs ); 4507 } 4508 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount ); 4509 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs ); 4510 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs ); 4511 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax ); 4512 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 4513 FPrintF( stdout, "---\n" ); 4514 4515 dispatch_main(); 4516 4517exit: 4518 FreeNullSafe( context ); 4519 if( err ) exit( 1 ); 4520} 4521 4522//=========================================================================================================================== 4523// GetAddrInfoStressEvent 4524//=========================================================================================================================== 4525 4526#define kStressRandStrLen 5 4527 4528#define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz" 4529 4530static void GetAddrInfoStressEvent( void *inContext ) 4531{ 4532 GAIStressContext * const context = (GAIStressContext *) inContext; 4533 OSStatus err; 4534 DNSServiceRef sdRef; 4535 unsigned int nextMs; 4536 char randomStr[ kStressRandStrLen + 1 ]; 4537 char hostname[ kStressRandStrLen + 4 + 1 ]; 4538 char time[ kTimestampBufLen ]; 4539 Boolean isConnectionNew = false; 4540 static Boolean printedHeader = false; 4541 4542 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) ) 4543 { 4544 DNSServiceForget( &context->mainRef ); 4545 context->sdRef = NULL; 4546 context->requestCount = 0; 4547 context->requestCountLimit = RandomRange( 1, context->requestCountMax ); 4548 4549 err = DNSServiceCreateConnection( &context->mainRef ); 4550 require_noerr( err, exit ); 4551 4552 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() ); 4553 require_noerr( err, exit ); 4554 4555 isConnectionNew = true; 4556 } 4557 4558 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr ); 4559 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr ); 4560 4561 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs ); 4562 4563 if( !printedHeader ) 4564 { 4565 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" ); 4566 printedHeader = true; 4567 } 4568 FPrintF( stdout, "%-26s %3u%c %9s %8u\n", 4569 GetTimestampStr( time ), context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs ); 4570 4571 DNSServiceForget( &context->sdRef ); 4572 sdRef = context->mainRef; 4573 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex, 4574 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL ); 4575 require_noerr( err, exit ); 4576 context->sdRef = sdRef; 4577 4578 context->requestCount++; 4579 4580 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent ); 4581 4582exit: 4583 if( err ) exit( 1 ); 4584} 4585 4586//=========================================================================================================================== 4587// GetAddrInfoStressCallback 4588//=========================================================================================================================== 4589 4590static void DNSSD_API 4591 GetAddrInfoStressCallback( 4592 DNSServiceRef inSDRef, 4593 DNSServiceFlags inFlags, 4594 uint32_t inInterfaceIndex, 4595 DNSServiceErrorType inError, 4596 const char * inHostname, 4597 const struct sockaddr * inSockAddr, 4598 uint32_t inTTL, 4599 void * inContext ) 4600{ 4601 Unused( inSDRef ); 4602 Unused( inFlags ); 4603 Unused( inInterfaceIndex ); 4604 Unused( inError ); 4605 Unused( inHostname ); 4606 Unused( inSockAddr ); 4607 Unused( inTTL ); 4608 Unused( inContext ); 4609} 4610 4611//=========================================================================================================================== 4612// DNSQueryCmd 4613//=========================================================================================================================== 4614 4615#define kDNSPort 53 4616 4617typedef struct 4618{ 4619 sockaddr_ip serverAddr; 4620 uint64_t sendTicks; 4621 uint8_t * msgPtr; 4622 size_t msgLen; 4623 size_t msgOffset; 4624 const char * name; 4625 dispatch_source_t readSource; 4626 SocketRef sock; 4627 int timeLimitSecs; 4628 uint16_t queryID; 4629 uint16_t type; 4630 Boolean haveTCPLen; 4631 Boolean useTCP; 4632 Boolean printRawRData; // True if RDATA results are not to be formatted. 4633 uint8_t msgBuf[ 512 ]; 4634 4635} DNSQueryContext; 4636 4637static void DNSQueryPrintPrologue( const DNSQueryContext *inContext ); 4638static void DNSQueryReadHandler( void *inContext ); 4639static void DNSQueryCancelHandler( void *inContext ); 4640 4641static void DNSQueryCmd( void ) 4642{ 4643 OSStatus err; 4644 DNSQueryContext * context = NULL; 4645 uint8_t * msgPtr; 4646 size_t msgLen, sendLen; 4647 4648 // Check command parameters. 4649 4650 if( gDNSQuery_TimeLimitSecs < -1 ) 4651 { 4652 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs ); 4653 err = kParamErr; 4654 goto exit; 4655 } 4656 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) ) 4657 { 4658 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags ); 4659 err = kParamErr; 4660 goto exit; 4661 } 4662 4663 // Create context. 4664 4665 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) ); 4666 require_action( context, exit, err = kNoMemoryErr ); 4667 4668 context->name = gDNSQuery_Name; 4669 context->sock = kInvalidSocketRef; 4670 context->timeLimitSecs = gDNSQuery_TimeLimitSecs; 4671 context->queryID = (uint16_t) Random32(); 4672 context->useTCP = gDNSQuery_UseTCP ? true : false; 4673 context->printRawRData = gDNSQuery_RawRData ? true : false; 4674 4675#if( TARGET_OS_DARWIN ) 4676 if( gDNSQuery_Server ) 4677#endif 4678 { 4679 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL ); 4680 require_noerr( err, exit ); 4681 } 4682#if( TARGET_OS_DARWIN ) 4683 else 4684 { 4685 err = GetDefaultDNSServer( &context->serverAddr ); 4686 require_noerr( err, exit ); 4687 } 4688#endif 4689 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort ); 4690 4691 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type ); 4692 require_noerr( err, exit ); 4693 4694 // Write query message. 4695 4696 check_compile_time_code( sizeof( context->msgBuf ) >= ( kDNSQueryMessageMaxLen + 2 ) ); 4697 4698 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf; 4699 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type, 4700 kDNSServiceClass_IN, &msgLen ); 4701 require_noerr( err, exit ); 4702 check( msgLen <= UINT16_MAX ); 4703 4704 if( context->useTCP ) 4705 { 4706 WriteBig16( context->msgBuf, msgLen ); 4707 sendLen = 2 + msgLen; 4708 } 4709 else 4710 { 4711 sendLen = msgLen; 4712 } 4713 4714 DNSQueryPrintPrologue( context ); 4715 4716 if( gDNSQuery_Verbose ) 4717 { 4718 FPrintF( stdout, "DNS message to send:\n\n" ); 4719 PrintUDNSMessage( msgPtr, msgLen, false ); 4720 FPrintF( stdout, "---\n" ); 4721 } 4722 4723 if( context->useTCP ) 4724 { 4725 // Create TCP socket. 4726 4727 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP ); 4728 err = map_socket_creation_errno( context->sock ); 4729 require_noerr( err, exit ); 4730 4731 err = SocketConnect( context->sock, &context->serverAddr, 5 ); 4732 require_noerr( err, exit ); 4733 } 4734 else 4735 { 4736 // Create UDP socket. 4737 4738 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock ); 4739 require_noerr( err, exit ); 4740 } 4741 4742 context->sendTicks = UpTicks(); 4743 err = SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 ); 4744 require_noerr( err, exit ); 4745 4746 if( context->timeLimitSecs == 0 ) goto exit; 4747 4748 err = DispatchReadSourceCreate( context->sock, DNSQueryReadHandler, DNSQueryCancelHandler, context, 4749 &context->readSource ); 4750 require_noerr( err, exit ); 4751 dispatch_resume( context->readSource ); 4752 4753 if( context->timeLimitSecs > 0 ) 4754 { 4755 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout, 4756 Exit ); 4757 } 4758 dispatch_main(); 4759 4760exit: 4761 if( context ) 4762 { 4763 dispatch_source_forget( &context->readSource ); 4764 ForgetSocket( &context->sock ); 4765 free( context ); 4766 } 4767 if( err ) exit( 1 ); 4768} 4769 4770//=========================================================================================================================== 4771// DNSQueryPrintPrologue 4772//=========================================================================================================================== 4773 4774static void DNSQueryPrintPrologue( const DNSQueryContext *inContext ) 4775{ 4776 const int timeLimitSecs = inContext->timeLimitSecs; 4777 char time[ kTimestampBufLen ]; 4778 4779 FPrintF( stdout, "Name: %s\n", inContext->name ); 4780 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type ); 4781 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr ); 4782 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" ); 4783 FPrintF( stdout, "Time limit: " ); 4784 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); 4785 else FPrintF( stdout, "���\n" ); 4786 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 4787 FPrintF( stdout, "---\n" ); 4788} 4789 4790//=========================================================================================================================== 4791// DNSQueryReadHandler 4792//=========================================================================================================================== 4793 4794static void DNSQueryReadHandler( void *inContext ) 4795{ 4796 OSStatus err; 4797 const uint64_t nowTicks = UpTicks(); 4798 DNSQueryContext * const context = (DNSQueryContext *) inContext; 4799 char time[ kTimestampBufLen ]; 4800 4801 GetTimestampStr( time ); 4802 4803 if( context->useTCP ) 4804 { 4805 if( !context->haveTCPLen ) 4806 { 4807 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset ); 4808 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; } 4809 require_noerr( err, exit ); 4810 4811 context->msgOffset = 0; 4812 context->msgLen = ReadBig16( context->msgBuf ); 4813 context->haveTCPLen = true; 4814 if( context->msgLen <= sizeof( context->msgBuf ) ) 4815 { 4816 context->msgPtr = context->msgBuf; 4817 } 4818 else 4819 { 4820 context->msgPtr = (uint8_t *) malloc( context->msgLen ); 4821 require_action( context->msgPtr, exit, err = kNoMemoryErr ); 4822 } 4823 } 4824 4825 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset ); 4826 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; } 4827 require_noerr( err, exit ); 4828 context->msgOffset = 0; 4829 context->haveTCPLen = false; 4830 } 4831 else 4832 { 4833 sockaddr_ip fromAddr; 4834 4835 context->msgPtr = context->msgBuf; 4836 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr, 4837 sizeof( fromAddr ), NULL, NULL, NULL, NULL ); 4838 require_noerr( err, exit ); 4839 4840 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 ); 4841 } 4842 4843 FPrintF( stdout, "Receive time: %s\n", time ); 4844 FPrintF( stdout, "Source: %##a\n", &context->serverAddr ); 4845 FPrintF( stdout, "Message size: %zu\n", context->msgLen ); 4846 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) ); 4847 PrintUDNSMessage( context->msgPtr, context->msgLen, context->printRawRData ); 4848 4849 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) ) 4850 { 4851 Exit( kExitReason_ReceivedResponse ); 4852 } 4853 4854exit: 4855 if( err ) dispatch_source_forget( &context->readSource ); 4856} 4857 4858//=========================================================================================================================== 4859// DNSQueryCancelHandler 4860//=========================================================================================================================== 4861 4862static void DNSQueryCancelHandler( void *inContext ) 4863{ 4864 DNSQueryContext * const context = (DNSQueryContext *) inContext; 4865 4866 check( !context->readSource ); 4867 ForgetSocket( &context->sock ); 4868 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr ); 4869 free( context ); 4870 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit ); 4871} 4872 4873#if( DNSSDUTIL_INCLUDE_DNSCRYPT ) 4874//=========================================================================================================================== 4875// DNSCryptCmd 4876//=========================================================================================================================== 4877 4878#define kDNSCryptPort 443 4879 4880#define kDNSCryptMinPadLength 8 4881#define kDNSCryptMaxPadLength 256 4882#define kDNSCryptBlockSize 64 4883#define kDNSCryptCertMinimumLength 124 4884#define kDNSCryptClientMagicLength 8 4885#define kDNSCryptResolverMagicLength 8 4886#define kDNSCryptHalfNonceLength 12 4887#define kDNSCryptCertMagicLength 4 4888 4889check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES ); 4890 4891static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' }; 4892static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] = 4893{ 4894 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38 4895}; 4896 4897typedef struct 4898{ 4899 uint8_t certMagic[ kDNSCryptCertMagicLength ]; 4900 uint8_t esVersion[ 2 ]; 4901 uint8_t minorVersion[ 2 ]; 4902 uint8_t signature[ crypto_sign_BYTES ]; 4903 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ]; 4904 uint8_t clientMagic[ kDNSCryptClientMagicLength ]; 4905 uint8_t serial[ 4 ]; 4906 uint8_t startTime[ 4 ]; 4907 uint8_t endTime[ 4 ]; 4908 uint8_t extensions[ 1 ]; // Variably-sized extension data. 4909 4910} DNSCryptCert; 4911 4912check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength ); 4913 4914typedef struct 4915{ 4916 uint8_t clientMagic[ kDNSCryptClientMagicLength ]; 4917 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ]; 4918 uint8_t clientNonce[ kDNSCryptHalfNonceLength ]; 4919 uint8_t poly1305MAC[ 16 ]; 4920 4921} DNSCryptQueryHeader; 4922 4923check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 ); 4924check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES ); 4925check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) == 4926 offsetof( DNSCryptQueryHeader, poly1305MAC ) ); 4927 4928typedef struct 4929{ 4930 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ]; 4931 uint8_t clientNonce[ kDNSCryptHalfNonceLength ]; 4932 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ]; 4933 uint8_t poly1305MAC[ 16 ]; 4934 4935} DNSCryptResponseHeader; 4936 4937check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 ); 4938check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES ); 4939check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) == 4940 sizeof( DNSCryptResponseHeader ) ); 4941 4942typedef struct 4943{ 4944 sockaddr_ip serverAddr; 4945 uint64_t sendTicks; 4946 const char * providerName; 4947 const char * qname; 4948 const uint8_t * certPtr; 4949 size_t certLen; 4950 dispatch_source_t readSource; 4951 size_t msgLen; 4952 int timeLimitSecs; 4953 uint16_t queryID; 4954 uint16_t qtype; 4955 Boolean printRawRData; 4956 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ]; 4957 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ]; 4958 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ]; 4959 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ]; 4960 uint8_t clientMagic[ kDNSCryptClientMagicLength ]; 4961 uint8_t clientNonce[ kDNSCryptHalfNonceLength ]; 4962 uint8_t nmKey[ crypto_box_BEFORENMBYTES ]; 4963 uint8_t msgBuf[ 512 ]; 4964 4965} DNSCryptContext; 4966 4967static void DNSCryptReceiveCertHandler( void *inContext ); 4968static void DNSCryptReceiveResponseHandler( void *inContext ); 4969static void DNSCryptProceed( void *inContext ); 4970static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext ); 4971static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext ); 4972static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext ); 4973static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen ); 4974 4975static void DNSCryptCmd( void ) 4976{ 4977 OSStatus err; 4978 DNSCryptContext * context = NULL; 4979 size_t writtenBytes; 4980 size_t totalBytes; 4981 SocketContext * sockContext; 4982 SocketRef sock = kInvalidSocketRef; 4983 const char * ptr; 4984 4985 // Check command parameters. 4986 4987 if( gDNSCrypt_TimeLimitSecs < -1 ) 4988 { 4989 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs ); 4990 err = kParamErr; 4991 goto exit; 4992 } 4993 4994 // Create context. 4995 4996 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) ); 4997 require_action( context, exit, err = kNoMemoryErr ); 4998 4999 context->providerName = gDNSCrypt_ProviderName; 5000 context->qname = gDNSCrypt_Name; 5001 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs; 5002 context->printRawRData = gDNSCrypt_RawRData ? true : false; 5003 5004 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey ); 5005 require_noerr( err, exit ); 5006 5007 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags, 5008 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr ); 5009 if( err || ( *ptr != '\0' ) ) 5010 { 5011 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey ); 5012 goto exit; 5013 } 5014 else if( totalBytes != sizeof( context->serverPublicSignKey ) ) 5015 { 5016 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n", 5017 totalBytes, sizeof( context->serverPublicSignKey ) ); 5018 err = kSizeErr; 5019 goto exit; 5020 } 5021 check( writtenBytes == totalBytes ); 5022 5023 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL ); 5024 require_noerr( err, exit ); 5025 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort ); 5026 5027 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype ); 5028 require_noerr( err, exit ); 5029 5030 // Write query message. 5031 5032 context->queryID = (uint16_t) Random32(); 5033 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName, 5034 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen ); 5035 require_noerr( err, exit ); 5036 5037 // Create UDP socket. 5038 5039 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock ); 5040 require_noerr( err, exit ); 5041 5042 // Send DNS query. 5043 5044 context->sendTicks = UpTicks(); 5045 err = SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 ); 5046 require_noerr( err, exit ); 5047 5048 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); 5049 require_action( sockContext, exit, err = kNoMemoryErr ); 5050 5051 err = DispatchReadSourceCreate( sock, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockContext, 5052 &context->readSource ); 5053 if( err ) ForgetMem( &sockContext ); 5054 require_noerr( err, exit ); 5055 5056 sockContext->context = context; 5057 sockContext->sock = sock; 5058 sock = kInvalidSocketRef; 5059 dispatch_resume( context->readSource ); 5060 5061 if( context->timeLimitSecs > 0 ) 5062 { 5063 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout, 5064 Exit ); 5065 } 5066 dispatch_main(); 5067 5068exit: 5069 if( context ) free( context ); 5070 ForgetSocket( &sock ); 5071 if( err ) exit( 1 ); 5072} 5073 5074//=========================================================================================================================== 5075// DNSCryptReceiveCertHandler 5076//=========================================================================================================================== 5077 5078static void DNSCryptReceiveCertHandler( void *inContext ) 5079{ 5080 OSStatus err; 5081 const uint64_t nowTicks = UpTicks(); 5082 SocketContext * const sockContext = (SocketContext *) inContext; 5083 DNSCryptContext * const context = (DNSCryptContext *) sockContext->context; 5084 const DNSHeader * hdr; 5085 sockaddr_ip fromAddr; 5086 const uint8_t * ptr; 5087 const uint8_t * txtPtr; 5088 size_t txtLen; 5089 unsigned int answerCount, i; 5090 uint8_t targetName[ kDomainNameLengthMax ]; 5091 char time[ kTimestampBufLen ]; 5092 5093 GetTimestampStr( time ); 5094 5095 dispatch_source_forget( &context->readSource ); 5096 5097 err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen, 5098 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL ); 5099 require_noerr( err, exit ); 5100 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 ); 5101 5102 FPrintF( stdout, "Receive time: %s\n", time ); 5103 FPrintF( stdout, "Source: %##a\n", &context->serverAddr ); 5104 FPrintF( stdout, "Message size: %zu\n", context->msgLen ); 5105 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) ); 5106 5107 PrintUDNSMessage( context->msgBuf, context->msgLen, context->printRawRData ); 5108 5109 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr ); 5110 5111 hdr = (DNSHeader *) context->msgBuf; 5112 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr ); 5113 5114 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr ); 5115 require_noerr( err, exit ); 5116 5117 targetName[ 0 ] = 0; 5118 err = DomainNameAppendString( targetName, context->providerName, NULL ); 5119 require_noerr( err, exit ); 5120 5121 answerCount = DNSHeaderGetAnswerCount( hdr ); 5122 for( i = 0; i < answerCount; ++i ) 5123 { 5124 uint16_t type; 5125 uint16_t class; 5126 uint8_t name[ kDomainNameLengthMax ]; 5127 5128 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen, 5129 &ptr ); 5130 require_noerr( err, exit ); 5131 5132 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) ) 5133 { 5134 break; 5135 } 5136 } 5137 5138 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) ) 5139 { 5140 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 ); 5141 err = kSizeErr; 5142 goto exit; 5143 } 5144 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength ) 5145 { 5146 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength ); 5147 err = kSizeErr; 5148 goto exit; 5149 } 5150 5151 context->certLen = txtPtr[ 0 ]; 5152 context->certPtr = &txtPtr[ 1 ]; 5153 5154 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed ); 5155 5156exit: 5157 if( err ) Exit( NULL ); 5158} 5159 5160//=========================================================================================================================== 5161// DNSCryptReceiveResponseHandler 5162//=========================================================================================================================== 5163 5164static void DNSCryptReceiveResponseHandler( void *inContext ) 5165{ 5166 OSStatus err; 5167 const uint64_t nowTicks = UpTicks(); 5168 SocketContext * const sockContext = (SocketContext *) inContext; 5169 DNSCryptContext * const context = (DNSCryptContext *) sockContext->context; 5170 sockaddr_ip fromAddr; 5171 DNSCryptResponseHeader * hdr; 5172 const uint8_t * end; 5173 uint8_t * ciphertext; 5174 uint8_t * plaintext; 5175 const uint8_t * response; 5176 char time[ kTimestampBufLen ]; 5177 uint8_t nonce[ crypto_box_NONCEBYTES ]; 5178 5179 GetTimestampStr( time ); 5180 5181 dispatch_source_forget( &context->readSource ); 5182 5183 err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen, 5184 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL ); 5185 require_noerr( err, exit ); 5186 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 ); 5187 5188 FPrintF( stdout, "Receive time: %s\n", time ); 5189 FPrintF( stdout, "Source: %##a\n", &context->serverAddr ); 5190 FPrintF( stdout, "Message size: %zu\n", context->msgLen ); 5191 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) ); 5192 5193 if( context->msgLen < sizeof( DNSCryptResponseHeader ) ) 5194 { 5195 FPrintF( stderr, "DNSCrypt response is too short.\n" ); 5196 err = kSizeErr; 5197 goto exit; 5198 } 5199 5200 hdr = (DNSCryptResponseHeader *) context->msgBuf; 5201 5202 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 ) 5203 { 5204 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n", 5205 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX, 5206 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX ); 5207 err = kValueErr; 5208 goto exit; 5209 } 5210 5211 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 ) 5212 { 5213 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" ); 5214 err = kValueErr; 5215 goto exit; 5216 } 5217 5218 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES ); 5219 5220 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES; 5221 memset( ciphertext, 0, crypto_box_BOXZEROBYTES ); 5222 5223 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES; 5224 check( plaintext == ciphertext ); 5225 5226 end = context->msgBuf + context->msgLen; 5227 5228 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey ); 5229 require_noerr( err, exit ); 5230 5231 response = plaintext + crypto_box_ZEROBYTES; 5232 PrintUDNSMessage( response, (size_t)( end - response ), context->printRawRData ); 5233 Exit( kExitReason_ReceivedResponse ); 5234 5235exit: 5236 if( err ) Exit( NULL ); 5237} 5238 5239//=========================================================================================================================== 5240// DNSCryptProceed 5241//=========================================================================================================================== 5242 5243static void DNSCryptProceed( void *inContext ) 5244{ 5245 OSStatus err; 5246 DNSCryptContext * const context = (DNSCryptContext *) inContext; 5247 5248 err = DNSCryptProcessCert( context ); 5249 require_noerr_quiet( err, exit ); 5250 5251 err = DNSCryptBuildQuery( context ); 5252 require_noerr_quiet( err, exit ); 5253 5254 err = DNSCryptSendQuery( context ); 5255 require_noerr_quiet( err, exit ); 5256 5257exit: 5258 if( err ) Exit( NULL ); 5259} 5260 5261//=========================================================================================================================== 5262// DNSCryptProcessCert 5263//=========================================================================================================================== 5264 5265static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext ) 5266{ 5267 OSStatus err; 5268 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr; 5269 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen; 5270 struct timeval now; 5271 time_t startTimeSecs, endTimeSecs; 5272 size_t signedLen; 5273 uint8_t * tempBuf; 5274 unsigned long long tempLen; 5275 5276 DNSCryptPrintCertificate( cert, inContext->certLen ); 5277 5278 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 ) 5279 { 5280 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n", 5281 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX, 5282 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX ); 5283 err = kValueErr; 5284 goto exit; 5285 } 5286 5287 startTimeSecs = (time_t) ReadBig32( cert->startTime ); 5288 endTimeSecs = (time_t) ReadBig32( cert->endTime ); 5289 5290 gettimeofday( &now, NULL ); 5291 if( now.tv_sec < startTimeSecs ) 5292 { 5293 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" ); 5294 err = kDateErr; 5295 goto exit; 5296 } 5297 if( now.tv_sec >= endTimeSecs ) 5298 { 5299 FPrintF( stderr, "DNSCrypt certificate has expired.\n" ); 5300 err = kDateErr; 5301 goto exit; 5302 } 5303 5304 signedLen = (size_t)( certEnd - cert->signature ); 5305 tempBuf = (uint8_t *) malloc( signedLen ); 5306 require_action( tempBuf, exit, err = kNoMemoryErr ); 5307 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey ); 5308 free( tempBuf ); 5309 if( err ) 5310 { 5311 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" ); 5312 err = kAuthenticationErr; 5313 goto exit; 5314 } 5315 5316 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES ); 5317 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength ); 5318 5319 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey ); 5320 require_noerr( err, exit ); 5321 5322 inContext->certPtr = NULL; 5323 inContext->certLen = 0; 5324 inContext->msgLen = 0; 5325 5326exit: 5327 return( err ); 5328} 5329 5330//=========================================================================================================================== 5331// DNSCryptBuildQuery 5332//=========================================================================================================================== 5333 5334static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen ); 5335 5336static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext ) 5337{ 5338 OSStatus err; 5339 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf; 5340 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 ); 5341 size_t queryLen; 5342 size_t paddedQueryLen; 5343 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf ); 5344 const uint8_t * padLimit; 5345 uint8_t nonce[ crypto_box_NONCEBYTES ]; 5346 5347 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) ); 5348 5349 inContext->queryID = (uint16_t) Random32(); 5350 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname, 5351 inContext->qtype, kDNSServiceClass_IN, &queryLen ); 5352 require_noerr( err, exit ); 5353 5354 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ]; 5355 if( padLimit > msgLimit ) padLimit = msgLimit; 5356 5357 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen ); 5358 require_noerr( err, exit ); 5359 5360 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES ); 5361 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength ); 5362 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength ); 5363 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength ); 5364 5365 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES, 5366 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey ); 5367 require_noerr( err, exit ); 5368 5369 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength ); 5370 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES ); 5371 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength ); 5372 5373 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf ); 5374 5375exit: 5376 return( err ); 5377} 5378 5379static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen ) 5380{ 5381 OSStatus err; 5382 size_t paddedLen; 5383 5384 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr ); 5385 5386 paddedLen = inMsgLen + kDNSCryptMinPadLength + 5387 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) ); 5388 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) ); 5389 if( paddedLen > inMaxLen ) paddedLen = inMaxLen; 5390 5391 inMsgPtr[ inMsgLen ] = 0x80; 5392 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) ); 5393 5394 if( outPaddedLen ) *outPaddedLen = paddedLen; 5395 err = kNoErr; 5396 5397exit: 5398 return( err ); 5399} 5400 5401//=========================================================================================================================== 5402// DNSCryptSendQuery 5403//=========================================================================================================================== 5404 5405static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext ) 5406{ 5407 OSStatus err; 5408 SocketContext * sockContext; 5409 SocketRef sock = kInvalidSocketRef; 5410 5411 check( inContext->msgLen > 0 ); 5412 check( !inContext->readSource ); 5413 5414 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock ); 5415 require_noerr( err, exit ); 5416 5417 inContext->sendTicks = UpTicks(); 5418 err = SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 ); 5419 require_noerr( err, exit ); 5420 5421 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); 5422 require_action( sockContext, exit, err = kNoMemoryErr ); 5423 5424 err = DispatchReadSourceCreate( sock, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockContext, 5425 &inContext->readSource ); 5426 if( err ) ForgetMem( &sockContext ); 5427 require_noerr( err, exit ); 5428 5429 sockContext->context = inContext; 5430 sockContext->sock = sock; 5431 sock = kInvalidSocketRef; 5432 5433 dispatch_resume( inContext->readSource ); 5434 5435exit: 5436 ForgetSocket( &sock ); 5437 return( err ); 5438} 5439 5440//=========================================================================================================================== 5441// DNSCryptPrintCertificate 5442//=========================================================================================================================== 5443 5444#define kCertTimeStrBufLen 32 5445 5446static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] ); 5447 5448static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen ) 5449{ 5450 time_t startTime, endTime; 5451 int extLen; 5452 char timeBuf[ kCertTimeStrBufLen ]; 5453 5454 check( inLen >= kDNSCryptCertMinimumLength ); 5455 5456 startTime = (time_t) ReadBig32( inCert->startTime ); 5457 endTime = (time_t) ReadBig32( inCert->endTime ); 5458 5459 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen ); 5460 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX ); 5461 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) ); 5462 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) ); 5463 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX ); 5464 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX ); 5465 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX ); 5466 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX ); 5467 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) ); 5468 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) ); 5469 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) ); 5470 5471 if( inLen > kDNSCryptCertMinimumLength ) 5472 { 5473 extLen = (int)( inLen - kDNSCryptCertMinimumLength ); 5474 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen ); 5475 } 5476 FPrintF( stdout, "\n" ); 5477} 5478 5479static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] ) 5480{ 5481 struct tm * tm; 5482 5483 tm = localtime( &inTime ); 5484 if( !tm ) 5485 { 5486 dlogassert( "localtime() returned a NULL pointer.\n" ); 5487 *inBuffer = '\0'; 5488 } 5489 else 5490 { 5491 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm ); 5492 } 5493 5494 return( inBuffer ); 5495} 5496 5497#endif // DNSSDUTIL_INCLUDE_DNSCRYPT 5498 5499//=========================================================================================================================== 5500// MDNSQueryCmd 5501//=========================================================================================================================== 5502 5503#define kMDNSPort 5353 5504 5505#define kDefaultMDNSMessageID 0 5506#define kDefaultMDNSQueryFlags 0 5507 5508typedef struct 5509{ 5510 const char * qnameStr; // Name (QNAME) of the record being queried as a C string. 5511 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket. 5512 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket. 5513 int localPort; // The port number to which the sockets are bound. 5514 int receiveSecs; // After send, the amount of time to spend receiving. 5515 uint32_t ifIndex; // Index of the interface over which to send the query. 5516 uint16_t qtype; // The type (QTYPE) of the record being queried. 5517 Boolean isQU; // True if the query is QU, i.e., requests unicast responses. 5518 Boolean allResponses; // True if all mDNS messages received should be printed. 5519 Boolean printRawRData; // True if RDATA should be printed as hexdumps. 5520 Boolean useIPv4; // True if the query should be sent via IPv4 multicast. 5521 Boolean useIPv6; // True if the query should be sent via IPv6 multicast. 5522 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query. 5523 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in label format. 5524 uint8_t msgBuf[ 8940 ]; // Message buffer. 8940 is max size used by mDNSResponder. 5525 5526} MDNSQueryContext; 5527 5528static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext ); 5529static void MDNSQueryReadHandler( void *inContext ); 5530 5531static void MDNSQueryCmd( void ) 5532{ 5533 OSStatus err; 5534 MDNSQueryContext * context; 5535 struct sockaddr_in mcastAddr4; 5536 struct sockaddr_in6 mcastAddr6; 5537 SocketRef sockV4 = kInvalidSocketRef; 5538 SocketRef sockV6 = kInvalidSocketRef; 5539 ssize_t n; 5540 const char * ifNamePtr; 5541 size_t msgLen; 5542 unsigned int sendCount; 5543 5544 // Check command parameters. 5545 5546 if( gMDNSQuery_ReceiveSecs < -1 ) 5547 { 5548 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs ); 5549 err = kParamErr; 5550 goto exit; 5551 } 5552 5553 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) ); 5554 require_action( context, exit, err = kNoMemoryErr ); 5555 5556 context->qnameStr = gMDNSQuery_Name; 5557 context->receiveSecs = gMDNSQuery_ReceiveSecs; 5558 context->isQU = gMDNSQuery_IsQU ? true : false; 5559 context->allResponses = gMDNSQuery_AllResponses ? true : false; 5560 context->printRawRData = gMDNSQuery_RawRData ? true : false; 5561 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false; 5562 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false; 5563 5564 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex ); 5565 require_noerr_quiet( err, exit ); 5566 5567 ifNamePtr = if_indextoname( context->ifIndex, context->ifName ); 5568 require_action( ifNamePtr, exit, err = kNameErr ); 5569 5570 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype ); 5571 require_noerr( err, exit ); 5572 5573 // Set up IPv4 socket. 5574 5575 if( context->useIPv4 ) 5576 { 5577 err = ServerSocketOpen( AF_INET, SOCK_DGRAM, IPPROTO_UDP, 5578 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ), 5579 &context->localPort, kSocketBufferSize_DontSet, &sockV4 ); 5580 require_noerr( err, exit ); 5581 5582 err = SocketSetMulticastInterface( sockV4, ifNamePtr, context->ifIndex ); 5583 require_noerr( err, exit ); 5584 5585 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) ); 5586 err = map_socket_noerr_errno( sockV4, err ); 5587 require_noerr( err, exit ); 5588 5589 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) ); 5590 SIN_LEN_SET( &mcastAddr4 ); 5591 mcastAddr4.sin_family = AF_INET; 5592 mcastAddr4.sin_port = htons( kMDNSPort ); 5593 mcastAddr4.sin_addr.s_addr = htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251 5594 5595 if( !context->isQU && ( context->localPort == kMDNSPort ) ) 5596 { 5597 SocketJoinMulticast( sockV4, &mcastAddr4, ifNamePtr, context->ifIndex ); 5598 require_noerr( err, exit ); 5599 } 5600 } 5601 5602 // Set up IPv6 socket. 5603 5604 if( context->useIPv6 ) 5605 { 5606 err = ServerSocketOpen( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, 5607 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ), 5608 &context->localPort, kSocketBufferSize_DontSet, &sockV6 ); 5609 require_noerr( err, exit ); 5610 5611 err = SocketSetMulticastInterface( sockV6, ifNamePtr, context->ifIndex ); 5612 require_noerr( err, exit ); 5613 5614 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) ); 5615 err = map_socket_noerr_errno( sockV6, err ); 5616 require_noerr( err, exit ); 5617 5618 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) ); 5619 SIN6_LEN_SET( &mcastAddr6 ); 5620 mcastAddr6.sin6_family = AF_INET6; 5621 mcastAddr6.sin6_port = htons( kMDNSPort ); 5622 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // mDNS IPv6 multicast address FF02::FB 5623 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02; 5624 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0xFB; 5625 5626 if( !context->isQU && ( context->localPort == kMDNSPort ) ) 5627 { 5628 SocketJoinMulticast( sockV6, &mcastAddr6, ifNamePtr, context->ifIndex ); 5629 require_noerr( err, exit ); 5630 } 5631 } 5632 5633 // Craft mDNS query message. 5634 5635 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen ); 5636 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr, 5637 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kQClassUnicastResponseBit ) : kDNSServiceClass_IN, &msgLen ); 5638 require_noerr( err, exit ); 5639 5640 // Print prologue. 5641 5642 MDNSQueryPrintPrologue( context ); 5643 5644 // Send mDNS query message. 5645 5646 sendCount = 0; 5647 if( IsValidSocket( sockV4 ) ) 5648 { 5649 n = sendto( sockV4, context->msgBuf, msgLen, 0, (struct sockaddr *) &mcastAddr4, (socklen_t) sizeof( mcastAddr4 ) ); 5650 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n ); 5651 if( err ) 5652 { 5653 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err ); 5654 ForgetSocket( &sockV4 ); 5655 } 5656 else 5657 { 5658 ++sendCount; 5659 } 5660 } 5661 if( IsValidSocket( sockV6 ) ) 5662 { 5663 n = sendto( sockV6, context->msgBuf, msgLen, 0, (struct sockaddr *) &mcastAddr6, (socklen_t) sizeof( mcastAddr6 ) ); 5664 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n ); 5665 if( err ) 5666 { 5667 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err ); 5668 ForgetSocket( &sockV6 ); 5669 } 5670 else 5671 { 5672 ++sendCount; 5673 } 5674 } 5675 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr ); 5676 5677 // If there's no wait period after the send, then exit. 5678 5679 if( context->receiveSecs == 0 ) goto exit; 5680 5681 // Create dispatch read sources for socket(s). 5682 5683 if( IsValidSocket( sockV4 ) ) 5684 { 5685 SocketContext * sockContext; 5686 5687 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); 5688 require_action( sockContext, exit, err = kNoMemoryErr ); 5689 5690 err = DispatchReadSourceCreate( sockV4, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext, 5691 &context->readSourceV4 ); 5692 if( err ) ForgetMem( &sockContext ); 5693 require_noerr( err, exit ); 5694 5695 sockContext->context = context; 5696 sockContext->sock = sockV4; 5697 sockV4 = kInvalidSocketRef; 5698 dispatch_resume( context->readSourceV4 ); 5699 } 5700 5701 if( IsValidSocket( sockV6 ) ) 5702 { 5703 SocketContext * sockContext; 5704 5705 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); 5706 require_action( sockContext, exit, err = kNoMemoryErr ); 5707 5708 err = DispatchReadSourceCreate( sockV6, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext, 5709 &context->readSourceV6 ); 5710 if( err ) ForgetMem( &sockContext ); 5711 require_noerr( err, exit ); 5712 5713 sockContext->context = context; 5714 sockContext->sock = sockV6; 5715 sockV6 = kInvalidSocketRef; 5716 dispatch_resume( context->readSourceV6 ); 5717 } 5718 5719 if( context->receiveSecs > 0 ) 5720 { 5721 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout, 5722 Exit ); 5723 } 5724 dispatch_main(); 5725 5726exit: 5727 ForgetSocket( &sockV4 ); 5728 ForgetSocket( &sockV6 ); 5729 if( err ) exit( 1 ); 5730} 5731 5732//=========================================================================================================================== 5733// MDNSQueryPrintPrologue 5734//=========================================================================================================================== 5735 5736static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext ) 5737{ 5738 const int receiveSecs = inContext->receiveSecs; 5739 char time[ kTimestampBufLen ]; 5740 5741 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName ); 5742 FPrintF( stdout, "Name: %s\n", inContext->qnameStr ); 5743 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype ); 5744 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" ); 5745 FPrintF( stdout, "Local port: %d\n", inContext->localPort ); 5746 FPrintF( stdout, "IP protocols: %?s%?s%?s\n", 5747 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" ); 5748 FPrintF( stdout, "Receive duration: " ); 5749 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' ); 5750 else FPrintF( stdout, "���\n" ); 5751 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); 5752} 5753 5754//=========================================================================================================================== 5755// MDNSQueryReadHandler 5756//=========================================================================================================================== 5757 5758static void MDNSQueryReadHandler( void *inContext ) 5759{ 5760 OSStatus err; 5761 SocketContext * const sockContext = (SocketContext *) inContext; 5762 MDNSQueryContext * const context = (MDNSQueryContext *) sockContext->context; 5763 size_t msgLen; 5764 sockaddr_ip fromAddr; 5765 char time[ kTimestampBufLen ]; 5766 Boolean foundAnswer = false; 5767 5768 GetTimestampStr( time ); 5769 5770 err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr, 5771 sizeof( fromAddr ), NULL, NULL, NULL, NULL ); 5772 require_noerr( err, exit ); 5773 5774 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) ) 5775 { 5776 const uint8_t * ptr; 5777 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf; 5778 unsigned int rrCount, i; 5779 uint16_t type, class; 5780 uint8_t name[ kDomainNameLengthMax ]; 5781 5782 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr ); 5783 require_noerr( err, exit ); 5784 5785 if( context->qname[ 0 ] == 0 ) 5786 { 5787 err = DomainNameAppendString( context->qname, context->qnameStr, NULL ); 5788 require_noerr( err, exit ); 5789 } 5790 5791 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr ); 5792 for( i = 0; i < rrCount; ++i ) 5793 { 5794 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr ); 5795 require_noerr( err, exit ); 5796 5797 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) && 5798 DomainNameEqual( name, context->qname ) ) 5799 { 5800 foundAnswer = true; 5801 break; 5802 } 5803 } 5804 } 5805 if( context->allResponses || foundAnswer ) 5806 { 5807 FPrintF( stdout, "---\n" ); 5808 FPrintF( stdout, "Receive time: %s\n", time ); 5809 FPrintF( stdout, "Source: %##a\n", &fromAddr ); 5810 FPrintF( stdout, "Message size: %zu\n\n", msgLen ); 5811 5812 PrintMDNSMessage( context->msgBuf, msgLen, context->printRawRData ); 5813 } 5814 5815exit: 5816 if( err ) exit( 1 ); 5817} 5818 5819//=========================================================================================================================== 5820// PIDToUUIDCmd 5821//=========================================================================================================================== 5822 5823static void PIDToUUIDCmd( void ) 5824{ 5825 OSStatus err; 5826 int n; 5827 struct proc_uniqidentifierinfo info; 5828 5829 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) ); 5830 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr ); 5831 5832 FPrintF( stdout, "%#U\n", info.p_uuid ); 5833 err = kNoErr; 5834 5835exit: 5836 if( err ) exit( 1 ); 5837} 5838 5839//=========================================================================================================================== 5840// DaemonVersionCmd 5841//=========================================================================================================================== 5842 5843static void DaemonVersionCmd( void ) 5844{ 5845 OSStatus err; 5846 uint32_t size, version; 5847 char strBuf[ 16 ]; 5848 5849 size = (uint32_t) sizeof( version ); 5850 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size ); 5851 require_noerr( err, exit ); 5852 5853 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) ); 5854 5855exit: 5856 if( err ) exit( 1 ); 5857} 5858 5859//=========================================================================================================================== 5860// Exit 5861//=========================================================================================================================== 5862 5863static void Exit( void *inContext ) 5864{ 5865 const char * const reason = (const char *) inContext; 5866 char time[ kTimestampBufLen ]; 5867 5868 FPrintF( stdout, "---\n" ); 5869 FPrintF( stdout, "End time: %s\n", GetTimestampStr( time ) ); 5870 if( reason ) FPrintF( stdout, "End reason: %s\n", reason ); 5871 exit( gExitCode ); 5872} 5873 5874//=========================================================================================================================== 5875// GetTimestampStr 5876//=========================================================================================================================== 5877 5878static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] ) 5879{ 5880 struct timeval now; 5881 struct tm * tm; 5882 size_t len; 5883 5884 gettimeofday( &now, NULL ); 5885 tm = localtime( &now.tv_sec ); 5886 require_action( tm, exit, *inBuffer = '\0' ); 5887 5888 len = strftime( inBuffer, kTimestampBufLen, "%Y-%m-%d %H:%M:%S", tm ); 5889 SNPrintF( &inBuffer[ len ], kTimestampBufLen - len, ".%06u", (unsigned int) now.tv_usec ); 5890 5891exit: 5892 return( inBuffer ); 5893} 5894 5895//=========================================================================================================================== 5896// GetDNSSDFlagsFromOpts 5897//=========================================================================================================================== 5898 5899static DNSServiceFlags GetDNSSDFlagsFromOpts( void ) 5900{ 5901 DNSServiceFlags flags; 5902 5903 flags = (DNSServiceFlags) gDNSSDFlags; 5904 if( flags & kDNSServiceFlagsShareConnection ) 5905 { 5906 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n", 5907 kDNSServiceFlagsShareConnection ); 5908 } 5909 5910 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains; 5911 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular; 5912 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive; 5913 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast; 5914 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL; 5915 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename; 5916 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone; 5917 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains; 5918 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates; 5919 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared; 5920 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable; 5921 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout; 5922 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse; 5923 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique; 5924 5925 return( flags ); 5926} 5927 5928//=========================================================================================================================== 5929// CreateConnectionFromArgString 5930//=========================================================================================================================== 5931 5932static OSStatus 5933 CreateConnectionFromArgString( 5934 const char * inString, 5935 dispatch_queue_t inQueue, 5936 DNSServiceRef * outSDRef, 5937 ConnectionDesc * outDesc ) 5938{ 5939 OSStatus err; 5940 DNSServiceRef sdRef = NULL; 5941 ConnectionType type; 5942 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized. 5943 uint8_t uuid[ 16 ]; 5944 5945 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 ) 5946 { 5947 err = DNSServiceCreateConnection( &sdRef ); 5948 require_noerr( err, exit ); 5949 type = kConnectionType_Normal; 5950 } 5951 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 ) 5952 { 5953 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID ); 5954 5955 err = StringToInt32( pidStr, &pid ); 5956 if( err ) 5957 { 5958 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr ); 5959 err = kParamErr; 5960 goto exit; 5961 } 5962 5963 memset( uuid, 0, sizeof( uuid ) ); 5964 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid ); 5965 if( err ) 5966 { 5967 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid ); 5968 goto exit; 5969 } 5970 type = kConnectionType_DelegatePID; 5971 } 5972 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 ) 5973 { 5974 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID ); 5975 5976 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) ); 5977 5978 err = StringToUUID( uuidStr, kSizeCString, false, uuid ); 5979 if( err ) 5980 { 5981 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr ); 5982 err = kParamErr; 5983 goto exit; 5984 } 5985 5986 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid ); 5987 if( err ) 5988 { 5989 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid ); 5990 goto exit; 5991 } 5992 type = kConnectionType_DelegateUUID; 5993 } 5994 else 5995 { 5996 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString ); 5997 err = kParamErr; 5998 goto exit; 5999 } 6000 6001 err = DNSServiceSetDispatchQueue( sdRef, inQueue ); 6002 require_noerr( err, exit ); 6003 6004 *outSDRef = sdRef; 6005 if( outDesc ) 6006 { 6007 outDesc->type = type; 6008 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid; 6009 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 ); 6010 } 6011 sdRef = NULL; 6012 6013exit: 6014 if( sdRef ) DNSServiceRefDeallocate( sdRef ); 6015 return( err ); 6016} 6017 6018//=========================================================================================================================== 6019// InterfaceIndexFromArgString 6020//=========================================================================================================================== 6021 6022static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex ) 6023{ 6024 OSStatus err; 6025 uint32_t ifIndex; 6026 6027 if( inString ) 6028 { 6029 ifIndex = if_nametoindex( inString ); 6030 if( ifIndex == 0 ) 6031 { 6032 err = StringToUInt32( inString, &ifIndex ); 6033 if( err ) 6034 { 6035 FPrintF( stderr, "Invalid interface value: %s\n", inString ); 6036 err = kParamErr; 6037 goto exit; 6038 } 6039 } 6040 } 6041 else 6042 { 6043 ifIndex = 0; 6044 } 6045 6046 *outIndex = ifIndex; 6047 err = kNoErr; 6048 6049exit: 6050 return( err ); 6051} 6052 6053//=========================================================================================================================== 6054// RecordDataFromArgString 6055//=========================================================================================================================== 6056 6057#define kRDataMaxLen UINT16_C( 0xFFFF ) 6058 6059static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen ) 6060{ 6061 OSStatus err; 6062 uint8_t * dataPtr = NULL; 6063 size_t dataLen; 6064 DataBuffer dataBuf; 6065 6066 DataBuffer_Init( &dataBuf, NULL, 0, kRDataMaxLen ); 6067 6068 if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 ) 6069 { 6070 const char * const strPtr = inString + sizeof_string( kRDataArgPrefix_String ); 6071 const size_t strLen = strlen( strPtr ); 6072 size_t copiedLen; 6073 size_t totalLen; 6074 6075 if( strLen > 0 ) 6076 { 6077 require_action( strLen <= kRDataMaxLen, exit, err = kSizeErr ); 6078 dataPtr = (uint8_t *) malloc( strLen ); 6079 require_action( dataPtr, exit, err = kNoMemoryErr ); 6080 6081 copiedLen = 0; 6082 ParseQuotedEscapedString( strPtr, strPtr + strLen, "", (char *) dataPtr, strLen, &copiedLen, &totalLen, NULL ); 6083 check( copiedLen == totalLen ); 6084 dataLen = copiedLen; 6085 } 6086 else 6087 { 6088 dataPtr = NULL; 6089 dataLen = 0; 6090 } 6091 } 6092 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 ) 6093 { 6094 const char * const strPtr = inString + sizeof_string( kRDataArgPrefix_HexString ); 6095 6096 err = HexToDataCopy( strPtr, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL ); 6097 require_noerr( err, exit ); 6098 require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr ); 6099 } 6100 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 ) 6101 { 6102 const char * const path = inString + sizeof_string( kRDataArgPrefix_File ); 6103 6104 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen ); 6105 require_noerr( err, exit ); 6106 require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr ); 6107 } 6108 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 ) 6109 { 6110 const char * strPtr = inString + sizeof_string( kRDataArgPrefix_TXT ); 6111 const char * const strEnd = strPtr + strlen( strPtr ); 6112 6113 while( strPtr < strEnd ) 6114 { 6115 size_t copiedLen, totalLen; 6116 uint8_t kvBuf[ 1 + 255 + 1 ]; // Length byte + max key-value length + 1 for NUL terminator. 6117 6118 err = ParseEscapedString( strPtr, strEnd, ',', (char *) &kvBuf[ 1 ], sizeof( kvBuf ) - 1, 6119 &copiedLen, &totalLen, &strPtr ); 6120 require_noerr_quiet( err, exit ); 6121 check( copiedLen == totalLen ); 6122 if( totalLen > 255 ) 6123 { 6124 FPrintF( stderr, "TXT key-value pair length %zu is too long (> 255 bytes).\n", totalLen ); 6125 err = kParamErr; 6126 goto exit; 6127 } 6128 6129 kvBuf[ 0 ] = (uint8_t) copiedLen; 6130 err = DataBuffer_Append( &dataBuf, kvBuf, 1 + kvBuf[ 0 ] ); 6131 require_noerr( err, exit ); 6132 } 6133 6134 err = DataBuffer_Commit( &dataBuf, NULL, NULL ); 6135 require_noerr( err, exit ); 6136 6137 err = DataBuffer_Detach( &dataBuf, &dataPtr, &dataLen ); 6138 require_noerr( err, exit ); 6139 } 6140 else 6141 { 6142 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString ); 6143 err = kParamErr; 6144 goto exit; 6145 } 6146 err = kNoErr; 6147 6148 *outDataLen = dataLen; 6149 *outDataPtr = dataPtr; 6150 dataPtr = NULL; 6151 6152exit: 6153 DataBuffer_Free( &dataBuf ); 6154 FreeNullSafe( dataPtr ); 6155 return( err ); 6156} 6157 6158//=========================================================================================================================== 6159// RecordTypeFromArgString 6160//=========================================================================================================================== 6161 6162typedef struct 6163{ 6164 uint16_t value; // Record type's numeric value. 6165 const char * name; // Record type's name as a string (e.g., "A", "PTR", "SRV"). 6166 6167} RecordType; 6168 6169static const RecordType kRecordTypes[] = 6170{ 6171 // Common types. 6172 6173 { kDNSServiceType_A, "A" }, 6174 { kDNSServiceType_AAAA, "AAAA" }, 6175 { kDNSServiceType_PTR, "PTR" }, 6176 { kDNSServiceType_SRV, "SRV" }, 6177 { kDNSServiceType_TXT, "TXT" }, 6178 { kDNSServiceType_CNAME, "CNAME" }, 6179 { kDNSServiceType_SOA, "SOA" }, 6180 { kDNSServiceType_NSEC, "NSEC" }, 6181 { kDNSServiceType_NS, "NS" }, 6182 { kDNSServiceType_MX, "MX" }, 6183 { kDNSServiceType_ANY, "ANY" }, 6184 { kDNSServiceType_OPT, "OPT" }, 6185 6186 // Less common types. 6187 6188 { kDNSServiceType_MD, "MD" }, 6189 { kDNSServiceType_NS, "NS" }, 6190 { kDNSServiceType_MD, "MD" }, 6191 { kDNSServiceType_MF, "MF" }, 6192 { kDNSServiceType_MB, "MB" }, 6193 { kDNSServiceType_MG, "MG" }, 6194 { kDNSServiceType_MR, "MR" }, 6195 { kDNSServiceType_NULL, "NULL" }, 6196 { kDNSServiceType_WKS, "WKS" }, 6197 { kDNSServiceType_HINFO, "HINFO" }, 6198 { kDNSServiceType_MINFO, "MINFO" }, 6199 { kDNSServiceType_RP, "RP" }, 6200 { kDNSServiceType_AFSDB, "AFSDB" }, 6201 { kDNSServiceType_X25, "X25" }, 6202 { kDNSServiceType_ISDN, "ISDN" }, 6203 { kDNSServiceType_RT, "RT" }, 6204 { kDNSServiceType_NSAP, "NSAP" }, 6205 { kDNSServiceType_NSAP_PTR, "NSAP_PTR" }, 6206 { kDNSServiceType_SIG, "SIG" }, 6207 { kDNSServiceType_KEY, "KEY" }, 6208 { kDNSServiceType_PX, "PX" }, 6209 { kDNSServiceType_GPOS, "GPOS" }, 6210 { kDNSServiceType_LOC, "LOC" }, 6211 { kDNSServiceType_NXT, "NXT" }, 6212 { kDNSServiceType_EID, "EID" }, 6213 { kDNSServiceType_NIMLOC, "NIMLOC" }, 6214 { kDNSServiceType_ATMA, "ATMA" }, 6215 { kDNSServiceType_NAPTR, "NAPTR" }, 6216 { kDNSServiceType_KX, "KX" }, 6217 { kDNSServiceType_CERT, "CERT" }, 6218 { kDNSServiceType_A6, "A6" }, 6219 { kDNSServiceType_DNAME, "DNAME" }, 6220 { kDNSServiceType_SINK, "SINK" }, 6221 { kDNSServiceType_APL, "APL" }, 6222 { kDNSServiceType_DS, "DS" }, 6223 { kDNSServiceType_SSHFP, "SSHFP" }, 6224 { kDNSServiceType_IPSECKEY, "IPSECKEY" }, 6225 { kDNSServiceType_RRSIG, "RRSIG" }, 6226 { kDNSServiceType_DNSKEY, "DNSKEY" }, 6227 { kDNSServiceType_DHCID, "DHCID" }, 6228 { kDNSServiceType_NSEC3, "NSEC3" }, 6229 { kDNSServiceType_NSEC3PARAM, "NSEC3PARAM" }, 6230 { kDNSServiceType_HIP, "HIP" }, 6231 { kDNSServiceType_SPF, "SPF" }, 6232 { kDNSServiceType_UINFO, "UINFO" }, 6233 { kDNSServiceType_UID, "UID" }, 6234 { kDNSServiceType_GID, "GID" }, 6235 { kDNSServiceType_UNSPEC, "UNSPEC" }, 6236 { kDNSServiceType_TKEY, "TKEY" }, 6237 { kDNSServiceType_TSIG, "TSIG" }, 6238 { kDNSServiceType_IXFR, "IXFR" }, 6239 { kDNSServiceType_AXFR, "AXFR" }, 6240 { kDNSServiceType_MAILB, "MAILB" }, 6241 { kDNSServiceType_MAILA, "MAILA" } 6242}; 6243 6244static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue ) 6245{ 6246 OSStatus err; 6247 int32_t i32; 6248 const RecordType * type; 6249 const RecordType * const end = kRecordTypes + countof( kRecordTypes ); 6250 6251 for( type = kRecordTypes; type < end; ++type ) 6252 { 6253 if( strcasecmp( type->name, inString ) == 0 ) 6254 { 6255 *outValue = type->value; 6256 return( kNoErr ); 6257 } 6258 } 6259 6260 err = StringToInt32( inString, &i32 ); 6261 require_noerr_quiet( err, exit ); 6262 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr ); 6263 6264 *outValue = (uint16_t) i32; 6265 6266exit: 6267 return( err ); 6268} 6269 6270//=========================================================================================================================== 6271// RecordClassFromArgString 6272//=========================================================================================================================== 6273 6274static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue ) 6275{ 6276 OSStatus err; 6277 int32_t i32; 6278 6279 if( strcasecmp( inString, "IN" ) == 0 ) 6280 { 6281 *outValue = kDNSServiceClass_IN; 6282 err = kNoErr; 6283 goto exit; 6284 } 6285 6286 err = StringToInt32( inString, &i32 ); 6287 require_noerr_quiet( err, exit ); 6288 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr ); 6289 6290 *outValue = (uint16_t) i32; 6291 6292exit: 6293 return( err ); 6294} 6295 6296//=========================================================================================================================== 6297// InterfaceIndexToName 6298//=========================================================================================================================== 6299 6300static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] ) 6301{ 6302 switch( inIfIndex ) 6303 { 6304 case kDNSServiceInterfaceIndexAny: 6305 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen ); 6306 break; 6307 6308 case kDNSServiceInterfaceIndexLocalOnly: 6309 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen ); 6310 break; 6311 6312 case kDNSServiceInterfaceIndexUnicast: 6313 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen ); 6314 break; 6315 6316 case kDNSServiceInterfaceIndexP2P: 6317 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen ); 6318 break; 6319 6320 #if( defined( kDNSServiceInterfaceIndexBLE ) ) 6321 case kDNSServiceInterfaceIndexBLE: 6322 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen ); 6323 break; 6324 #endif 6325 6326 default: 6327 { 6328 const char * name; 6329 6330 name = if_indextoname( inIfIndex, inNameBuf ); 6331 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen ); 6332 break; 6333 } 6334 } 6335 6336 return( inNameBuf ); 6337} 6338 6339//=========================================================================================================================== 6340// RecordTypeToString 6341//=========================================================================================================================== 6342 6343static const char * RecordTypeToString( unsigned int inValue ) 6344{ 6345 const RecordType * type; 6346 const RecordType * const end = kRecordTypes + countof( kRecordTypes ); 6347 6348 for( type = kRecordTypes; type < end; ++type ) 6349 { 6350 if( type->value == inValue ) return( type->name ); 6351 } 6352 return( "???" ); 6353} 6354 6355//=========================================================================================================================== 6356// DNSMessageExtractDomainName 6357//=========================================================================================================================== 6358 6359#define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 ) 6360 6361static OSStatus 6362 DNSMessageExtractDomainName( 6363 const uint8_t * inMsgPtr, 6364 size_t inMsgLen, 6365 const uint8_t * inNamePtr, 6366 uint8_t inBuf[ kDomainNameLengthMax ], 6367 const uint8_t ** outNextPtr ) 6368{ 6369 OSStatus err; 6370 const uint8_t * label; 6371 uint8_t labelLen; 6372 const uint8_t * nextLabel; 6373 const uint8_t * const msgEnd = inMsgPtr + inMsgLen; 6374 uint8_t * dst = inBuf; 6375 const uint8_t * const dstLim = inBuf ? ( inBuf + kDomainNameLengthMax ) : NULL; 6376 const uint8_t * nameEnd = NULL; 6377 6378 require_action( ( inNamePtr >= inMsgPtr ) && ( inNamePtr < msgEnd ), exit, err = kRangeErr ); 6379 6380 for( label = inNamePtr; ( labelLen = label[ 0 ] ) != 0; label = nextLabel ) 6381 { 6382 if( labelLen <= kDomainLabelLengthMax ) 6383 { 6384 nextLabel = label + 1 + labelLen; 6385 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr ); 6386 if( dst ) 6387 { 6388 require_action( ( dstLim - dst ) > ( 1 + labelLen ), exit, err = kOverrunErr ); 6389 memcpy( dst, label, 1 + labelLen ); 6390 dst += ( 1 + labelLen ); 6391 } 6392 } 6393 else if( IsCompressionByte( labelLen ) ) 6394 { 6395 uint16_t offset; 6396 6397 require_action( ( msgEnd - label ) >= 2, exit, err = kUnderrunErr ); 6398 if( !nameEnd ) 6399 { 6400 nameEnd = label + 2; 6401 if( !dst ) break; 6402 } 6403 offset = (uint16_t)( ( ( label[ 0 ] & 0x3F ) << 8 ) | label[ 1 ] ); 6404 nextLabel = inMsgPtr + offset; 6405 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr ); 6406 require_action( !IsCompressionByte( nextLabel[ 0 ] ), exit, err = kMalformedErr ); 6407 } 6408 else 6409 { 6410 dlogassert( "Unhandled label length 0x%02X\n", labelLen ); 6411 err = kMalformedErr; 6412 goto exit; 6413 } 6414 } 6415 6416 if( dst ) *dst = 0; 6417 if( !nameEnd ) nameEnd = label + 1; 6418 6419 if( outNextPtr ) *outNextPtr = nameEnd; 6420 err = kNoErr; 6421 6422exit: 6423 return( err ); 6424} 6425 6426//=========================================================================================================================== 6427// DNSMessageExtractDomainNameString 6428//=========================================================================================================================== 6429 6430static OSStatus 6431 DNSMessageExtractDomainNameString( 6432 const void * inMsgPtr, 6433 size_t inMsgLen, 6434 const void * inNamePtr, 6435 char inBuf[ kDNSServiceMaxDomainName ], 6436 const uint8_t ** outNextPtr ) 6437{ 6438 OSStatus err; 6439 const uint8_t * nextPtr; 6440 uint8_t domainName[ kDomainNameLengthMax ]; 6441 6442 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inNamePtr, domainName, &nextPtr ); 6443 require_noerr( err, exit ); 6444 6445 err = DomainNameToString( domainName, NULL, inBuf, NULL ); 6446 require_noerr( err, exit ); 6447 6448 if( outNextPtr ) *outNextPtr = nextPtr; 6449 6450exit: 6451 return( err ); 6452} 6453 6454//=========================================================================================================================== 6455// DNSMessageExtractRecord 6456//=========================================================================================================================== 6457 6458typedef struct 6459{ 6460 uint8_t type[ 2 ]; 6461 uint8_t class[ 2 ]; 6462 uint8_t ttl[ 4 ]; 6463 uint8_t rdLength[ 2 ]; 6464 uint8_t rdata[ 1 ]; 6465 6466} DNSRecordFields; 6467 6468check_compile_time( offsetof( DNSRecordFields, rdata ) == 10 ); 6469 6470static OSStatus 6471 DNSMessageExtractRecord( 6472 const uint8_t * inMsgPtr, 6473 size_t inMsgLen, 6474 const uint8_t * inPtr, 6475 uint8_t inNameBuf[ kDomainNameLengthMax ], 6476 uint16_t * outType, 6477 uint16_t * outClass, 6478 uint32_t * outTTL, 6479 const uint8_t ** outRDataPtr, 6480 size_t * outRDataLen, 6481 const uint8_t ** outPtr ) 6482{ 6483 OSStatus err; 6484 const uint8_t * const msgEnd = inMsgPtr + inMsgLen; 6485 const uint8_t * ptr; 6486 const DNSRecordFields * record; 6487 size_t rdLength; 6488 6489 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, inNameBuf, &ptr ); 6490 require_noerr_quiet( err, exit ); 6491 require_action_quiet( (size_t)( msgEnd - ptr ) >= offsetof( DNSRecordFields, rdata ), exit, err = kUnderrunErr ); 6492 6493 record = (DNSRecordFields *) ptr; 6494 rdLength = ReadBig16( record->rdLength ); 6495 require_action_quiet( (size_t)( msgEnd - record->rdata ) >= rdLength , exit, err = kUnderrunErr ); 6496 6497 if( outType ) *outType = ReadBig16( record->type ); 6498 if( outClass ) *outClass = ReadBig16( record->class ); 6499 if( outTTL ) *outTTL = ReadBig32( record->ttl ); 6500 if( outRDataPtr ) *outRDataPtr = record->rdata; 6501 if( outRDataLen ) *outRDataLen = rdLength; 6502 if( outPtr ) *outPtr = record->rdata + rdLength; 6503 6504exit: 6505 return( err ); 6506} 6507 6508//=========================================================================================================================== 6509// DNSMessageGetAnswerSection 6510//=========================================================================================================================== 6511 6512static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr ) 6513{ 6514 OSStatus err; 6515 const uint8_t * const msgEnd = inMsgPtr + inMsgLen; 6516 unsigned int questionCount, i; 6517 const DNSHeader * hdr; 6518 const uint8_t * ptr; 6519 6520 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr ); 6521 6522 hdr = (DNSHeader *) inMsgPtr; 6523 questionCount = DNSHeaderGetQuestionCount( hdr ); 6524 6525 ptr = (uint8_t *)( hdr + 1 ); 6526 for( i = 0; i < questionCount; ++i ) 6527 { 6528 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, ptr, NULL, &ptr ); 6529 require_noerr( err, exit ); 6530 require_action_quiet( ( msgEnd - ptr ) >= 4, exit, err = kUnderrunErr ); 6531 ptr += 4; 6532 } 6533 6534 if( outPtr ) *outPtr = ptr; 6535 err = kNoErr; 6536 6537exit: 6538 return( err ); 6539} 6540 6541//=========================================================================================================================== 6542// DNSRecordDataToString 6543//=========================================================================================================================== 6544 6545static OSStatus 6546 DNSRecordDataToString( 6547 const void * inRDataPtr, 6548 size_t inRDataLen, 6549 unsigned int inRDataType, 6550 const void * inMsgPtr, 6551 size_t inMsgLen, 6552 char ** outString ) 6553{ 6554 OSStatus err; 6555 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr; 6556 const uint8_t * const rdataEnd = rdataPtr + inRDataLen; 6557 char * rdataStr; 6558 const uint8_t * ptr; 6559 int n; 6560 char domainNameStr[ kDNSServiceMaxDomainName ]; 6561 6562 rdataStr = NULL; 6563 if( inRDataType == kDNSServiceType_A ) 6564 { 6565 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr ); 6566 6567 ASPrintF( &rdataStr, "%.4a", rdataPtr ); 6568 require_action( rdataStr, exit, err = kNoMemoryErr ); 6569 } 6570 else if( inRDataType == kDNSServiceType_AAAA ) 6571 { 6572 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr ); 6573 6574 ASPrintF( &rdataStr, "%.16a", rdataPtr ); 6575 require_action( rdataStr, exit, err = kNoMemoryErr ); 6576 } 6577 else if( ( inRDataType == kDNSServiceType_PTR ) || ( inRDataType == kDNSServiceType_CNAME ) || 6578 ( inRDataType == kDNSServiceType_NS ) ) 6579 { 6580 if( inMsgPtr ) 6581 { 6582 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, NULL ); 6583 require_noerr( err, exit ); 6584 } 6585 else 6586 { 6587 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL ); 6588 require_noerr( err, exit ); 6589 } 6590 6591 rdataStr = strdup( domainNameStr ); 6592 require_action( rdataStr, exit, err = kNoMemoryErr ); 6593 } 6594 else if( inRDataType == kDNSServiceType_SRV ) 6595 { 6596 uint16_t priority, weight, port; 6597 const uint8_t * target; 6598 6599 require_action_quiet( ( rdataPtr + 6 ) < rdataEnd, exit, err = kMalformedErr ); 6600 6601 priority = ReadBig16( rdataPtr ); 6602 weight = ReadBig16( rdataPtr + 2 ); 6603 port = ReadBig16( rdataPtr + 4 ); 6604 target = rdataPtr + 6; 6605 6606 if( inMsgPtr ) 6607 { 6608 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, target, domainNameStr, NULL ); 6609 require_noerr( err, exit ); 6610 } 6611 else 6612 { 6613 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL ); 6614 require_noerr( err, exit ); 6615 } 6616 6617 ASPrintF( &rdataStr, "%u %u %u %s", priority, weight, port, domainNameStr ); 6618 require_action( rdataStr, exit, err = kNoMemoryErr ); 6619 } 6620 else if( inRDataType == kDNSServiceType_TXT ) 6621 { 6622 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr ); 6623 6624 if( inRDataLen == 1 ) 6625 { 6626 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) inRDataLen, INT_MAX ); 6627 require_action( rdataStr, exit, err = kNoMemoryErr ); 6628 } 6629 else 6630 { 6631 ASPrintF( &rdataStr, "%#{txt}", rdataPtr, inRDataLen ); 6632 require_action( rdataStr, exit, err = kNoMemoryErr ); 6633 } 6634 } 6635 else if( inRDataType == kDNSServiceType_SOA ) 6636 { 6637 uint32_t serial, refresh, retry, expire, minimum; 6638 6639 if( inMsgPtr ) 6640 { 6641 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr ); 6642 require_noerr( err, exit ); 6643 6644 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr ); 6645 6646 rdataStr = strdup( domainNameStr ); 6647 require_action( rdataStr, exit, err = kNoMemoryErr ); 6648 6649 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, domainNameStr, &ptr ); 6650 require_noerr( err, exit ); 6651 } 6652 else 6653 { 6654 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr ); 6655 require_noerr( err, exit ); 6656 6657 rdataStr = strdup( domainNameStr ); 6658 require_action( rdataStr, exit, err = kNoMemoryErr ); 6659 6660 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr ); 6661 require_noerr( err, exit ); 6662 } 6663 6664 require_action_quiet( ( ptr + 20 ) == rdataEnd, exit, err = kMalformedErr ); 6665 6666 serial = ReadBig32( ptr ); 6667 refresh = ReadBig32( ptr + 4 ); 6668 retry = ReadBig32( ptr + 8 ); 6669 expire = ReadBig32( ptr + 12 ); 6670 minimum = ReadBig32( ptr + 16 ); 6671 6672 n = AppendPrintF( &rdataStr, " %s %u %u %u %u %u\n", domainNameStr, serial, refresh, retry, expire, minimum ); 6673 require_action( n > 0, exit, err = kUnknownErr ); 6674 } 6675 else if( inRDataType == kDNSServiceType_NSEC ) 6676 { 6677 unsigned int windowBlock, bitmapLen, i, recordType; 6678 const uint8_t * bitmapPtr; 6679 6680 if( inMsgPtr ) 6681 { 6682 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr ); 6683 require_noerr( err, exit ); 6684 } 6685 else 6686 { 6687 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr ); 6688 require_noerr( err, exit ); 6689 } 6690 6691 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr ); 6692 6693 rdataStr = strdup( domainNameStr ); 6694 require_action( rdataStr, exit, err = kNoMemoryErr ); 6695 6696 for( ; ptr < rdataEnd; ptr += ( 2 + bitmapLen ) ) 6697 { 6698 require_action_quiet( ( ptr + 2 ) < rdataEnd, exit, err = kMalformedErr ); 6699 6700 windowBlock = ptr[ 0 ]; 6701 bitmapLen = ptr[ 1 ]; 6702 bitmapPtr = &ptr[ 2 ]; 6703 6704 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr ); 6705 require_action_quiet( ( bitmapPtr + bitmapLen ) <= rdataEnd, exit, err = kMalformedErr ); 6706 6707 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i ) 6708 { 6709 if( BitArray_GetBit( bitmapPtr, bitmapLen, i ) ) 6710 { 6711 recordType = ( windowBlock * 256 ) + i; 6712 n = AppendPrintF( &rdataStr, " %s", RecordTypeToString( recordType ) ); 6713 require_action( n > 0, exit, err = kUnknownErr ); 6714 } 6715 } 6716 } 6717 } 6718 else if( inRDataType == kDNSServiceType_MX ) 6719 { 6720 uint16_t preference; 6721 const uint8_t * exchange; 6722 6723 require_action_quiet( ( rdataPtr + 2 ) < rdataEnd, exit, err = kMalformedErr ); 6724 6725 preference = ReadBig16( rdataPtr ); 6726 exchange = &rdataPtr[ 2 ]; 6727 6728 if( inMsgPtr ) 6729 { 6730 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, exchange, domainNameStr, NULL ); 6731 require_noerr( err, exit ); 6732 } 6733 else 6734 { 6735 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL ); 6736 require_noerr( err, exit ); 6737 } 6738 6739 n = ASPrintF( &rdataStr, "%u %s", preference, domainNameStr ); 6740 require_action( n > 0, exit, err = kUnknownErr ); 6741 } 6742 else 6743 { 6744 err = kNotHandledErr; 6745 goto exit; 6746 } 6747 6748 check( rdataStr ); 6749 *outString = rdataStr; 6750 rdataStr = NULL; 6751 err = kNoErr; 6752 6753exit: 6754 FreeNullSafe( rdataStr ); 6755 return( err ); 6756} 6757 6758//=========================================================================================================================== 6759// DomainNameAppendString 6760//=========================================================================================================================== 6761 6762static OSStatus 6763 DomainNameAppendString( 6764 uint8_t inDomainName[ kDomainNameLengthMax ], 6765 const char * inString, 6766 uint8_t ** outEndPtr ) 6767{ 6768 OSStatus err; 6769 const char * src; 6770 uint8_t * dst; 6771 const uint8_t * const nameLimit = inDomainName + kDomainNameLengthMax; 6772 6773 // Find the root label. 6774 6775 for( dst = inDomainName; ( dst < nameLimit ) && *dst; dst += ( 1 + *dst ) ) {} 6776 require_action_quiet( dst < nameLimit, exit, err = kMalformedErr ); 6777 6778 // Append the string's labels one label at a time. 6779 6780 src = inString; 6781 while( *src ) 6782 { 6783 uint8_t * const label = dst++; 6784 const uint8_t * const labelLimit = Min( dst + kDomainLabelLengthMax, nameLimit - 1 ); 6785 6786 // If the first character is a label separator, then the label is empty. Empty non-root labels are not allowed. 6787 6788 require_action_quiet( *src != '.', exit, err = kMalformedErr ); 6789 6790 // Write the label characters until the end of the label, a separator or NUL character, is encountered, or until no 6791 // more space is available. 6792 6793 while( ( *src != '.' ) && ( *src != '\0' ) && ( dst < labelLimit ) ) 6794 { 6795 uint8_t value; 6796 6797 value = (uint8_t) *src++; 6798 if( value == '\\' ) 6799 { 6800 if( *src == '\0' ) break; 6801 value = (uint8_t) *src++; 6802 if( isdigit_safe( value ) && isdigit_safe( src[ 0 ] ) && isdigit_safe( src[ 1 ] ) ) 6803 { 6804 int decimalValue; 6805 6806 decimalValue = ( ( value - '0' ) * 100 ) + ( ( src[ 0 ] - '0' ) * 10 ) + ( src[ 1 ] - '0' ); 6807 if( decimalValue <= 255 ) 6808 { 6809 value = (uint8_t) decimalValue; 6810 src += 2; 6811 } 6812 } 6813 } 6814 *dst++ = value; 6815 } 6816 if( ( *src == '.' ) || ( *src == '\0' ) ) 6817 { 6818 label[ 0 ] = (uint8_t)( dst - &label[ 1 ] ); // Write the label length. 6819 if( *src == '.' ) ++src; // Advance the pointer past the label separator. 6820 } 6821 else 6822 { 6823 label[ 0 ] = 0; 6824 err = kOverrunErr; 6825 goto exit; 6826 } 6827 } 6828 6829 *dst++ = 0; // Write the empty root label. 6830 if( outEndPtr ) *outEndPtr = dst; 6831 err = kNoErr; 6832 6833exit: 6834 return( err ); 6835} 6836 6837//=========================================================================================================================== 6838// DomainNameEqual 6839//=========================================================================================================================== 6840 6841static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 ) 6842{ 6843 const uint8_t * p1 = inName1; 6844 const uint8_t * p2 = inName2; 6845 unsigned int len; 6846 6847 for( ;; ) 6848 { 6849 if( ( len = *p1++ ) != *p2++ ) return( false ); 6850 if( len == 0 ) break; 6851 for( ; len > 0; ++p1, ++p2, --len ) 6852 { 6853 if( tolower_safe( *p1 ) != tolower_safe( *p2 ) ) return( false ); 6854 } 6855 } 6856 return( true ); 6857} 6858 6859//=========================================================================================================================== 6860// DomainNameToString 6861//=========================================================================================================================== 6862 6863static OSStatus 6864 DomainNameToString( 6865 const uint8_t * inDomainName, 6866 const uint8_t * inEnd, 6867 char inBuf[ kDNSServiceMaxDomainName ], 6868 const uint8_t ** outNextPtr ) 6869{ 6870 OSStatus err; 6871 const uint8_t * label; 6872 uint8_t labelLen; 6873 const uint8_t * nextLabel; 6874 char * dst; 6875 const uint8_t * src; 6876 6877 require_action( !inEnd || ( inDomainName < inEnd ), exit, err = kUnderrunErr ); 6878 6879 // Convert each label up until the root label, i.e., the zero-length label. 6880 6881 dst = inBuf; 6882 for( label = inDomainName; ( labelLen = label[ 0 ] ) != 0; label = nextLabel ) 6883 { 6884 require_action( labelLen <= kDomainLabelLengthMax, exit, err = kMalformedErr ); 6885 6886 nextLabel = &label[ 1 ] + labelLen; 6887 require_action( ( nextLabel - inDomainName ) < kDomainNameLengthMax, exit, err = kMalformedErr ); 6888 require_action( !inEnd || ( nextLabel < inEnd ), exit, err = kUnderrunErr ); 6889 6890 for( src = &label[ 1 ]; src < nextLabel; ++src ) 6891 { 6892 if( isprint_safe( *src ) ) 6893 { 6894 if( ( *src == '.' ) || ( *src == '\\' ) || ( *src == ' ' ) ) *dst++ = '\\'; 6895 *dst++ = (char) *src; 6896 } 6897 else 6898 { 6899 *dst++ = '\\'; 6900 *dst++ = '0' + ( *src / 100 ); 6901 *dst++ = '0' + ( ( *src / 10 ) % 10 ); 6902 *dst++ = '0' + ( *src % 10 ); 6903 } 6904 } 6905 *dst++ = '.'; 6906 } 6907 6908 // At this point, label points to the root label. 6909 // If the root label was the only label, then write a dot for it. 6910 6911 if( label == inDomainName ) *dst++ = '.'; 6912 *dst = '\0'; 6913 if( outNextPtr ) *outNextPtr = label + 1; 6914 err = kNoErr; 6915 6916exit: 6917 return( err ); 6918} 6919 6920//=========================================================================================================================== 6921// PrintDNSMessage 6922//=========================================================================================================================== 6923 6924#define DNSFlagsOpCodeToString( X ) ( \ 6925 ( (X) == kDNSOpCode_Query ) ? "Query" : \ 6926 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \ 6927 ( (X) == kDNSOpCode_Status ) ? "Status" : \ 6928 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \ 6929 ( (X) == kDNSOpCode_Update ) ? "Update" : \ 6930 "Unassigned" ) 6931 6932#define DNSFlagsRCodeToString( X ) ( \ 6933 ( (X) == kDNSRCode_NoError ) ? "NoError" : \ 6934 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \ 6935 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \ 6936 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \ 6937 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \ 6938 ( (X) == kDNSRCode_Refused ) ? "Refused" : \ 6939 "???" ) 6940 6941#define DNSFlagsGetOpCode( X ) ( ( (X) >> 11 ) & 0x0F ) 6942#define DNSFlagsGetRCode( X ) ( (X) & 0x0F ) 6943 6944static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const Boolean inIsMDNS, const Boolean inPrintRaw ) 6945{ 6946 OSStatus err; 6947 const DNSHeader * hdr; 6948 const uint8_t * const msgEnd = inMsgPtr + inMsgLen; 6949 const uint8_t * ptr; 6950 unsigned int id, flags, opcode, rcode; 6951 unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount; 6952 char nameStr[ kDNSServiceMaxDomainName ]; 6953 6954 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr ); 6955 6956 hdr = (DNSHeader *) inMsgPtr; 6957 id = DNSHeaderGetID( hdr ); 6958 flags = DNSHeaderGetFlags( hdr ); 6959 questionCount = DNSHeaderGetQuestionCount( hdr ); 6960 answerCount = DNSHeaderGetAnswerCount( hdr ); 6961 authorityCount = DNSHeaderGetAuthorityCount( hdr ); 6962 additionalCount = DNSHeaderGetAdditionalCount( hdr ); 6963 opcode = DNSFlagsGetOpCode( flags ); 6964 rcode = DNSFlagsGetRCode( flags ); 6965 6966 FPrintF( stdout, "ID: 0x%04X (%u)\n", id, id ); 6967 FPrintF( stdout, "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA %s\n", 6968 flags, 6969 ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ), 6970 ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!', 6971 ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!', 6972 ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!', 6973 ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!', 6974 DNSFlagsRCodeToString( rcode ) ); 6975 FPrintF( stdout, "Question count: %u\n", questionCount ); 6976 FPrintF( stdout, "Answer count: %u\n", answerCount ); 6977 FPrintF( stdout, "Authority count: %u\n", authorityCount ); 6978 FPrintF( stdout, "Additional count: %u\n", additionalCount ); 6979 6980 ptr = (uint8_t *)( hdr + 1 ); 6981 for( i = 0; i < questionCount; ++i ) 6982 { 6983 unsigned int qType, qClass; 6984 Boolean isQU; 6985 6986 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, nameStr, &ptr ); 6987 require_noerr( err, exit ); 6988 6989 if( ( msgEnd - ptr ) < 4 ) 6990 { 6991 err = kUnderrunErr; 6992 goto exit; 6993 } 6994 6995 qType = ReadBig16( ptr ); 6996 ptr += 2; 6997 qClass = ReadBig16( ptr ); 6998 ptr += 2; 6999 7000 isQU = ( inIsMDNS && ( qClass & kQClassUnicastResponseBit ) ) ? true : false; 7001 if( inIsMDNS ) qClass &= ~kQClassUnicastResponseBit; 7002 7003 if( i == 0 ) FPrintF( stdout, "\nQUESTION SECTION\n" ); 7004 7005 FPrintF( stdout, "%s %2s %?2s%?2u %-5s\n", 7006 nameStr, inIsMDNS ? ( isQU ? "QU" : "QM" ) : "", 7007 ( qClass == kDNSServiceClass_IN ), "IN", ( qClass != kDNSServiceClass_IN ), qClass, 7008 RecordTypeToString( qType ) ); 7009 } 7010 7011 totalRRCount = answerCount + authorityCount + additionalCount; 7012 for( i = 0; i < totalRRCount; ++i ) 7013 { 7014 uint16_t type; 7015 uint16_t class; 7016 uint32_t ttl; 7017 const uint8_t * rdataPtr; 7018 size_t rdataLen; 7019 char * rdataStr; 7020 Boolean cacheFlush; 7021 uint8_t name[ kDomainNameLengthMax ]; 7022 7023 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr ); 7024 require_noerr( err, exit ); 7025 7026 err = DomainNameToString( name, NULL, nameStr, NULL ); 7027 require_noerr( err, exit ); 7028 7029 cacheFlush = ( inIsMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false; 7030 if( inIsMDNS ) class &= ~kRRClassCacheFlushBit; 7031 7032 rdataStr = NULL; 7033 if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr ); 7034 if( !rdataStr ) 7035 { 7036 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) rdataLen, INT_MAX ); 7037 require_action( rdataStr, exit, err = kNoMemoryErr ); 7038 } 7039 7040 if( answerCount && ( i == 0 ) ) FPrintF( stdout, "\nANSWER SECTION\n" ); 7041 else if( authorityCount && ( i == answerCount ) ) FPrintF( stdout, "\nAUTHORITY SECTION\n" ); 7042 else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) FPrintF( stdout, "\nADDITIONAL SECTION\n" ); 7043 7044 FPrintF( stdout, "%-42s %6u %2s %?2s%?2u %-5s %s\n", 7045 nameStr, ttl, cacheFlush ? "CF" : "", 7046 ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class, 7047 RecordTypeToString( type ), rdataStr ); 7048 free( rdataStr ); 7049 } 7050 FPrintF( stdout, "\n" ); 7051 err = kNoErr; 7052 7053exit: 7054 return( err ); 7055} 7056 7057//=========================================================================================================================== 7058// WriteDNSQueryMessage 7059//=========================================================================================================================== 7060 7061static OSStatus 7062 WriteDNSQueryMessage( 7063 uint8_t inMsg[ kDNSQueryMessageMaxLen ], 7064 uint16_t inMsgID, 7065 uint16_t inFlags, 7066 const char * inQName, 7067 uint16_t inQType, 7068 uint16_t inQClass, 7069 size_t * outMsgLen ) 7070{ 7071 OSStatus err; 7072 DNSHeader * const hdr = (DNSHeader *) inMsg; 7073 uint8_t * ptr; 7074 size_t msgLen; 7075 7076 WriteBig16( hdr->id, inMsgID ); 7077 WriteBig16( hdr->flags, inFlags ); 7078 WriteBig16( hdr->questionCount, 1 ); 7079 WriteBig16( hdr->answerCount, 0 ); 7080 WriteBig16( hdr->authorityCount, 0 ); 7081 WriteBig16( hdr->additionalCount, 0 ); 7082 7083 ptr = (uint8_t *)( hdr + 1 ); 7084 ptr[ 0 ] = 0; 7085 err = DomainNameAppendString( ptr, inQName, &ptr ); 7086 require_noerr_quiet( err, exit ); 7087 7088 WriteBig16( ptr, inQType ); 7089 ptr += 2; 7090 WriteBig16( ptr, inQClass ); 7091 ptr += 2; 7092 msgLen = (size_t)( ptr - inMsg ); 7093 check( msgLen <= kDNSQueryMessageMaxLen ); 7094 7095 if( outMsgLen ) *outMsgLen = msgLen; 7096 7097exit: 7098 return( err ); 7099} 7100 7101//=========================================================================================================================== 7102// DispatchSignalSourceCreate 7103//=========================================================================================================================== 7104 7105static OSStatus 7106 DispatchSignalSourceCreate( 7107 int inSignal, 7108 DispatchHandler inEventHandler, 7109 void * inContext, 7110 dispatch_source_t * outSource ) 7111{ 7112 OSStatus err; 7113 dispatch_source_t source; 7114 7115 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, dispatch_get_main_queue() ); 7116 require_action( source, exit, err = kUnknownErr ); 7117 7118 dispatch_set_context( source, inContext ); 7119 dispatch_source_set_event_handler_f( source, inEventHandler ); 7120 7121 *outSource = source; 7122 err = kNoErr; 7123 7124exit: 7125 return( err ); 7126} 7127 7128//=========================================================================================================================== 7129// DispatchReadSourceCreate 7130//=========================================================================================================================== 7131 7132static OSStatus 7133 DispatchReadSourceCreate( 7134 SocketRef inSock, 7135 DispatchHandler inEventHandler, 7136 DispatchHandler inCancelHandler, 7137 void * inContext, 7138 dispatch_source_t * outSource ) 7139{ 7140 OSStatus err; 7141 dispatch_source_t source; 7142 7143 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_READ, (uintptr_t) inSock, 0, dispatch_get_main_queue() ); 7144 require_action( source, exit, err = kUnknownErr ); 7145 7146 dispatch_set_context( source, inContext ); 7147 dispatch_source_set_event_handler_f( source, inEventHandler ); 7148 dispatch_source_set_cancel_handler_f( source, inCancelHandler ); 7149 7150 *outSource = source; 7151 err = kNoErr; 7152 7153exit: 7154 return( err ); 7155} 7156 7157//=========================================================================================================================== 7158// DispatchTimerCreate 7159//=========================================================================================================================== 7160 7161static OSStatus 7162 DispatchTimerCreate( 7163 dispatch_time_t inStart, 7164 uint64_t inIntervalNs, 7165 uint64_t inLeewayNs, 7166 DispatchHandler inEventHandler, 7167 DispatchHandler inCancelHandler, 7168 void * inContext, 7169 dispatch_source_t * outTimer ) 7170{ 7171 OSStatus err; 7172 dispatch_source_t timer; 7173 7174 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() ); 7175 require_action( timer, exit, err = kUnknownErr ); 7176 7177 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs ); 7178 dispatch_set_context( timer, inContext ); 7179 dispatch_source_set_event_handler_f( timer, inEventHandler ); 7180 dispatch_source_set_cancel_handler_f( timer, inCancelHandler ); 7181 7182 *outTimer = timer; 7183 err = kNoErr; 7184 7185exit: 7186 return( err ); 7187} 7188 7189//=========================================================================================================================== 7190// ServiceTypeDescription 7191//=========================================================================================================================== 7192 7193typedef struct 7194{ 7195 const char * name; // Name of the service type in two-label "_service._proto" format. 7196 const char * description; // Description of the service type. 7197 7198} ServiceType; 7199 7200// A Non-comprehensive table of DNS-SD service types 7201 7202static const ServiceType kServiceTypes[] = 7203{ 7204 { "_acp-sync._tcp", "AirPort Base Station Sync" }, 7205 { "_adisk._tcp", "Automatic Disk Discovery" }, 7206 { "_afpovertcp._tcp", "Apple File Sharing" }, 7207 { "_airdrop._tcp", "AirDrop" }, 7208 { "_airplay._tcp", "AirPlay" }, 7209 { "_airport._tcp", "AirPort Base Station" }, 7210 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" }, 7211 { "_eppc._tcp", "Remote AppleEvents" }, 7212 { "_ftp._tcp", "File Transfer Protocol" }, 7213 { "_home-sharing._tcp", "Home Sharing" }, 7214 { "_homekit._tcp", "HomeKit" }, 7215 { "_http._tcp", "World Wide Web HTML-over-HTTP" }, 7216 { "_https._tcp", "HTTP over SSL/TLS" }, 7217 { "_ipp._tcp", "Internet Printing Protocol" }, 7218 { "_ldap._tcp", "Lightweight Directory Access Protocol" }, 7219 { "_mediaremotetv._tcp", "Media Remote" }, 7220 { "_net-assistant._tcp", "Apple Remote Desktop" }, 7221 { "_od-master._tcp", "OpenDirectory Master" }, 7222 { "_nfs._tcp", "Network File System" }, 7223 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" }, 7224 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" }, 7225 { "_raop._tcp", "Remote Audio Output Protocol" }, 7226 { "_rfb._tcp", "Remote Frame Buffer" }, 7227 { "_scanner._tcp", "Bonjour Scanning" }, 7228 { "_smb._tcp", "Server Message Block over TCP/IP" }, 7229 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" }, 7230 { "_sleep-proxy._udp", "Sleep Proxy Server" }, 7231 { "_ssh._tcp", "SSH Remote Login Protocol" }, 7232 { "_teleport._tcp", "teleport" }, 7233 { "_tftp._tcp", "Trivial File Transfer Protocol" }, 7234 { "_workstation._tcp", "Workgroup Manager" }, 7235 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" }, 7236 { "_webdavs._tcp", "WebDAV over SSL/TLS" } 7237}; 7238 7239static const char * ServiceTypeDescription( const char *inName ) 7240{ 7241 const ServiceType * serviceType; 7242 const ServiceType * const end = kServiceTypes + countof( kServiceTypes ); 7243 7244 for( serviceType = kServiceTypes; serviceType < end; ++serviceType ) 7245 { 7246 if( strcasecmp( inName, serviceType->name ) == 0 ) return( serviceType->description ); 7247 } 7248 return( NULL ); 7249} 7250 7251//=========================================================================================================================== 7252// SocketContextCancelHandler 7253//=========================================================================================================================== 7254 7255static void SocketContextCancelHandler( void *inContext ) 7256{ 7257 SocketContext * const context = (SocketContext *) inContext; 7258 7259 ForgetSocket( &context->sock ); 7260 free( context ); 7261} 7262 7263//=========================================================================================================================== 7264// StringToInt32 7265//=========================================================================================================================== 7266 7267static OSStatus StringToInt32( const char *inString, int32_t *outValue ) 7268{ 7269 OSStatus err; 7270 long value; 7271 char * endPtr; 7272 7273 value = strtol( inString, &endPtr, 0 ); 7274 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr ); 7275 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr ); 7276 7277 *outValue = (int32_t) value; 7278 err = kNoErr; 7279 7280exit: 7281 return( err ); 7282} 7283 7284//=========================================================================================================================== 7285// StringToUInt32 7286//=========================================================================================================================== 7287 7288static OSStatus StringToUInt32( const char *inString, uint32_t *outValue ) 7289{ 7290 OSStatus err; 7291 uint32_t value; 7292 char * endPtr; 7293 7294 value = (uint32_t) strtol( inString, &endPtr, 0 ); 7295 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr ); 7296 7297 *outValue = value; 7298 err = kNoErr; 7299 7300exit: 7301 return( err ); 7302} 7303 7304#if( TARGET_OS_DARWIN ) 7305//=========================================================================================================================== 7306// GetDefaultDNSServer 7307//=========================================================================================================================== 7308 7309static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr ) 7310{ 7311 OSStatus err; 7312 dns_config_t * config; 7313 struct sockaddr * addr; 7314 int32_t i; 7315 7316 config = dns_configuration_copy(); 7317 require_action( config, exit, err = kUnknownErr ); 7318 7319 addr = NULL; 7320 for( i = 0; i < config->n_resolver; ++i ) 7321 { 7322 const dns_resolver_t * const resolver = config->resolver[ i ]; 7323 7324 if( !resolver->domain && ( resolver->n_nameserver > 0 ) ) 7325 { 7326 addr = resolver->nameserver[ 0 ]; 7327 break; 7328 } 7329 } 7330 require_action_quiet( addr, exit, err = kNotFoundErr ); 7331 7332 SockAddrCopy( addr, outAddr ); 7333 err = kNoErr; 7334 7335exit: 7336 if( config ) dns_configuration_free( config ); 7337 return( err ); 7338} 7339#endif 7340 7341//=========================================================================================================================== 7342// SocketWriteAll 7343// 7344// Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework. 7345//=========================================================================================================================== 7346 7347OSStatus SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs ) 7348{ 7349 OSStatus err; 7350 const uint8_t * src; 7351 const uint8_t * end; 7352 fd_set writeSet; 7353 struct timeval timeout; 7354 ssize_t n; 7355 7356 FD_ZERO( &writeSet ); 7357 src = (const uint8_t *) inData; 7358 end = src + inSize; 7359 while( src < end ) 7360 { 7361 FD_SET( inSock, &writeSet ); 7362 timeout.tv_sec = inTimeoutSecs; 7363 timeout.tv_usec = 0; 7364 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout ); 7365 if( n == 0 ) { err = kTimeoutErr; goto exit; } 7366 err = map_socket_value_errno( inSock, n > 0, n ); 7367 require_noerr( err, exit ); 7368 7369 n = send( inSock, (char *) src, (size_t)( end - src ), 0 ); 7370 err = map_socket_value_errno( inSock, n >= 0, n ); 7371 if( err == EINTR ) continue; 7372 require_noerr( err, exit ); 7373 7374 src += n; 7375 } 7376 err = kNoErr; 7377 7378exit: 7379 return( err ); 7380} 7381 7382//=========================================================================================================================== 7383// ParseEscapedString 7384// 7385// Note: This was copied from CoreUtils because the ParseEscapedString function is currently not exported in the framework. 7386//=========================================================================================================================== 7387 7388OSStatus 7389 ParseEscapedString( 7390 const char * inSrc, 7391 const char * inEnd, 7392 char inDelimiter, 7393 char * inBuf, 7394 size_t inMaxLen, 7395 size_t * outCopiedLen, 7396 size_t * outTotalLen, 7397 const char ** outSrc ) 7398{ 7399 OSStatus err; 7400 char c; 7401 char * dst; 7402 char * lim; 7403 size_t len; 7404 7405 dst = inBuf; 7406 lim = dst + ( ( inMaxLen > 0 ) ? ( inMaxLen - 1 ) : 0 ); // Leave room for null terminator. 7407 len = 0; 7408 while( ( inSrc < inEnd ) && ( ( c = *inSrc++ ) != inDelimiter ) ) 7409 { 7410 if( c == '\\' ) 7411 { 7412 require_action_quiet( inSrc < inEnd, exit, err = kUnderrunErr ); 7413 c = *inSrc++; 7414 } 7415 if( dst < lim ) 7416 { 7417 if( inBuf ) *dst = c; 7418 ++dst; 7419 } 7420 ++len; 7421 } 7422 if( inBuf && ( inMaxLen > 0 ) ) *dst = '\0'; 7423 err = kNoErr; 7424 7425exit: 7426 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - inBuf ); 7427 if( outTotalLen ) *outTotalLen = len; 7428 if( outSrc ) *outSrc = inSrc; 7429 return( err ); 7430} 7431 7432//=========================================================================================================================== 7433// ParseIPv4Address 7434// 7435// Warning: "inBuffer" may be modified even in error cases. 7436// 7437// Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework. 7438//=========================================================================================================================== 7439 7440static OSStatus ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr ) 7441{ 7442 OSStatus err; 7443 uint8_t * dst; 7444 int segments; 7445 int sawDigit; 7446 int c; 7447 int v; 7448 7449 check( inBuffer ); 7450 check( outStr ); 7451 7452 dst = inBuffer; 7453 *dst = 0; 7454 sawDigit = 0; 7455 segments = 0; 7456 for( ; ( c = *inStr ) != '\0'; ++inStr ) 7457 { 7458 if( isdigit_safe( c ) ) 7459 { 7460 v = ( *dst * 10 ) + ( c - '0' ); 7461 require_action_quiet( v <= 255, exit, err = kRangeErr ); 7462 *dst = (uint8_t) v; 7463 if( !sawDigit ) 7464 { 7465 ++segments; 7466 require_action_quiet( segments <= 4, exit, err = kOverrunErr ); 7467 sawDigit = 1; 7468 } 7469 } 7470 else if( ( c == '.' ) && sawDigit ) 7471 { 7472 require_action_quiet( segments < 4, exit, err = kMalformedErr ); 7473 *++dst = 0; 7474 sawDigit = 0; 7475 } 7476 else 7477 { 7478 break; 7479 } 7480 } 7481 require_action_quiet( segments == 4, exit, err = kUnderrunErr ); 7482 7483 *outStr = inStr; 7484 err = kNoErr; 7485 7486exit: 7487 return( err ); 7488} 7489 7490//=========================================================================================================================== 7491// StringToIPv4Address 7492// 7493// Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework. 7494//=========================================================================================================================== 7495 7496OSStatus 7497 StringToIPv4Address( 7498 const char * inStr, 7499 StringToIPAddressFlags inFlags, 7500 uint32_t * outIP, 7501 int * outPort, 7502 uint32_t * outSubnet, 7503 uint32_t * outRouter, 7504 const char ** outStr ) 7505{ 7506 OSStatus err; 7507 uint8_t buf[ 4 ]; 7508 int c; 7509 uint32_t ip; 7510 int hasPort; 7511 int port; 7512 int hasPrefix; 7513 int prefix; 7514 uint32_t subnetMask; 7515 uint32_t router; 7516 7517 require_action( inStr, exit, err = kParamErr ); 7518 7519 // Parse the address-only part of the address (e.g. "1.2.3.4"). 7520 7521 err = ParseIPv4Address( inStr, buf, &inStr ); 7522 require_noerr_quiet( err, exit ); 7523 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] ); 7524 c = *inStr; 7525 7526 // Parse the port (if any). 7527 7528 hasPort = 0; 7529 port = 0; 7530 if( c == ':' ) 7531 { 7532 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr ); 7533 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' ); 7534 require_action_quiet( port <= 65535, exit, err = kRangeErr ); 7535 hasPort = 1; 7536 } 7537 7538 // Parse the prefix length (if any). 7539 7540 hasPrefix = 0; 7541 prefix = 0; 7542 subnetMask = 0; 7543 router = 0; 7544 if( c == '/' ) 7545 { 7546 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr ); 7547 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' ); 7548 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr ); 7549 hasPrefix = 1; 7550 7551 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0; 7552 router = ( ip & subnetMask ) | 1; 7553 } 7554 7555 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults. 7556 7557 if( outIP ) *outIP = ip; 7558 if( outPort && hasPort ) *outPort = port; 7559 if( outSubnet && hasPrefix ) *outSubnet = subnetMask; 7560 if( outRouter && hasPrefix ) *outRouter = router; 7561 if( outStr ) *outStr = inStr; 7562 err = kNoErr; 7563 7564exit: 7565 return( err ); 7566} 7567 7568//=========================================================================================================================== 7569// ParseIPv6Address 7570// 7571// Note: Parsed according to the rules specified in RFC 3513. 7572// Warning: "inBuffer" may be modified even in error cases. 7573// 7574// Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework. 7575//=========================================================================================================================== 7576 7577static OSStatus ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr ) 7578{ 7579 // Table to map uppercase hex characters - '0' to their numeric values. 7580 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F 7581 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 }; 7582 OSStatus err; 7583 const char * ptr; 7584 uint8_t * dst; 7585 uint8_t * lim; 7586 uint8_t * colonPtr; 7587 int c; 7588 int sawDigit; 7589 unsigned int v; 7590 int i; 7591 int n; 7592 7593 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1"). 7594 7595 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0; 7596 7597 // Special case leading :: (e.g. "::1") to simplify processing later. 7598 7599 if( *inStr == ':' ) 7600 { 7601 ++inStr; 7602 require_action_quiet( *inStr == ':', exit, err = kMalformedErr ); 7603 } 7604 7605 // Parse the address. 7606 7607 ptr = inStr; 7608 dst = inBuffer; 7609 lim = dst + 16; 7610 colonPtr = NULL; 7611 sawDigit = 0; 7612 v = 0; 7613 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) ) 7614 { 7615 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' ); 7616 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) ) 7617 { 7618 c -= '0'; 7619 check( c < (int) countof( kASCIItoHexTable ) ); 7620 v = ( v << 4 ) | kASCIItoHexTable[ c ]; 7621 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr ); 7622 sawDigit = 1; 7623 continue; 7624 } 7625 if( c == ':' ) 7626 { 7627 ptr = inStr; 7628 if( !sawDigit ) 7629 { 7630 require_action_quiet( !colonPtr, exit, err = kMalformedErr ); 7631 colonPtr = dst; 7632 continue; 7633 } 7634 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr ); 7635 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr ); 7636 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF ); 7637 *dst++ = (uint8_t)( v & 0xFF ); 7638 sawDigit = 0; 7639 v = 0; 7640 continue; 7641 } 7642 7643 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4). 7644 7645 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) ) 7646 { 7647 err = ParseIPv4Address( ptr, dst, &inStr ); 7648 require_noerr_quiet( err, exit ); 7649 dst += 4; 7650 sawDigit = 0; 7651 ++inStr; // Increment because the code below expects the end to be at "inStr - 1". 7652 } 7653 break; 7654 } 7655 if( sawDigit ) 7656 { 7657 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr ); 7658 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF ); 7659 *dst++ = (uint8_t)( v & 0xFF ); 7660 } 7661 check( dst <= lim ); 7662 if( colonPtr ) 7663 { 7664 require_action_quiet( dst < lim, exit, err = kOverrunErr ); 7665 n = (int)( dst - colonPtr ); 7666 for( i = 1; i <= n; ++i ) 7667 { 7668 lim[ -i ] = colonPtr[ n - i ]; 7669 colonPtr[ n - i ] = 0; 7670 } 7671 dst = lim; 7672 } 7673 require_action_quiet( dst == lim, exit, err = kUnderrunErr ); 7674 7675 *outStr = inStr - 1; 7676 err = kNoErr; 7677 7678exit: 7679 return( err ); 7680} 7681 7682//=========================================================================================================================== 7683// ParseIPv6Scope 7684// 7685// Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework. 7686//=========================================================================================================================== 7687 7688static OSStatus ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr ) 7689{ 7690#if( TARGET_OS_POSIX ) 7691 OSStatus err; 7692 char scopeStr[ 64 ]; 7693 char * dst; 7694 char * lim; 7695 int c; 7696 uint32_t scope; 7697 const char * ptr; 7698 7699 // Copy into a local NULL-terminated string since that is what if_nametoindex expects. 7700 7701 dst = scopeStr; 7702 lim = dst + ( countof( scopeStr ) - 1 ); 7703 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) ) 7704 { 7705 *dst++ = *inStr++; 7706 } 7707 *dst = '\0'; 7708 check( dst <= lim ); 7709 7710 // First try to map as a name and if that fails, treat it as a numeric scope. 7711 7712 scope = if_nametoindex( scopeStr ); 7713 if( scope == 0 ) 7714 { 7715 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr ) 7716 { 7717 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' ); 7718 } 7719 require_action_quiet( c == '\0', exit, err = kMalformedErr ); 7720 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr ); 7721 } 7722 7723 *outScope = scope; 7724 *outStr = inStr; 7725 err = kNoErr; 7726 7727exit: 7728 return( err ); 7729#else 7730 OSStatus err; 7731 uint32_t scope; 7732 const char * start; 7733 int c; 7734 7735 scope = 0; 7736 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr ) 7737 { 7738 scope = ( scope * 10 ) + ( c - '0' ); 7739 } 7740 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr ); 7741 7742 *outScope = scope; 7743 *outStr = inStr; 7744 err = kNoErr; 7745 7746exit: 7747 return( err ); 7748#endif 7749} 7750 7751//=========================================================================================================================== 7752// StringToIPv6Address 7753// 7754// Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework. 7755//=========================================================================================================================== 7756 7757OSStatus 7758 StringToIPv6Address( 7759 const char * inStr, 7760 StringToIPAddressFlags inFlags, 7761 uint8_t outIPv6[ 16 ], 7762 uint32_t * outScope, 7763 int * outPort, 7764 int * outPrefix, 7765 const char ** outStr ) 7766{ 7767 OSStatus err; 7768 uint8_t ipv6[ 16 ]; 7769 int c; 7770 int hasScope; 7771 uint32_t scope; 7772 int hasPort; 7773 int port; 7774 int hasPrefix; 7775 int prefix; 7776 int hasBracket; 7777 int i; 7778 7779 require_action( inStr, exit, err = kParamErr ); 7780 7781 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80"). 7782 7783 // Parse the address-only part of the address (e.g. "1::1"). 7784 7785 err = ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr ); 7786 require_noerr_quiet( err, exit ); 7787 c = *inStr; 7788 7789 // Parse the scope, port, or prefix length. 7790 7791 hasScope = 0; 7792 scope = 0; 7793 hasPort = 0; 7794 port = 0; 7795 hasPrefix = 0; 7796 prefix = 0; 7797 hasBracket = 0; 7798 for( ;; ) 7799 { 7800 if( c == '%' ) // Scope (e.g. "%en0" or "%5") 7801 { 7802 require_action_quiet( !hasScope, exit, err = kMalformedErr ); 7803 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr ); 7804 ++inStr; 7805 err = ParseIPv6Scope( inStr, &scope, &inStr ); 7806 require_noerr_quiet( err, exit ); 7807 hasScope = 1; 7808 c = *inStr; 7809 } 7810 else if( c == ':' ) // Port (e.g. ":80") 7811 { 7812 require_action_quiet( !hasPort, exit, err = kMalformedErr ); 7813 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr ); 7814 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' ); 7815 require_action_quiet( port <= 65535, exit, err = kRangeErr ); 7816 hasPort = 1; 7817 } 7818 else if( c == '/' ) // Prefix Length (e.g. "/64") 7819 { 7820 require_action_quiet( !hasPrefix, exit, err = kMalformedErr ); 7821 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr ); 7822 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' ); 7823 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr ); 7824 hasPrefix = 1; 7825 } 7826 else if( c == ']' ) 7827 { 7828 require_action_quiet( !hasBracket, exit, err = kMalformedErr ); 7829 hasBracket = 1; 7830 c = *( ++inStr ); 7831 } 7832 else 7833 { 7834 break; 7835 } 7836 } 7837 7838 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults. 7839 7840 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ]; 7841 if( outScope && hasScope ) *outScope = scope; 7842 if( outPort && hasPort ) *outPort = port; 7843 if( outPrefix && hasPrefix ) *outPrefix = prefix; 7844 if( outStr ) *outStr = inStr; 7845 err = kNoErr; 7846 7847exit: 7848 return( err ); 7849} 7850 7851//=========================================================================================================================== 7852// StringArray_Append 7853// 7854// Note: This was copied from CoreUtils because the StringArray_Append function is currently not exported in the framework. 7855//=========================================================================================================================== 7856 7857OSStatus StringArray_Append( char ***ioArray, size_t *ioCount, const char *inStr ) 7858{ 7859 OSStatus err; 7860 char * newStr; 7861 size_t oldCount; 7862 size_t newCount; 7863 char ** oldArray; 7864 char ** newArray; 7865 7866 newStr = strdup( inStr ); 7867 require_action( newStr, exit, err = kNoMemoryErr ); 7868 7869 oldCount = *ioCount; 7870 newCount = oldCount + 1; 7871 newArray = (char **) malloc( newCount * sizeof( *newArray ) ); 7872 require_action( newArray, exit, err = kNoMemoryErr ); 7873 7874 if( oldCount > 0 ) 7875 { 7876 oldArray = *ioArray; 7877 memcpy( newArray, oldArray, oldCount * sizeof( *oldArray ) ); 7878 free( oldArray ); 7879 } 7880 newArray[ oldCount ] = newStr; 7881 newStr = NULL; 7882 7883 *ioArray = newArray; 7884 *ioCount = newCount; 7885 err = kNoErr; 7886 7887exit: 7888 if( newStr ) free( newStr ); 7889 return( err ); 7890} 7891 7892//=========================================================================================================================== 7893// StringArray_Free 7894// 7895// Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework. 7896//=========================================================================================================================== 7897 7898void StringArray_Free( char **inArray, size_t inCount ) 7899{ 7900 size_t i; 7901 7902 for( i = 0; i < inCount; ++i ) 7903 { 7904 free( inArray[ i ] ); 7905 } 7906 if( inCount > 0 ) free( inArray ); 7907} 7908 7909//=========================================================================================================================== 7910// ParseQuotedEscapedString 7911// 7912// Note: This was copied from CoreUtils because it's currently not exported in the framework. 7913//=========================================================================================================================== 7914 7915Boolean 7916 ParseQuotedEscapedString( 7917 const char * inSrc, 7918 const char * inEnd, 7919 const char * inDelimiters, 7920 char * inBuf, 7921 size_t inMaxLen, 7922 size_t * outCopiedLen, 7923 size_t * outTotalLen, 7924 const char ** outSrc ) 7925{ 7926 const unsigned char * src; 7927 const unsigned char * end; 7928 unsigned char * dst; 7929 unsigned char * lim; 7930 unsigned char c; 7931 unsigned char c2; 7932 size_t totalLen; 7933 Boolean singleQuote; 7934 Boolean doubleQuote; 7935 7936 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc ); 7937 src = (const unsigned char *) inSrc; 7938 end = (const unsigned char *) inEnd; 7939 dst = (unsigned char *) inBuf; 7940 lim = dst + inMaxLen; 7941 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces. 7942 if( src >= end ) return( false ); 7943 7944 // Parse each argument from the string. 7945 // 7946 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details. 7947 7948 totalLen = 0; 7949 singleQuote = false; 7950 doubleQuote = false; 7951 while( src < end ) 7952 { 7953 c = *src++; 7954 if( singleQuote ) 7955 { 7956 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes. 7957 7958 if( c == '\'' ) 7959 { 7960 singleQuote = false; 7961 continue; 7962 } 7963 } 7964 else if( doubleQuote ) 7965 { 7966 // Double quotes protect everything except double quotes and backslashes. A backslash can be 7967 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely. 7968 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte. 7969 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte. 7970 // A backslash that does not precede ", \, x, X, or a newline is taken literally. 7971 7972 if( c == '"' ) 7973 { 7974 doubleQuote = false; 7975 continue; 7976 } 7977 else if( c == '\\' ) 7978 { 7979 if( src < end ) 7980 { 7981 c2 = *src; 7982 if( ( c2 == '"' ) || ( c2 == '\\' ) ) 7983 { 7984 ++src; 7985 c = c2; 7986 } 7987 else if( c2 == '\n' ) 7988 { 7989 ++src; 7990 continue; 7991 } 7992 else if( ( c2 == 'x' ) || ( c2 == 'X' ) ) 7993 { 7994 ++src; 7995 c = c2; 7996 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) ) 7997 { 7998 c = HexPairToByte( src ); 7999 src += 2; 8000 } 8001 } 8002 else if( isoctal_safe( c2 ) ) 8003 { 8004 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) ) 8005 { 8006 c = OctalTripleToByte( src ); 8007 src += 3; 8008 } 8009 } 8010 } 8011 } 8012 } 8013 else if( strchr( inDelimiters, c ) ) 8014 { 8015 break; 8016 } 8017 else if( c == '\\' ) 8018 { 8019 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes. 8020 // A backslash followed by a newline disappears completely. 8021 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte. 8022 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte. 8023 8024 if( src < end ) 8025 { 8026 c = *src; 8027 if( c == '\n' ) 8028 { 8029 ++src; 8030 continue; 8031 } 8032 else if( ( c == 'x' ) || ( c == 'X' ) ) 8033 { 8034 ++src; 8035 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) ) 8036 { 8037 c = HexPairToByte( src ); 8038 src += 2; 8039 } 8040 } 8041 else if( isoctal_safe( c ) ) 8042 { 8043 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) ) 8044 { 8045 c = OctalTripleToByte( src ); 8046 src += 3; 8047 } 8048 else 8049 { 8050 ++src; 8051 } 8052 } 8053 else 8054 { 8055 ++src; 8056 } 8057 } 8058 } 8059 else if( c == '\'' ) 8060 { 8061 singleQuote = true; 8062 continue; 8063 } 8064 else if( c == '"' ) 8065 { 8066 doubleQuote = true; 8067 continue; 8068 } 8069 8070 if( dst < lim ) 8071 { 8072 if( inBuf ) *dst = c; 8073 ++dst; 8074 } 8075 ++totalLen; 8076 } 8077 8078 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) ); 8079 if( outTotalLen ) *outTotalLen = totalLen; 8080 if( outSrc ) *outSrc = (const char *) src; 8081 return( true ); 8082} 8083