From c9b9ae52cd24d9becbe66090b85e59f088d39b79 Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 10 Sep 2003 00:51:47 +0000 Subject: [PATCH] mDNSResponder-58.tar.gz --- APPLE_LICENSE | 367 + CFSocket.c | 879 -- DNSServiceDiscoveryDefines.h | 24 - DNSServiceDiscoveryReply.defs | 23 - DNSServiceDiscoveryRequest.defs | 23 - Makefile | 33 + README.txt | 145 + daemon.c | 1182 --- mDNSCore/Implementer Notes.txt | 66 + mDNSCore/mDNS.c | 7238 ++++++++++++----- mDNSCore/mDNSClientAPI.h | 1141 ++- mDNSCore/mDNSDebug.h | 99 +- mDNSCore/mDNSPlatformEnvironment.h | 103 - mDNSCore/mDNSPlatformFunctions.h | 156 +- mDNSCore/mDNSsprintf.c | 227 - mDNSCore/mDNSsprintf.h | 9 - mDNSCore/mDNSvsprintf.h | 9 - mDNSMacOS9/CarbonResource.r | 31 + mDNSMacOS9/Mac OS Test Responder.c | 248 + mDNSMacOS9/Mac OS Test Searcher.c | 255 + mDNSMacOS9/README.txt | 10 + mDNSMacOS9/mDNS.mcp | Bin 0 -> 413450 bytes mDNSMacOS9/mDNSMacOS9.c | 544 ++ mDNSMacOS9/mDNSMacOS9.h | 66 + mDNSMacOS9/mDNSPrefixCarbon.h | 43 + mDNSMacOS9/mDNSPrefixCarbonDebug.h | 43 + mDNSMacOS9/mDNSPrefixClassic.h | 40 + .../DNSServiceBrowser/BrowserController.h | 83 + .../DNSServiceBrowser/BrowserController.m | 541 ++ .../project.pbxproj | 377 + .../English.lproj/InfoPlist.strings | Bin 0 -> 612 bytes .../English.lproj/MainMenu.nib/classes.nib | 37 + .../English.lproj/MainMenu.nib/info.nib | 22 + .../English.lproj/MainMenu.nib/objects.nib | Bin 0 -> 9876 bytes .../Applications/DNSServiceBrowser/main.m | 36 + .../project.pbxproj | 377 + .../English.lproj/InfoPlist.strings | Bin 0 -> 642 bytes .../English.lproj/MainMenu.nib/classes.nib | 30 + .../English.lproj/MainMenu.nib/info.nib | 22 + .../English.lproj/MainMenu.nib/objects.nib | Bin 0 -> 9113 bytes .../RegistrationController.h | 66 + .../RegistrationController.m | 247 + .../DNSServiceRegistration/main.m | 36 + .../HAAutomounter/HAAutomounter.h | 40 + .../HAAutomounter/HAAutomounter.m | 102 + .../HAAutomounter.pbproj/project.pbxproj | 296 + mDNSMacOSX/Applications/HAAutomounter/main.m | 44 + mDNSMacOSX/CFSocket.c | 1464 ++++ mDNSMacOSX/CFSocketPuma.c | 238 + mDNSMacOSX/DNSServiceDiscoveryDefines.h | 43 + mDNSMacOSX/DNSServiceDiscoveryReply.defs | 66 + mDNSMacOSX/DNSServiceDiscoveryRequest.defs | 85 + mDNSMacOSX/SampleUDSClient.c | 411 + .../SamplemDNSClient.c | 201 +- mDNSMacOSX/daemon.c | 1701 ++++ mDNSMacOSX/dns_sd.h | 1006 +++ mDNSMacOSX/dnssd_clientstub.c | 998 +++ mDNSMacOSX/dnssd_ipc.c | 137 + mDNSMacOSX/dnssd_ipc.h | 162 + mDNSMacOSX/mDNSMacOSX.h | 192 + .../mDNSResponder.pbproj}/project.pbxproj | 515 +- mDNSMacOSX/uds_daemon.c | 2212 +++++ mDNSPosix/Client.c | 254 + mDNSPosix/ExampleClientApp.c | 113 + mDNSPosix/ExampleClientApp.h | 37 + mDNSPosix/Identify.c | 329 + mDNSPosix/Makefile | 120 + mDNSPosix/NetMonitor.c | 928 +++ mDNSPosix/ProxyResponder.c | 332 + mDNSPosix/ReadMe.txt | 310 + mDNSPosix/Responder.c | 923 +++ mDNSPosix/Services.txt | 14 + mDNSPosix/mDNSPosix.c | 999 +++ mDNSPosix/mDNSPosix.h | 91 + mDNSPosix/mDNSUNP.c | 424 + mDNSPosix/mDNSUNP.h | 120 + mDNSVxWorks/README.txt | 8 + mDNSVxWorks/mDNSVxWorks.c | 2097 +++++ mDNSVxWorks/mDNSVxWorks.h | 144 + .../RendezvousBrowser/Windows/Application.sln | 21 + .../Windows/Application.vcproj | 244 + .../Windows/Resources/Application.ico | Bin 0 -> 2238 bytes .../Windows/Resources/Application.rc | 281 + .../Windows/Resources/Application.rc2 | 39 + .../Windows/Resources/Resource.h | 45 + .../Windows/Sources/AboutDialog.cpp | 96 + .../Windows/Sources/AboutDialog.h | 87 + .../Windows/Sources/Application.cpp | 145 + .../Windows/Sources/Application.h | 93 + .../Windows/Sources/ChooserDialog.cpp | 1171 +++ .../Windows/Sources/ChooserDialog.h | 155 + .../Windows/Sources/StdAfx.cpp | 43 + .../Windows/Sources/StdAfx.h | 72 + .../WindowsCE/Application.vcc | 37 + .../WindowsCE/Application.vcp | 499 ++ .../WindowsCE/Application.vcw | 29 + .../WindowsCE/Resources/Application.ico | Bin 0 -> 1078 bytes .../WindowsCE/Resources/Application.rc | 194 + .../WindowsCE/Resources/Application.rc2 | 13 + .../WindowsCE/Resources/newres.h | 28 + .../WindowsCE/Resources/resource.h | 22 + .../WindowsCE/Sources/Application.cpp | 111 + .../WindowsCE/Sources/Application.h | 70 + .../WindowsCE/Sources/BrowserDialog.cpp | 257 + .../WindowsCE/Sources/BrowserDialog.h | 96 + .../WindowsCE/Sources/StdAfx.cpp | 31 + .../WindowsCE/Sources/StdAfx.h | 58 + .../Applications/RendezvousTest/Tool.c | 1047 +++ .../RendezvousTest/ToolPrefixWindows.h | 39 + .../RendezvousTest/ToolPrefixWindowsDebug.h | 40 + .../Applications/RendezvousTest/ToolWin32.mcp | Bin 0 -> 284642 bytes .../Applications/RendezvousTest/ToolWin32.sln | 21 + .../RendezvousTest/ToolWin32.vcproj | 185 + mDNSWindows/DNSServices/DNSServiceDiscovery.c | 761 ++ mDNSWindows/DNSServices/DNSServiceDiscovery.h | 343 + mDNSWindows/DNSServices/DNSServices.c | 3158 +++++++ mDNSWindows/DNSServices/DNSServices.h | 1912 +++++ mDNSWindows/README.txt | 44 + mDNSWindows/mDNSWin32.c | 2267 ++++++ mDNSWindows/mDNSWin32.h | 194 + 120 files changed, 40956 insertions(+), 5026 deletions(-) create mode 100644 APPLE_LICENSE delete mode 100644 CFSocket.c delete mode 100644 DNSServiceDiscoveryDefines.h delete mode 100644 DNSServiceDiscoveryReply.defs delete mode 100644 DNSServiceDiscoveryRequest.defs create mode 100644 Makefile create mode 100644 README.txt delete mode 100644 daemon.c create mode 100644 mDNSCore/Implementer Notes.txt delete mode 100755 mDNSCore/mDNSPlatformEnvironment.h delete mode 100755 mDNSCore/mDNSsprintf.c delete mode 100755 mDNSCore/mDNSsprintf.h delete mode 100755 mDNSCore/mDNSvsprintf.h create mode 100644 mDNSMacOS9/CarbonResource.r create mode 100644 mDNSMacOS9/Mac OS Test Responder.c create mode 100644 mDNSMacOS9/Mac OS Test Searcher.c create mode 100644 mDNSMacOS9/README.txt create mode 100644 mDNSMacOS9/mDNS.mcp create mode 100644 mDNSMacOS9/mDNSMacOS9.c create mode 100755 mDNSMacOS9/mDNSMacOS9.h create mode 100644 mDNSMacOS9/mDNSPrefixCarbon.h create mode 100644 mDNSMacOS9/mDNSPrefixCarbonDebug.h create mode 100644 mDNSMacOS9/mDNSPrefixClassic.h create mode 100755 mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h create mode 100755 mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m create mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj create mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings create mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib create mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib create mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib create mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/main.m create mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj create mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings create mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/classes.nib create mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/info.nib create mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib create mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.h create mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m create mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/main.m create mode 100644 mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h create mode 100644 mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m create mode 100644 mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj create mode 100644 mDNSMacOSX/Applications/HAAutomounter/main.m create mode 100644 mDNSMacOSX/CFSocket.c create mode 100644 mDNSMacOSX/CFSocketPuma.c create mode 100644 mDNSMacOSX/DNSServiceDiscoveryDefines.h create mode 100644 mDNSMacOSX/DNSServiceDiscoveryReply.defs create mode 100644 mDNSMacOSX/DNSServiceDiscoveryRequest.defs create mode 100755 mDNSMacOSX/SampleUDSClient.c rename SamplemDNSClient.c => mDNSMacOSX/SamplemDNSClient.c (64%) create mode 100644 mDNSMacOSX/daemon.c create mode 100755 mDNSMacOSX/dns_sd.h create mode 100755 mDNSMacOSX/dnssd_clientstub.c create mode 100644 mDNSMacOSX/dnssd_ipc.c create mode 100644 mDNSMacOSX/dnssd_ipc.h create mode 100644 mDNSMacOSX/mDNSMacOSX.h rename {mDNSResponder.pbproj => mDNSMacOSX/mDNSResponder.pbproj}/project.pbxproj (51%) create mode 100644 mDNSMacOSX/uds_daemon.c create mode 100755 mDNSPosix/Client.c create mode 100644 mDNSPosix/ExampleClientApp.c create mode 100644 mDNSPosix/ExampleClientApp.h create mode 100644 mDNSPosix/Identify.c create mode 100755 mDNSPosix/Makefile create mode 100644 mDNSPosix/NetMonitor.c create mode 100644 mDNSPosix/ProxyResponder.c create mode 100755 mDNSPosix/ReadMe.txt create mode 100755 mDNSPosix/Responder.c create mode 100755 mDNSPosix/Services.txt create mode 100755 mDNSPosix/mDNSPosix.c create mode 100755 mDNSPosix/mDNSPosix.h create mode 100755 mDNSPosix/mDNSUNP.c create mode 100755 mDNSPosix/mDNSUNP.h create mode 100644 mDNSVxWorks/README.txt create mode 100644 mDNSVxWorks/mDNSVxWorks.c create mode 100644 mDNSVxWorks/mDNSVxWorks.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Application.sln create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Application.vcproj create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc2 create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Resource.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.cpp create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.cpp create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.cpp create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.cpp create mode 100644 mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcc create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcp create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcw create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.ico create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc2 create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/newres.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/resource.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.cpp create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.cpp create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.h create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.cpp create mode 100644 mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.h create mode 100644 mDNSWindows/Applications/RendezvousTest/Tool.c create mode 100644 mDNSWindows/Applications/RendezvousTest/ToolPrefixWindows.h create mode 100644 mDNSWindows/Applications/RendezvousTest/ToolPrefixWindowsDebug.h create mode 100644 mDNSWindows/Applications/RendezvousTest/ToolWin32.mcp create mode 100644 mDNSWindows/Applications/RendezvousTest/ToolWin32.sln create mode 100644 mDNSWindows/Applications/RendezvousTest/ToolWin32.vcproj create mode 100644 mDNSWindows/DNSServices/DNSServiceDiscovery.c create mode 100644 mDNSWindows/DNSServices/DNSServiceDiscovery.h create mode 100755 mDNSWindows/DNSServices/DNSServices.c create mode 100755 mDNSWindows/DNSServices/DNSServices.h create mode 100644 mDNSWindows/README.txt create mode 100755 mDNSWindows/mDNSWin32.c create mode 100755 mDNSWindows/mDNSWin32.h diff --git a/APPLE_LICENSE b/APPLE_LICENSE new file mode 100644 index 0000000..fe81a60 --- /dev/null +++ b/APPLE_LICENSE @@ -0,0 +1,367 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. +By downloading or using this software, you are agreeing to be bound by +the terms of this License. If you do not or cannot agree to the terms +of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") makes publicly available and +which contains a notice placed by Apple identifying such program or +work as "Original Code" and stating that it is subject to the terms of +this Apple Public Source License version 2.0 ("License"). As used in +this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or +contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or +otherwise make Covered Code available, directly or indirectly, to +anyone other than You; and/or (b) to use Covered Code, alone or as +part of a Larger Work, in any way to provide a service, including but +not limited to delivery of content, through electronic communication +with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous +Modifications, and/or any respective portions thereof. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non-exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, +internally distribute within Your organization, and Externally Deploy +verbatim, unmodified copies of the Original Code, for commercial or +non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as +they appear in the Original Code, and keep intact all notices in the +Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source +Code of Covered Code and documentation You distribute or Externally +Deploy, and You may not offer or impose any terms on such Source Code +that alter or restrict this License or the recipients' rights +hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial +or non-commercial purposes, provided that in each instance You also +meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to +the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the +notice in Exhibit A in each file of the Source Code of all Your +Modifications, and cause the modified files to carry prominent notices +stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make +Source Code of all Your Externally Deployed Modifications either +available to those to whom You have Externally Deployed Your +Modifications, or publicly available. Source Code of Your Externally +Deployed Modifications must be released under the terms set forth in +this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve +(12) months from the date of initial External Deployment, whichever is +longer. You should preferably distribute the Source Code of Your +Externally Deployed Modifications electronically (e.g. download from a +web site). + +2.3 Distribution of Executable Versions. In addition, if You +Externally Deploy Covered Code (Original Code and/or Modifications) in +object code, executable form only, You must include a prominent +notice, in the code itself as well as in related documentation, +stating that Source Code of the Covered Code is available under the +terms of this License with information on how and where to obtain such +Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that +although Apple and each Contributor grants the licenses to their +respective portions of the Covered Code set forth herein, no +assurances are provided by Apple or any Contributor that the Covered +Code does not infringe the patent or other intellectual property +rights of any other entity. Apple and each Contributor disclaim any +liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a +condition to exercising the rights and licenses granted hereunder, You +hereby assume sole responsibility to secure any other intellectual +property rights needed, if any. For example, if a third party patent +license is required to allow You to distribute the Covered Code, it is +Your responsibility to acquire that license before distributing the +Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License, You hereby grant to any +person or entity receiving or distributing Covered Code under this +License a non-exclusive, royalty-free, perpetual, irrevocable license, +under Your Applicable Patent Rights and other intellectual property +rights (other than patent) owned or controlled by You, to use, +reproduce, display, perform, modify, sublicense, distribute and +Externally Deploy Your Modifications of the same scope and extent as +Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such instance, +You must make sure the requirements of this License are fulfilled for +the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require additional +patent licenses from Apple which Apple may grant in its sole +discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other +rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered Code. +However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple or any Contributor. You +must obtain the recipient's agreement that any such Additional Terms +are offered by You alone, and You hereby agree to indemnify, defend +and hold Apple and every Contributor harmless for any liability +incurred by or claims asserted against Apple or such Contributor by +reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be given +a distinguishing version number. Once Original Code has been published +under a particular version of this License, You may continue to use it +under the terms of that version. You may also choose to use such +Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered +Code may contain errors that could cause failures or loss of data, and +may be incomplete or contain inaccuracies. You expressly acknowledge +and agree that use of the Covered Code, or any portion thereof, is at +Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND +WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND +APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE +PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF +MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR +PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST +INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE +FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, +THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO +ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. +You acknowledge that the Covered Code is not intended for use in the +operation of nuclear facilities, aircraft navigation, communication +systems, or air traffic control machines in which case the failure of +the Covered Code could lead to death, personal injury, or severe +physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING +TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR +ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, +TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF +APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY +REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF +INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY +TO YOU. In no event shall Apple's total liability to You for all +damages (other than as may be required by applicable law) under this +License exceed the amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", +"QuickTime", "QuickTime Streaming Server" or any other trademarks, +service marks, logos or trade names belonging to Apple (collectively +"Apple Marks") or to any trademark, service mark, logo or trade name +belonging to any Contributor. You agree not to use any Apple Marks in +or as part of the name of products derived from the Original Code or +to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times +with Apple's third party trademark usage guidelines which are posted +at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, +each Contributor retains all rights, title and interest in and to any +Modifications made by such Contributor. Apple retains all rights, +title and interest in and to the Original Code and any Modifications +made by or on behalf of Apple ("Apple Modifications"), and such Apple +Modifications will not be automatically subject to this License. Apple +may, at its sole discretion, choose to license such Apple +Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section +13.5(b); or + +(c) automatically without notice from Apple if You, at any time during +the term of this License, commence an action for patent infringement +against Apple; provided that Apple did not first commence +an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately +stop any further use, reproduction, modification, sublicensing and +distribution of the Covered Code. All sublicenses to the Covered Code +which have been properly granted prior to termination shall survive +any termination of this License. Provisions which, by their nature, +should remain in effect beyond the termination of this License shall +survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, +12.2 and 13. No party will be liable to any other for compensation, +indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of +any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in +the Covered Code include only those rights customarily provided to the +public as defined in this License. This customary commercial license +in technical data and software is provided in accordance with FAR +12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between or among You, Apple or any Contributor, and +You will not represent to the contrary, whether expressly, by +implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to +enforce any provision of this License will not be deemed a waiver of +future enforcement of that or any other provision. Any law or +regulation which provides that the language of a contract shall be +construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +Reserved. + +This file contains Original Code and/or Modifications of Original Code +as defined in and that are subject to the Apple Public Source License +Version 2.0 (the 'License'). You may not use this file except in +compliance with the License. Please obtain a copy of the License at +http://www.opensource.apple.com/apsl/ and read it before using this +file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +Please see the License for the specific language governing rights and +limitations under the License." diff --git a/CFSocket.c b/CFSocket.c deleted file mode 100644 index 292d586..0000000 --- a/CFSocket.c +++ /dev/null @@ -1,879 +0,0 @@ -/* - * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in - * and are subject to the Apple Public Source License Version 1.1 - * (the "License"). You may not use this file except in compliance - * with the License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -// *************************************************************************** -// mDNS-CFSocket.c: -// Supporting routines to run mDNS on a CFRunLoop platform -// *************************************************************************** - -// Open Transport 2.7.x on Mac OS 9 used to send Multicast DNS queries to UDP port 53, -// before the Multicast DNS port was changed to 5353. For this reason, the mDNSResponder -// in earlier versions of Mac OS X 10.2 Jaguar used to set mDNS_AllowPort53 to 1 to allow -// it to also listen and answer queries on UDP port 53. Now that Transport 2.8 (included in -// the Classic subsystem of Mac OS X 10.2 Jaguar) has been corrected to issue Multicast DNS -// queries on UDP port 5353, this backwards-compatibility legacy support is no longer needed. -#define mDNS_AllowPort53 1 - -// Normally mDNSResponder is advertising local services on all active interfaces. -// However, should you wish to build a query-only mDNS client, setting mDNS_AdvertiseLocalAddresses -// to zero will cause CFSocket.c to not set the Advertise flag in its mDNS_RegisterInterface calls. -int mDNS_AdvertiseLocalAddresses = 1; - -void (*NotifyClientNetworkChanged)(void); - -#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above -#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below -#include "mDNSPlatformEnvironment.h" // Defines the specific types needed to run mDNS on this platform -#include "mDNSvsprintf.h" // Used to implement debugf_(); - -#include -#include // For va_list support -#include -#include -#include -#include -#include - -// Code contributed by Dave Heller: -// Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will -// work on Mac OS X 10.1, which does not have the getifaddrs call. -#define RUN_ON_PUMA_WITHOUT_IFADDRS 0 - -#if RUN_ON_PUMA_WITHOUT_IFADDRS - -#include -#include -#define ifaddrs ifa_info -#ifndef ifa_broadaddr -#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ -#endif -#include - -#else - -#include - -#endif - -#include -#include - -extern void LogErrorMessage(const char *format, ...); - -// *************************************************************************** -// Structures - -typedef struct NetworkInterfaceInfo2_struct NetworkInterfaceInfo2; -struct NetworkInterfaceInfo2_struct - { - NetworkInterfaceInfo ifinfo; - mDNS *m; - char *ifa_name; - NetworkInterfaceInfo2 *alias; - int socket; - CFSocketRef cfsocket; -#if mDNS_AllowPort53 - int socket53; - CFSocketRef cfsocket53; -#endif - }; - -// *************************************************************************** -// Functions - -mDNSexport void debugf_(const char *format, ...) - { - unsigned char buffer[512]; - va_list ptr; - va_start(ptr,format); - buffer[mDNS_vsprintf((char *)buffer, format, ptr)] = 0; - va_end(ptr); - fprintf(stderr, "%s\n", buffer); - fflush(stderr); - } - -mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - mDNSIPAddr src, mDNSIPPort srcport, mDNSIPAddr dst, mDNSIPPort dstport) - { - NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)(m->HostInterfaces); - struct sockaddr_in to; - to.sin_family = AF_INET; - to.sin_port = dstport.NotAnInteger; - to.sin_addr.s_addr = dst. NotAnInteger; - - if (src.NotAnInteger == 0) debugf("mDNSPlatformSendUDP ERROR! Cannot send from zero source address"); - - while (info) - { - if (info->ifinfo.ip.NotAnInteger == src.NotAnInteger) - { - int s, err; - if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) s = info->socket; -#if mDNS_AllowPort53 - else if (srcport.NotAnInteger == UnicastDNSPort.NotAnInteger ) s = info->socket53; -#endif - else { debugf("Source port %d not allowed", (mDNSu16)srcport.b[0]<<8 | srcport.b[1]); return(-1); } - err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, sizeof(to)); - if (err < 0) { perror("mDNSPlatformSendUDP sendto"); return(err); } - } - info = (NetworkInterfaceInfo2 *)(info->ifinfo.next); - } - - return(mStatus_NoError); - } - -static ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, - struct sockaddr *const from, size_t *const fromlen, struct in_addr *dstaddr, char ifname[128]) - { - static int numLogMessages = 0; - struct iovec databuffers = { (char *)buffer, max }; - struct msghdr msg; - ssize_t n; - struct cmsghdr *cmPtr; - char ancillary[1024]; - - // Set up the message - msg.msg_name = (caddr_t)from; - msg.msg_namelen = *fromlen; - msg.msg_iov = &databuffers; - msg.msg_iovlen = 1; - msg.msg_control = (caddr_t)&ancillary; - msg.msg_controllen = sizeof(ancillary); - msg.msg_flags = 0; - - // Receive the data - n = recvmsg(s, &msg, 0); - if (n<0) - { - if (numLogMessages++ < 100) LogErrorMessage("CFSocket.c: recvmsg(%d) returned error %d errno %d", s, n, errno); - return(-1); - } - if (msg.msg_controllen < sizeof(struct cmsghdr)) - { - if (numLogMessages++ < 100) LogErrorMessage("CFSocket.c: recvmsg(%d) msg.msg_controllen %d < sizeof(struct cmsghdr) %d", - s, msg.msg_controllen, sizeof(struct cmsghdr)); - return(-1); - } - if (msg.msg_flags & MSG_CTRUNC) - { - if (numLogMessages++ < 100) LogErrorMessage("CFSocket.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); - return(-1); - } - - *fromlen = msg.msg_namelen; - - // Parse each option out of the ancillary data. - for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) - { - // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) - *dstaddr = *(struct in_addr *)CMSG_DATA(cmPtr); - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) - { - struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); - if (sdl->sdl_nlen < sizeof(ifname)) - { - mDNSPlatformMemCopy(sdl->sdl_data, ifname, sdl->sdl_nlen); - ifname[sdl->sdl_nlen] = 0; - // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); - } - } - } - - return(n); - } - -mDNSlocal void myCFSocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *context) - { - mDNSIPAddr senderaddr, destaddr; - mDNSIPPort senderport; - NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)context; - mDNS *const m = info->m; - DNSMessage packet; - struct in_addr to; - struct sockaddr_in from; - size_t fromlen = sizeof(from); - char packetifname[128] = ""; - int err; - int s1 = -1; - - (void)address; // Parameter not used - (void)data; // Parameter not used - - if (type != kCFSocketReadCallBack) LogErrorMessage("CFSocketCallBack: CallBackType %d is not kCFSocketReadCallBack", type); -#if mDNS_AllowPort53 - if (s == info->cfsocket53) - s1 = info->socket53; - else -#endif - if (s == info->cfsocket) - s1 = info->socket; - - err = myrecvfrom(s1, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &to, packetifname); - - if (err < 0 || s1 < 0 || s1 != CFSocketGetNative(s)) - { - LogErrorMessage("CFSocketCallBack: s1 %d native socket %d", s1, CFSocketGetNative(s)); - LogErrorMessage("CFSocketCallBack: cfs %X, cfsocket53 %X, cfsocket %X", s, info->cfsocket53, info->cfsocket); - LogErrorMessage("CFSocketCallBack: skt53 %X, sktv4 %X", info->socket53, info->socket); - LogErrorMessage("CFSocketCallBack recvfrom(%d) error %d errno %d", s1, err, errno); - return; - } - - senderaddr.NotAnInteger = from.sin_addr.s_addr; - senderport.NotAnInteger = from.sin_port; - destaddr.NotAnInteger = to.s_addr; - - // Even though we indicated a specific interface in the IP_ADD_MEMBERSHIP call, a weirdness of the - // sockets API means that even though this socket has only officially joined the multicast group - // on one specific interface, the kernel will still deliver multicast packets to it no matter which - // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug. - // To work around this weirdness, we use the IP_RECVIF option to find the name of the interface - // on which the packet arrived, and ignore the packet if it really arrived on some other interface. - if (strcmp(info->ifa_name, packetifname)) - { - verbosedebugf("myCFSocketCallBack got a packet from %.4a to %.4a on interface %.4a/%s (Ignored -- really arrived on interface %s)", - &senderaddr, &destaddr, &info->ifinfo.ip, info->ifa_name, packetifname); - return; - } - else - verbosedebugf("myCFSocketCallBack got a packet from %.4a to %.4a on interface %.4a/%s", - &senderaddr, &destaddr, &info->ifinfo.ip, info->ifa_name); - - if (err < sizeof(DNSMessageHeader)) { debugf("myCFSocketCallBack packet length (%d) too short", err); return; } - -#if mDNS_AllowPort53 - if (s == info->cfsocket53) - mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, senderaddr, senderport, destaddr, UnicastDNSPort, info->ifinfo.ip); - else -#endif - mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, senderaddr, senderport, destaddr, MulticastDNSPort, info->ifinfo.ip); - } - -mDNSlocal void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info) - { - (void)timer; // Parameter not used - mDNSCoreTask((mDNS *const)info); - } - -// This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel -mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) - { - CFStringEncoding encoding = kCFStringEncodingUTF8; - CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); - if (cfs) - { - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); - } - } - -// This gets the text of the field currently labelled "Rendezvous Name" in the Sharing Prefs Control Panel -mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) - { - CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); - if (cfs) - { - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); - } - } - -mDNSlocal mStatus SetupSocket(struct sockaddr_in *ifa_addr, mDNSIPPort port, int *s, CFSocketRef *c, CFSocketContext *context) - { - mStatus err; - const int on = 1; - const int twofivefive = 255; - struct ip_mreq imr; - struct sockaddr_in listening_sockaddr; - CFRunLoopSourceRef rls; - - if (*s > 0) { LogErrorMessage("SetupSocket ERROR: socket %d is already set", *s); return(-1); } - if (*c) { LogErrorMessage("SetupSocket ERROR: CFSocketRef %X is already set", *c); return(-1); } - - // Open the socket... - *s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - *c = NULL; - if (*s < 0) { perror("socket"); return(*s); } - - // ... with a shared UDP port - err = setsockopt(*s, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); - if (err < 0) { perror("setsockopt - SO_REUSEPORT"); return(err); } - - // We want to receive destination addresses - err = setsockopt(*s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); - if (err < 0) { perror("setsockopt - IP_RECVDSTADDR"); return(err); } - - // We want to receive interface identifiers - err = setsockopt(*s, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); - if (err < 0) { perror("setsockopt - IP_RECVIF"); return(err); } - - // Add multicast group membership on this interface - imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; - imr.imr_interface = ifa_addr->sin_addr; - err = setsockopt(*s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(struct ip_mreq)); - if (err < 0) { perror("setsockopt - IP_ADD_MEMBERSHIP"); return(err); } - - // Specify outgoing interface too - err = setsockopt(*s, IPPROTO_IP, IP_MULTICAST_IF, &ifa_addr->sin_addr, sizeof(ifa_addr->sin_addr)); - if (err < 0) { perror("setsockopt - IP_MULTICAST_IF"); return(err); } - - // Send unicast packets with TTL 255 - err = setsockopt(*s, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); - if (err < 0) { perror("setsockopt - IP_TTL"); return(err); } - - // And multicast packets with TTL 255 too - err = setsockopt(*s, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); - if (err < 0) { perror("setsockopt - IP_MULTICAST_TTL"); return(err); } - - // And start listening for packets - listening_sockaddr.sin_family = AF_INET; - listening_sockaddr.sin_port = port.NotAnInteger; - listening_sockaddr.sin_addr.s_addr = 0; // Want to receive multicasts AND unicasts on this socket - err = bind(*s, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); - if (err) - { - if (port.NotAnInteger == UnicastDNSPort.NotAnInteger) err = 0; - else perror("bind"); - return(err); - } - - *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, context); - rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFRelease(rls); - - return(err); - } - -#if 0 -mDNSlocal NetworkInterfaceInfo2 *SearchForInterfaceByAddr(mDNS *const m, mDNSIPAddr ip) - { - NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2*)(m->HostInterfaces); - while (info) - { - if (info->ifinfo.ip.NotAnInteger == ip.NotAnInteger) return(info); - info = (NetworkInterfaceInfo2 *)(info->ifinfo.next); - } - return(NULL); - } -#endif - -mDNSlocal NetworkInterfaceInfo2 *SearchForInterfaceByName(mDNS *const m, char *ifname) - { - NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2*)(m->HostInterfaces); - while (info) - { - if (!strcmp(info->ifa_name, ifname)) return(info); - info = (NetworkInterfaceInfo2 *)(info->ifinfo.next); - } - return(NULL); - } - -#if RUN_ON_PUMA_WITHOUT_IFADDRS - -/* Our own header for the programs that need interface configuration info. - Include this file, instead of "unp.h". */ - -#define IFA_NAME 16 /* same as IFNAMSIZ in */ -#define IFA_HADDR 8 /* allow for 64-bit EUI-64 in future */ - -struct ifa_info { - char ifa_name[IFA_NAME]; /* interface name, null terminated */ - u_char ifa_haddr[IFA_HADDR]; /* hardware address */ - u_short ifa_hlen; /* #bytes in hardware address: 0, 6, 8 */ - short ifa_flags; /* IFF_xxx constants from */ - short ifa_myflags; /* our own IFI_xxx flags */ - struct sockaddr *ifa_addr; /* primary address */ - struct sockaddr *ifa_brdaddr;/* broadcast address */ - struct sockaddr *ifa_dstaddr;/* destination address */ - struct ifa_info *ifa_next; /* next of these structures */ -}; - -#define IFI_ALIAS 1 /* ifa_addr is an alias */ - - /* function prototypes */ -struct ifa_info *get_ifa_info(int, int); -struct ifa_info *Get_ifa_info(int, int); -void free_ifa_info(struct ifa_info *); - -#define HAVE_SOCKADDR_SA_LEN 1 - -struct ifa_info * -get_ifa_info(int family, int doaliases) -{ - struct ifa_info *ifi, *ifihead, **ifipnext; - int sockfd, len, lastlen, flags, myflags; - char *ptr, *buf, lastname[IFNAMSIZ], *cptr; - struct ifconf ifc; - struct ifreq *ifr, ifrcopy; - struct sockaddr_in *sinptr; - - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - - lastlen = 0; - len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ - for ( ; ; ) { - buf = (char *) malloc(len); - ifc.ifc_len = len; - ifc.ifc_buf = buf; - if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { - if (errno != EINVAL || lastlen != 0) - debugf("ioctl error"); - } else { - if (ifc.ifc_len == lastlen) - break; /* success, len has not changed */ - lastlen = ifc.ifc_len; - } - len += 10 * sizeof(struct ifreq); /* increment */ - free(buf); - } - ifihead = NULL; - ifipnext = &ifihead; - lastname[0] = 0; -/* end get_ifa_info1 */ - -/* include get_ifa_info2 */ - for (ptr = buf; ptr < buf + ifc.ifc_len; ) { - ifr = (struct ifreq *) ptr; - -#ifdef HAVE_SOCKADDR_SA_LEN - len = MAX(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); -#else - switch (ifr->ifr_addr.sa_family) { -#ifdef IPV6 - case AF_INET6: - len = sizeof(struct sockaddr_in6); - break; -#endif - case AF_INET: - default: - len = sizeof(struct sockaddr); - break; - } -#endif /* HAVE_SOCKADDR_SA_LEN */ - ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ - - if (ifr->ifr_addr.sa_family != family) - continue; /* ignore if not desired address family */ - - myflags = 0; - if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) - *cptr = 0; /* replace colon will null */ - if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { - if (doaliases == 0) - continue; /* already processed this interface */ - myflags = IFI_ALIAS; - } - memcpy(lastname, ifr->ifr_name, IFNAMSIZ); - - ifrcopy = *ifr; - ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); - flags = ifrcopy.ifr_flags; - if ((flags & IFF_UP) == 0) - continue; /* ignore if interface not up */ - - ifi = (struct ifa_info *) calloc(1, sizeof(struct ifa_info)); - *ifipnext = ifi; /* prev points to this new one */ - ifipnext = &ifi->ifa_next; /* pointer to next one goes here */ - - ifi->ifa_flags = flags; /* IFF_xxx values */ - ifi->ifa_myflags = myflags; /* IFI_xxx values */ - memcpy(ifi->ifa_name, ifr->ifr_name, IFA_NAME); - ifi->ifa_name[IFA_NAME-1] = '\0'; -/* end get_ifa_info2 */ -/* include get_ifa_info3 */ - switch (ifr->ifr_addr.sa_family) { - case AF_INET: - sinptr = (struct sockaddr_in *) &ifr->ifr_addr; - if (ifi->ifa_addr == NULL) { - ifi->ifa_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_addr, sinptr, sizeof(struct sockaddr_in)); - -#ifdef SIOCGIFBRDADDR - if (flags & IFF_BROADCAST) { - ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; - ifi->ifa_brdaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_brdaddr, sinptr, sizeof(struct sockaddr_in)); - } -#endif - -#ifdef SIOCGIFDSTADDR - if (flags & IFF_POINTOPOINT) { - ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; - ifi->ifa_dstaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_dstaddr, sinptr, sizeof(struct sockaddr_in)); - } -#endif - } - break; - - default: - break; - } - } - free(buf); - return(ifihead); /* pointer to first structure in linked list */ -} -/* end get_ifa_info3 */ - -/* include free_ifa_info */ -mDNSlocal void freeifaddrs(struct ifa_info *ifihead) -{ - struct ifa_info *ifi, *ifinext; - - for (ifi = ifihead; ifi != NULL; ifi = ifinext) { - if (ifi->ifa_addr != NULL) - free(ifi->ifa_addr); - if (ifi->ifa_brdaddr != NULL) - free(ifi->ifa_brdaddr); - if (ifi->ifa_dstaddr != NULL) - free(ifi->ifa_dstaddr); - ifinext = ifi->ifa_next; /* can't fetch ifa_next after free() */ - free(ifi); /* the ifa_info{} itself */ - } -} -/* end free_ifa_info */ - -struct ifa_info * -Get_ifa_info(int family, int doaliases) -{ - struct ifa_info *ifi; - - if ( (ifi = get_ifa_info(family, doaliases)) == NULL) - debugf("get_ifa_info error"); - return(ifi); -} - -mDNSlocal int getifaddrs(struct ifa_info **ifalist) - { - *ifalist = get_ifa_info(PF_INET, false); - if( ifalist == nil ) - return -1; - else - return(0); - } - -#endif - -mDNSlocal mStatus SetupInterface(mDNS *const m, NetworkInterfaceInfo2 *info, struct ifaddrs *ifa) - { - mStatus err = 0; - struct sockaddr_in *ifa_addr = (struct sockaddr_in *)ifa->ifa_addr; - CFSocketContext myCFSocketContext = { 0, info, NULL, NULL, NULL }; - - info->ifinfo.ip.NotAnInteger = ifa_addr->sin_addr.s_addr; - info->ifinfo.Advertise = mDNS_AdvertiseLocalAddresses; - info->m = m; - info->ifa_name = (char *)mallocL("NetworkInterfaceInfo2 name", strlen(ifa->ifa_name) + 1); - if (!info->ifa_name) return(-1); - strcpy(info->ifa_name, ifa->ifa_name); - info->alias = SearchForInterfaceByName(m, ifa->ifa_name); - info->socket = 0; - info->cfsocket = 0; -#if mDNS_AllowPort53 - info->socket53 = 0; - info->cfsocket53 = 0; -#endif - - mDNS_RegisterInterface(m, &info->ifinfo); - - if (info->alias) - debugf("SetupInterface: %s Flags %04X %.4a is an alias of %.4a", - ifa->ifa_name, ifa->ifa_flags, &info->ifinfo.ip, &info->alias->ifinfo.ip); - -#if mDNS_AllowPort53 - err = SetupSocket(ifa_addr, UnicastDNSPort, &info->socket53, &info->cfsocket53, &myCFSocketContext); -#endif - if (!err) - err = SetupSocket(ifa_addr, MulticastDNSPort, &info->socket, &info->cfsocket, &myCFSocketContext); - - debugf("SetupInterface: %s Flags %04X %.4a Registered socket53 %d socket5353 %d", - ifa->ifa_name, ifa->ifa_flags, &info->ifinfo.ip, info->socket53, info->socket); - - return(err); - } - -mDNSlocal void ClearInterfaceList(mDNS *const m) - { - while (m->HostInterfaces) - { - NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2*)(m->HostInterfaces); - mDNS_DeregisterInterface(m, &info->ifinfo); - if (info->ifa_name ) freeL("NetworkInterfaceInfo2 name", info->ifa_name); - if (info->socket > 0) shutdown(info->socket, 2); - if (info->cfsocket) { CFSocketInvalidate(info->cfsocket); CFRelease(info->cfsocket); } -#if mDNS_AllowPort53 - if (info->socket53 > 0) shutdown(info->socket53, 2); - if (info->cfsocket53) { CFSocketInvalidate(info->cfsocket53); CFRelease(info->cfsocket53); } -#endif - freeL("NetworkInterfaceInfo2", info); - } - } - -mDNSlocal mStatus SetupInterfaceList(mDNS *const m) - { - struct ifaddrs *ifalist; - int err = getifaddrs(&ifalist); - struct ifaddrs *ifa = ifalist; - struct ifaddrs *theLoopback = NULL; - if (err) return(err); - - // Set up the nice label - m->nicelabel.c[0] = 0; - GetUserSpecifiedFriendlyComputerName(&m->nicelabel); - if (m->nicelabel.c[0] == 0) ConvertCStringToDomainLabel("Macintosh", &m->nicelabel); - - // Set up the RFC 1034-compliant label - m->hostlabel.c[0] = 0; - GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); - if (m->hostlabel.c[0] == 0) ConvertCStringToDomainLabel("Macintosh", &m->hostlabel); - - mDNS_GenerateFQDN(m); - - while (ifa) - { -#if 0 - if (ifa->ifa_addr->sa_family != AF_INET) - debugf("SetupInterface: %s Flags %04X Family %d not AF_INET", - ifa->ifa_name, ifa->ifa_flags, ifa->ifa_addr->sa_family); - if (!(ifa->ifa_flags & IFF_UP)) - debugf("SetupInterface: %s Flags %04X Interface not IFF_UP", ifa->ifa_name, ifa->ifa_flags); - if (ifa->ifa_flags & IFF_LOOPBACK) - debugf("SetupInterface: %s Flags %04X Interface IFF_LOOPBACK", ifa->ifa_name, ifa->ifa_flags); - if (ifa->ifa_flags & IFF_POINTOPOINT) - debugf("SetupInterface: %s Flags %04X Interface IFF_POINTOPOINT", ifa->ifa_name, ifa->ifa_flags); -#endif - if (ifa->ifa_addr->sa_family == AF_INET && (ifa->ifa_flags & IFF_UP) && - !(ifa->ifa_flags & IFF_POINTOPOINT)) - { - if (ifa->ifa_flags & IFF_LOOPBACK) - theLoopback = ifa; - else - { - NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)mallocL("NetworkInterfaceInfo2", sizeof(*info)); - if (!info) debugf("SetupInterfaceList: Out of Memory!"); - else SetupInterface(m, info, ifa); - } - } - ifa = ifa->ifa_next; - } - - if (!m->HostInterfaces && theLoopback) - { - NetworkInterfaceInfo2 *info = (NetworkInterfaceInfo2 *)mallocL("NetworkInterfaceInfo2", sizeof(*info)); - if (!info) debugf("SetupInterfaceList: (theLoopback) Out of Memory!"); - else SetupInterface(m, info, theLoopback); - } - - freeifaddrs(ifalist); - return(err); - } - -mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) - { - mDNS *const m = (mDNS *const)context; - debugf("*** Network Configuration Change ***"); - (void)store; // Parameter not used - (void)changedKeys; // Parameter not used - - ClearInterfaceList(m); - SetupInterfaceList(m); - if (NotifyClientNetworkChanged) NotifyClientNetworkChanged(); - mDNSCoreSleep(m, false); - } - -mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) - { - mStatus err = -1; - SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder"), NetworkChanged, &context); - CFStringRef key1 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); - CFStringRef key2 = SCDynamicStoreKeyCreateComputerName(NULL); - CFStringRef key3 = SCDynamicStoreKeyCreateHostNames(NULL); - CFStringRef pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); - CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - - if (!store) { fprintf(stderr, "SCDynamicStoreCreate failed: %s\n", SCErrorString(SCError())); goto error; } - if (!key1 || !key2 || !key3 || !keys || !pattern || !patterns) goto error; - - CFArrayAppendValue(keys, key1); - CFArrayAppendValue(keys, key2); - CFArrayAppendValue(keys, key3); - CFArrayAppendValue(patterns, pattern); - if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) - { fprintf(stderr, "SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError())); goto error; } - - m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); - if (!m->p->StoreRLS) { fprintf(stderr, "SCDynamicStoreCreateRunLoopSource failed: %s\n", SCErrorString(SCError())); goto error; } - - CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); - m->p->Store = store; - err = 0; - goto exit; - -error: - if (store) CFRelease(store); - -exit: - if (key1) CFRelease(key1); - if (key2) CFRelease(key2); - if (key3) CFRelease(key3); - if (pattern) CFRelease(pattern); - if (keys) CFRelease(keys); - if (patterns) CFRelease(patterns); - - return(err); - } - -mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) - { - mDNS *const m = (mDNS *const)refcon; - (void)service; // Parameter not used - switch(messageType) - { - case kIOMessageCanSystemPowerOff: debugf("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 - case kIOMessageSystemWillPowerOff: debugf("PowerChanged kIOMessageSystemWillPowerOff"); mDNSCoreSleep(m, true); break; // E0000250 - case kIOMessageSystemWillNotPowerOff: debugf("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 - case kIOMessageCanSystemSleep: debugf("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 - case kIOMessageSystemWillSleep: debugf("PowerChanged kIOMessageSystemWillSleep"); mDNSCoreSleep(m, true); break; // E0000280 - case kIOMessageSystemWillNotSleep: debugf("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 - case kIOMessageSystemHasPoweredOn: debugf("PowerChanged kIOMessageSystemHasPoweredOn"); mDNSCoreSleep(m, false); break; // E0000300 - default: debugf("PowerChanged unknown message %X", messageType); break; - } - IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); - } - -mDNSlocal mStatus WatchForPowerChanges(mDNS *const m) - { - IONotificationPortRef thePortRef; - m->p->PowerConnection = IORegisterForSystemPower(m, &thePortRef, PowerChanged, &m->p->PowerNotifier); - if (m->p->PowerConnection) - { - m->p->PowerRLS = IONotificationPortGetRunLoopSource(thePortRef); - CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); - return(mStatus_NoError); - } - return(-1); - } - -mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) - { - mStatus err; - - CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, m, NULL, NULL, NULL }; - - // Note: Every CFRunLoopTimer has to be created with an initial fire time, and a repeat interval, or it becomes - // a one-shot timer and you can't use CFRunLoopTimerSetNextFireDate(timer, when) to schedule subsequent firings. - // Here we create it with an initial fire time ten seconds from now, and a repeat interval of ten seconds, - // knowing that we'll reschedule it using CFRunLoopTimerSetNextFireDate(timer, when) long before that happens. - m->p->CFTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 10.0, 10.0, 0, 1, - myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext); - CFRunLoopAddTimer(CFRunLoopGetCurrent(), m->p->CFTimer, kCFRunLoopDefaultMode); - - SetupInterfaceList(m); - - err = WatchForNetworkChanges(m); - if (err) return(err); - - err = WatchForPowerChanges(m); - return(err); - } - -mDNSexport mStatus mDNSPlatformInit(mDNS *const m) - { - mStatus result = mDNSPlatformInit_setup(m); - // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already - // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately - if (result == mStatus_NoError) mDNSCoreInitComplete(m, mStatus_NoError); - return(result); - } - -mDNSexport void mDNSPlatformClose(mDNS *const m) - { - if (m->p->PowerConnection) - { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); - CFRunLoopSourceInvalidate(m->p->PowerRLS); - CFRelease(m->p->PowerRLS); - IODeregisterForSystemPower(&m->p->PowerNotifier); - m->p->PowerConnection = NULL; - m->p->PowerNotifier = NULL; - m->p->PowerRLS = NULL; - } - - if (m->p->Store) - { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); - CFRunLoopSourceInvalidate(m->p->StoreRLS); - CFRelease(m->p->StoreRLS); - CFRelease(m->p->Store); - m->p->Store = NULL; - m->p->StoreRLS = NULL; - } - - ClearInterfaceList(m); - - if (m->p->CFTimer) - { - CFRunLoopTimerInvalidate(m->p->CFTimer); - CFRelease(m->p->CFTimer); - m->p->CFTimer = NULL; - } - } - -mDNSexport void mDNSPlatformScheduleTask(const mDNS *const m, SInt32 NextTaskTime) - { - if (m->p->CFTimer) - { - CFAbsoluteTime ticks = (CFAbsoluteTime)(NextTaskTime - mDNSPlatformTimeNow()); - CFAbsoluteTime interval = ticks / (CFAbsoluteTime)mDNSPlatformOneSecond; - CFRunLoopTimerSetNextFireDate(m->p->CFTimer, CFAbsoluteTimeGetCurrent() + interval); - } - } - -// Locking is a no-op here, because we're CFRunLoop-based, so we can never interrupt ourselves -mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; } -mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; } -mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { strcpy((char *)dst, (char *)src); } -mDNSexport UInt32 mDNSPlatformStrLen (const void *src) { return(strlen((char*)src)); } -mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, UInt32 len) { memcpy(dst, src, len); } -mDNSexport Boolean mDNSPlatformMemSame(const void *src, const void *dst, UInt32 len) { return(memcmp(dst, src, len) == 0); } -mDNSexport void mDNSPlatformMemZero( void *dst, UInt32 len) { bzero(dst, len); } - -mDNSexport SInt32 mDNSPlatformTimeNow() - { - struct timeval tp; - gettimeofday(&tp, NULL); - // tp.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time) - // tp.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999) - // We use the lower 22 bits of tp.tv_sec for the top 22 bits of our result - // and we multiply tp.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits. - // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second) - // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days). - return( (tp.tv_sec << 10) | (tp.tv_usec * 16 / 15625) ); - } - -mDNSexport SInt32 mDNSPlatformOneSecond = 1024; diff --git a/DNSServiceDiscoveryDefines.h b/DNSServiceDiscoveryDefines.h deleted file mode 100644 index 1c8e8ea..0000000 --- a/DNSServiceDiscoveryDefines.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include - diff --git a/DNSServiceDiscoveryReply.defs b/DNSServiceDiscoveryReply.defs deleted file mode 100644 index c08e86f..0000000 --- a/DNSServiceDiscoveryReply.defs +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - #import "/AppleInternal/Developer/Headers/DNSServiceDiscovery/DNSServiceDiscoveryReply.defs" \ No newline at end of file diff --git a/DNSServiceDiscoveryRequest.defs b/DNSServiceDiscoveryRequest.defs deleted file mode 100644 index fc17bd3..0000000 --- a/DNSServiceDiscoveryRequest.defs +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - #import "/AppleInternal/Developer/Headers/DNSServiceDiscovery/DNSServiceDiscoveryRequest.defs" \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..96298f8 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +# +# Top level makefile for Build & Integration. +# +# This file is used to facilitate checking the mDNSResponder project +# directly out of CVS and submitting to B&I at Apple. +# +# The various platform directories contain makefiles or projects +# specific to that platform. +# +# B&I builds must respect the following target: +# install: +# installsrc: +# installhdrs: +# clean: +# + +include /Developer/Makefiles/pb_makefiles/platform.make + +MVERS=58 + +install: + cd "$(SRCROOT)/mDNSMacOSX"; pbxbuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) + +installsrc: + ditto mDNSCore ${SRCROOT}/mDNSCore + ditto mDNSMacOSX ${SRCROOT}/mDNSMacOSX + ditto Makefile $(SRCROOT) + +installhdrs:: + cd "$(SRCROOT)/mDNSMacOSX"; pbxbuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) + +clean:: + echo clean diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..60abf83 --- /dev/null +++ b/README.txt @@ -0,0 +1,145 @@ +What is mDNSResponder? +---------------------- + +The mDNSResponder project is a component of Rendezvous, +Apple's ease-of-use IP networking initiative: + + +Apple's Rendezvous software derives from the ongoing standardization +work of the IETF Zero Configuration Networking Working Group: + + +The Zeroconf Working Group has identified three requirements for Zero +Configuration Networking: +1. An IP address (even when there is no DHCP server to assign one) +2. Name-to-address translation (even when there is no DNS server) +3. Discovery of Services on the network (again, without infrastucture) + +Requirement 1 is met by self-assigned link-local addresses, as +described in "Dynamic Configuration of IPv4 Link-Local Addresses" + + +Requirement 2 is met by sending DNS-like queries via Multicast (mDNS). + +Requirement 3 is met by DNS Service Dicsovery (DNS-SD). + +Self-assigned link-local address capability has been available since +1998, when it first appeared in Windows '98 and in Mac OS 8.5. +Implementations for other platforms also exist. + +The mDNSResponder project allows us to meet requirements 2 and 3. +It provides the ability for the user to identify hosts using names +instead of dotted-decimal IP addresses, even if the user doesn't have a +conventional DNS server set up. It also provides the ability for the +user to discover what services are being advertised on the network, +without having to know about them in advance, or configure the machines. + +The name "mDNS" was chosen because this protocol is designed to be, +as much as possible, similar to conventional DNS. The main difference is +that queries are sent via multicast to all local hosts, instead of via +unicast to a specific known server. Every host on the local link runs an +mDNSResponder which is constantly listening for those multicast queries, +and if the mDNSResponder receives a query for which it knows the answer, +then it responds. The mDNS protocol uses the same packet format as +unicast DNS, and the same name structure, and the same DNS record types. +The main difference is that queries are sent to a different UDP port +(5353 instead of 53) and they are sent via multicast to address +224.0.0.251. Another important difference is that all "mDNS" names +end in ".local." When a user types "yourcomputer.local." into their Web +browser, the presence of ".local." on the end of the name tells the host +OS that the name should be looked up using local multicast instead of by +sending that name to the worldwide DNS service for resolution. This +helps reduce potential user confusion about whether a particular name +is globally unique (e.g. "www.apple.com.") or whether that name has only +local significance (e.g. "yourcomputer.local."). + + +About the mDNSResponder Code +---------------------------- + +Because Apple benefits more from widespread adoption of Rendezvous than +it would benefit from keeping Rendezvous proprietary, Apple is making +this code open so that other developers can use it too. + +Because Apple recognises that networks are hetrogenous environments +where devices run many different kinds of OS, this code has been made +as portable as possible. + +A typical mDNS program contains three components: + + +------------------+ + | Application | + +------------------+ + | mDNS Core | + +------------------+ + | Platform Support | + +------------------+ + +The "mDNS Core" layer is absolutely identical for all applications and +all Operating Systems. + +The "Platform Support" layer provides the necessary supporting routines +that are specific to each platform -- what routine do you call to send +a UDP packet, what routine do you call to join multicast group, etc. + +The "Application" layer does whatever that particular application wants +to do. It calls routines provided by the "mDNS Core" layer to perform +the functions it needs -- + * advertise services, + * browse for named instances of a particular type of service + * resolve a named instance to a specific IP address and port number, + * etc. +The "mDNS Core" layer in turn calls through to the "Platform Support" +layer to send and receive the multicast UDP packets to do the actual work. + +Apple currently provides a "Platform Support" layer for OS X 10.2 +("Jaguar"), and a "Platform Support" layer for other Posix platforms +(OS X 10.1, Linux, etc.) Other support layers for platforms like Windows, +VxWorks, etc. are also planned. + +Note: Developers writing applications for OS X 10.2 ("Jaguar") do not +need to incorporate this code into their applications, since OS X 10.2 +provides a system service to handle this for them. If every application +developer were to link-in the mDNSResponder code into their application, +then we would end up with a situation like the picture below: + + +------------------+ +------------------+ +------------------+ + | Application 1 | | Application 2 | | Application 3 | + +------------------+ +------------------+ +------------------+ + | mDNS Core | | mDNS Core | | mDNS Core | + +------------------+ +------------------+ +------------------+ + | Platform Support | | Platform Support | | Platform Support | + +------------------+ +------------------+ +------------------+ + +This would not be very efficient. Each separate application would be +sending their own separate multicast UDP packets and maintaining their +own list of answers. Because of this, OS X 10.2 provides a common system +service which client software should access through the +"DNSServiceDiscovery.h" APIs. + +The situation on OS X 10.2 looks more like the picture below: + + ------------------- + / \ + +---------+ +------------------+ +---------+ \ +---------+ + | App 1 |<-->| daemon.c |<-->| App 2 | ->| App 3 | + +---------+ +------------------+ +---------+ +---------+ + | mDNS Core | + +------------------+ + | Platform Support | + +------------------+ + +Applications on OS X 10.2 make calls to the single mDNSResponder daemon +which implements the mDNS and DNS-SD protocols. + +Vendors of products such as printers, which are closed environments not +expecting to be running third-party application software, can reasonably +implement a single monolithic mDNSResponder to advertise all the +services of that device. Vendors of open systems which run third-party +application software should implement a system service such as the one +provided by the OS X mDNSResponder daemon, and application software on +that platform should, where possible, make use of that system service +instead of embedding their own mDNSResponder. + +See ReadMe.txt in the mDNSPosix directory for specific details of +building an mDNSResponder on a Posix Operating System. diff --git a/daemon.c b/daemon.c deleted file mode 100644 index 4432196..0000000 --- a/daemon.c +++ /dev/null @@ -1,1182 +0,0 @@ -/* - * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -/* - * Formatting notes: - * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion - * on C indentation can be found on the web, such as , - * but for the sake of brevity here I will say just this: Curly braces are not syntactially - * part of an "if" statement; they are the beginning and ending markers of a compound statement; - * therefore common sense dictates that if they are part of a compound statement then they - * should be indented to the same level as everything else in that compound statement. - * Indenting curly braces at the same level as the "if" implies that curly braces are - * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" - * thinking that variables x and y are both of type "char*" -- and anyone who doesn't - * understand why variable y is not of type "char*" just proves the point that poor code - * layout leads people to unfortunate misunderstandings about how the C language really works.) - */ - -#include -#include -#include -#include -#include - -#include "DNSServiceDiscoveryRequestServer.h" -#include "DNSServiceDiscoveryReply.h" - -#include "mDNSClientAPI.h" // Defines the interface to the client layer above -#include "mDNSPlatformFunctions.h" // For mDNSPlatformTimeNow() and mDNSPlatformOneSecond -#include "mDNSPlatformEnvironment.h" // Defines the specific types needed to run mDNS on this platform -#include "mDNSsprintf.h" -#include "mDNSvsprintf.h" // Used to implement LogErrorMessage(); - -#include - -//************************************************************************************************************* -// Globals - -static mDNS mDNSStorage; -static mDNS_PlatformSupport PlatformStorage; -#define RR_CACHE_SIZE 500 -static ResourceRecord rrcachestorage[RR_CACHE_SIZE]; -static const char PID_FILE[] = "/var/run/mDNSResponder.pid"; - -static const char kmDNSBootstrapName[] = "com.apple.mDNSResponder"; -static mach_port_t client_death_port = MACH_PORT_NULL; -static mach_port_t exit_m_port = MACH_PORT_NULL; -static mach_port_t server_priv_port = MACH_PORT_NULL; -static CFRunLoopTimerRef DeliverInstanceTimer; - -// mDNS Mach Message Timeout, in milliseconds. -// We need this to be short enough that we don't deadlock the mDNSResponder if a client -// fails to service its mach message queue, but long enough to give a well-written -// client a chance to service its mach message queue without getting cut off. -// Empirically, 50ms seems to work, so we set the timeout to 250ms to give -// even extra-slow clients a fair chance before we cut them off. -#define MDNS_MM_TIMEOUT 250 - -static int restarting_via_mach_init = 0; - -#if DEBUGBREAKS -static int debug_mode = 1; -#else -static int debug_mode = 0; -#endif - -//************************************************************************************************************* -// Active client list structures - -typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration; -struct DNSServiceDomainEnumeration_struct - { - DNSServiceDomainEnumeration *next; - mach_port_t ClientMachPort; - DNSQuestion dom; // Question asking for domains - DNSQuestion def; // Question asking for default domain - }; - -typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult; -struct DNSServiceBrowserResult_struct - { - DNSServiceBrowserResult *next; - int resultType; - char name[256], type[256], dom[256]; - }; - -typedef struct DNSServiceBrowser_struct DNSServiceBrowser; -struct DNSServiceBrowser_struct - { - DNSServiceBrowser *next; - mach_port_t ClientMachPort; - DNSQuestion q; - DNSServiceBrowserResult *results; - mDNSs32 lastsuccess; - }; - -typedef struct DNSServiceResolver_struct DNSServiceResolver; -struct DNSServiceResolver_struct - { - DNSServiceResolver *next; - mach_port_t ClientMachPort; - ServiceInfoQuery q; - ServiceInfo i; - }; - -typedef struct DNSServiceRegistration_struct DNSServiceRegistration; -struct DNSServiceRegistration_struct - { - DNSServiceRegistration *next; - mach_port_t ClientMachPort; - mDNSBool autoname; - mDNSBool autorename; - domainlabel name; - ServiceRecordSet s; - // Don't add any fields after ServiceRecordSet. - // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object - }; - -static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL; -static DNSServiceBrowser *DNSServiceBrowserList = NULL; -static DNSServiceResolver *DNSServiceResolverList = NULL; -static DNSServiceRegistration *DNSServiceRegistrationList = NULL; - -//************************************************************************************************************* -// General Utility Functions - -void LogErrorMessage(const char *format, ...) - { - unsigned char buffer[512]; - va_list ptr; - va_start(ptr,format); - buffer[mDNS_vsprintf((char *)buffer, format, ptr)] = 0; - va_end(ptr); - openlog("mDNSResponder", LOG_CONS | LOG_PERROR | LOG_PID, LOG_DAEMON); - fprintf(stderr, "%s\n", buffer); - syslog(LOG_ERR, "%s", buffer); - closelog(); - fflush(stderr); - } - -#if MACOSX_MDNS_MALLOC_DEBUGGING - -char _malloc_options[] = "AXZ"; - -static void validatelists(mDNS *const m) - { - DNSServiceDomainEnumeration *e; - DNSServiceBrowser *b; - DNSServiceResolver *l; - DNSServiceRegistration *r; - ResourceRecord *rr; - - for (e = DNSServiceDomainEnumerationList; e; e=e->next) - if (e->ClientMachPort == 0) - LogErrorMessage("!!!! DNSServiceDomainEnumerationList %X is garbage !!!!", e); - - for (b = DNSServiceBrowserList; b; b=b->next) - if (b->ClientMachPort == 0) - LogErrorMessage("!!!! DNSServiceBrowserList %X is garbage !!!!", b); - - for (l = DNSServiceResolverList; l; l=l->next) - if (l->ClientMachPort == 0) - LogErrorMessage("!!!! DNSServiceResolverList %X is garbage !!!!", l); - - for (r = DNSServiceRegistrationList; r; r=r->next) - if (r->ClientMachPort == 0) - LogErrorMessage("!!!! DNSServiceRegistrationList %X is garbage !!!!", r); - - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->RecordType == 0) - LogErrorMessage("!!!! ResourceRecords %X list is garbage !!!!"); - } - -void *mallocL(char *msg, unsigned int size) - { - unsigned long *mem = malloc(size+8); - if (!mem) - { LogErrorMessage("malloc(%s:%d) failed", msg, size); return(NULL); } - else - { - LogErrorMessage("malloc(%s:%d) = %X", msg, size, &mem[2]); - mem[0] = 0xDEADBEEF; - mem[1] = size; - bzero(&mem[2], size); - validatelists(&mDNSStorage); - return(&mem[2]); - } - } - -void freeL(char *msg, void *x) - { - if (!x) - LogErrorMessage("free(%s@NULL)!", msg); - else - { - unsigned long *mem = ((unsigned long *)x) - 2; - if (mem[0] != 0xDEADBEEF) - { LogErrorMessage("free(%s@%X) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; } - if (mem[1] > 8000) - { LogErrorMessage("free(%s:%d@%X) too big!", msg, mem[1], &mem[2]); return; } - LogErrorMessage("free(%s:%d@%X)", msg, mem[1], &mem[2]); - bzero(mem, mem[1]+8); - validatelists(&mDNSStorage); - free(mem); - } - } - -#endif - -//************************************************************************************************************* -// Client Death Detection - -mDNSlocal void AbortClient(mach_port_t ClientMachPort) - { - DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList; - DNSServiceBrowser **b = &DNSServiceBrowserList; - DNSServiceResolver **l = &DNSServiceResolverList; - DNSServiceRegistration **r = &DNSServiceRegistrationList; - - while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next; - if (*e) - { - DNSServiceDomainEnumeration *x = *e; - *e = (*e)->next; - debugf("Aborting DNSServiceDomainEnumeration %d", ClientMachPort); - mDNS_StopGetDomains(&mDNSStorage, &x->dom); - mDNS_StopGetDomains(&mDNSStorage, &x->def); - freeL("DNSServiceDomainEnumeration", x); - return; - } - - while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next; - if (*b) - { - DNSServiceBrowser *x = *b; - *b = (*b)->next; - debugf("Aborting DNSServiceBrowser %d", ClientMachPort); - mDNS_StopBrowse(&mDNSStorage, &x->q); - while (x->results) - { - DNSServiceBrowserResult *r = x->results; - x->results = x->results->next; - freeL("DNSServiceBrowserResult", r); - } - freeL("DNSServiceBrowser", x); - return; - } - - while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next; - if (*l) - { - DNSServiceResolver *x = *l; - *l = (*l)->next; - debugf("Aborting DNSServiceResolver %d", ClientMachPort); - mDNS_StopResolveService(&mDNSStorage, &x->q); - freeL("DNSServiceResolver", x); - return; - } - - while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next; - if (*r) - { - DNSServiceRegistration *x = *r; - *r = (*r)->next; - x->autorename = mDNSfalse; - mDNS_DeregisterService(&mDNSStorage, &x->s); - // Note that we don't do the "free(x);" here -- wait for the mStatus_MemFree message - return; - } - } - -mDNSlocal void AbortBlockedClient(mach_port_t c, char *m) - { - DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList; - DNSServiceBrowser **b = &DNSServiceBrowserList; - DNSServiceResolver **l = &DNSServiceResolverList; - DNSServiceRegistration **r = &DNSServiceRegistrationList; - while (*e && (*e)->ClientMachPort != c) e = &(*e)->next; - while (*b && (*b)->ClientMachPort != c) b = &(*b)->next; - while (*l && (*l)->ClientMachPort != c) l = &(*l)->next; - while (*r && (*r)->ClientMachPort != c) r = &(*r)->next; - if (*e) LogErrorMessage("%5d: DomainEnumeration(%##s) stopped accepting Mach messages (%s)", c, &e[0]->dom.name, m); - else if (*b) LogErrorMessage("%5d: Browser(%##s) stopped accepting Mach messages (%s)", c, &b[0]->q.name, m); - else if (*l) LogErrorMessage("%5d: Resolver(%##s) stopped accepting Mach messages (%s)", c, &l[0]->i.name, m); - else if (*r) LogErrorMessage("%5d: Registration(%##s) stopped accepting Mach messages (%s)", c, &r[0]->s.RR_SRV.name, m); - else LogErrorMessage("%5d (%s) stopped accepting Mach messages, but no record of client can be found!", c, m); - - AbortClient(c); - } - -mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info) - { - mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg; - if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME) - { - const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg; - AbortClient(deathMessage->not_port); - - /* Deallocate the send right that came in the dead name notification */ - mach_port_destroy( mach_task_self(), deathMessage->not_port ); - } - } - -mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort) - { - mach_port_t prev; - kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0, - client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev); - // If the port already died while we were thinking about it, then abort the operation right away - if (r != KERN_SUCCESS) - { - if (ClientMachPort != (mach_port_t)-1) - LogErrorMessage("Client %5d died before we could enable death notification", ClientMachPort); - AbortClient(ClientMachPort); - } - } - -//************************************************************************************************************* -// Domain Enumeration - -mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer) - { - kern_return_t status; - #pragma unused(m) - char buffer[256]; - DNSServiceDomainEnumerationReplyResultType rt; - DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->Context; - - debugf("FoundDomain: %##s PTR %##s", answer->name.c, answer->rdata->u.name.c); - if (answer->rrtype != kDNSType_PTR) return; - if (!x) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; } - - if (answer->rrremainingttl > 0) - { - if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain; - else rt = DNSServiceDomainEnumerationReplyAddDomainDefault; - } - else - { - if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain; - else return; - } - - ConvertDomainNameToCString(&answer->rdata->u.name, buffer); - status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "enumeration"); - } - -mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client, - int regDom) - { - kern_return_t status; - mStatus err; - - mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; - mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault; - const DNSServiceDomainEnumerationReplyResultType rt = DNSServiceDomainEnumerationReplyAddDomainDefault; - DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x)); - if (!x) { debugf("provide_DNSServiceDomainEnumerationCreate_rpc: No memory!"); return(mStatus_NoMemoryErr); } - x->ClientMachPort = client; - x->next = DNSServiceDomainEnumerationList; - DNSServiceDomainEnumerationList = x; - - debugf("Client %d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing"); - // We always give local. as the initial default browse domain, and then look for more - status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, "local.", 0, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - { - AbortBlockedClient(x->ClientMachPort, "local enumeration"); - return(mStatus_UnknownErr); - } - - err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, zeroIPAddr, FoundDomain, x); - if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, zeroIPAddr, FoundDomain, x); - - if (err) AbortClient(client); - else EnableDeathNotificationForClient(client); - - if (err) debugf("provide_DNSServiceDomainEnumerationCreate_rpc: mDNS_GetDomains error %d", err); - return(err); - } - -//************************************************************************************************************* -// Browse for services - -mDNSlocal void DeliverInstanceTimerCallBack(CFRunLoopTimerRef timer, void *info) - { - mDNSBool tryagain = mDNSfalse; - DNSServiceBrowser *b = DNSServiceBrowserList; - (void)timer; // Parameter not used - - while (b) - { - // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the - // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient() - // and that will cause the DNSServiceBrowser object's memory to be freed before it returns - DNSServiceBrowser *x = b; - b = b->next; - if (x->results) // Try to deliver the list of results - { - mDNSs32 now = mDNSPlatformTimeNow(); - while (x->results) - { - DNSServiceBrowserResult *const r = x->results; - DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0; - kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, r->name, r->type, r->dom, flags, 1); - // If we failed to send the mach message, try again in one second - if (status == MACH_SEND_TIMED_OUT) - { tryagain = mDNStrue; break; } - else - { - x->lastsuccess = now; - x->results = x->results->next; - freeL("DNSServiceBrowserResult", r); - } - } - // If this client hasn't read a single message in the last 60 seconds, abort it - if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond) - AbortBlockedClient(x->ClientMachPort, "browse"); - } - } - if (tryagain) - CFRunLoopTimerSetNextFireDate(DeliverInstanceTimer, CFAbsoluteTimeGetCurrent() + 1.0); - } - -mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer) - { - DNSServiceBrowser *browser = (DNSServiceBrowser *)question->Context; - DNSServiceBrowserResult **p = &browser->results; - DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x)); - domainlabel name; - domainname type, domain; - - if (answer->rrtype != kDNSType_PTR) - { - debugf("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); - return; - } - - if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain)) - { - debugf("FoundInstance: %##s PTR %##s is not valid NIAS service pointer", &answer->name, &answer->rdata->u.name); - return; - } - - if (!x) return; - - debugf("FoundInstance: %##s", answer->rdata->u.name.c); - ConvertDomainLabelToCString_unescaped(&name, x->name); - ConvertDomainNameToCString(&type, x->type); - ConvertDomainNameToCString(&domain, x->dom); - if (answer->rrremainingttl) - x->resultType = DNSServiceBrowserReplyAddInstance; - else x->resultType = DNSServiceBrowserReplyRemoveInstance; - x->next = NULL; - while (*p) p = &(*p)->next; - *p = x; - - // We schedule this timer 1/10 second in the future because CFRunLoop doesn't respect - // the relative priority between CFSocket and CFRunLoopTimer, and continues to call - // the timer callback even though there are packets waiting to be processed. - CFRunLoopTimerSetNextFireDate(DeliverInstanceTimer, CFAbsoluteTimeGetCurrent() + 0.1); - } - -mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client, - DNSCString regtype, DNSCString domain) - { - mStatus err; - domainname t, d; - DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x)); - if (!x) { debugf("provide_DNSServiceBrowserCreate_rpc: No memory!"); return(mStatus_NoMemoryErr); } - x->ClientMachPort = client; - x->results = NULL; - x->lastsuccess = 0; - x->next = DNSServiceBrowserList; - DNSServiceBrowserList = x; - - ConvertCStringToDomainName(regtype, &t); - ConvertCStringToDomainName(*domain ? domain : "local.", &d); - - debugf("Client %d: provide_DNSServiceBrowserCreate_rpc", client); - debugf("Client %d: Browse for Services: %##s%##s", client, &t, &d); - err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, zeroIPAddr, FoundInstance, x); - - if (err) AbortClient(client); - else EnableDeathNotificationForClient(client); - - if (err) debugf("provide_DNSServiceBrowserCreate_rpc: mDNS_StartBrowse error %d", err); - return(err); - } - -//************************************************************************************************************* -// Resolve Service Info - -mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) - { - kern_return_t status; - DNSServiceResolver *x = (DNSServiceResolver *)query->Context; - struct sockaddr_in interface; - struct sockaddr_in address; - char cstring[1024]; - int i, pstrlen = query->info->TXTinfo[0]; - - //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name); - - if (query->info->TXTlen > sizeof(cstring)) return; - - bzero(&interface, sizeof(interface)); - bzero(&address, sizeof(address)); - - interface.sin_len = sizeof(interface); - interface.sin_family = AF_INET; - interface.sin_port = 0; - interface.sin_addr.s_addr = query->info->InterfaceAddr.NotAnInteger; - - address.sin_len = sizeof(address); - address.sin_family = AF_INET; - address.sin_port = query->info->port.NotAnInteger; - address.sin_addr.s_addr = query->info->ip.NotAnInteger; - - // The OS X DNSServiceResolverResolve() API is defined using a C-string, - // but the mDNS_StartResolveService() call actually returns a packed block of P-strings. - // Hence we have to convert the P-string(s) to a C-string before returning the result to the client. - // ASCII-1 characters are used in the C-string as boundary markers, - // to indicate the boundaries between the original constituent P-strings. - for (i=1; iinfo->TXTlen; i++) - { - if (--pstrlen >= 0) - cstring[i-1] = query->info->TXTinfo[i]; - else - { - cstring[i-1] = 1; - pstrlen = query->info->TXTinfo[i]; - } - } - cstring[i-1] = 0; // Put the terminating NULL on the end - - status = DNSServiceResolverReply_rpc(x->ClientMachPort, - (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "resolve"); - } - -mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client, - DNSCString name, DNSCString regtype, DNSCString domain) - { - mStatus err; - domainlabel n; - domainname t, d; - DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x)); - if (!x) { debugf("provide_DNSServiceResolverResolve_rpc: No memory!"); return(mStatus_NoMemoryErr); } - x->ClientMachPort = client; - x->next = DNSServiceResolverList; - DNSServiceResolverList = x; - - ConvertCStringToDomainLabel(name, &n); - ConvertCStringToDomainName(regtype, &t); - ConvertCStringToDomainName(*domain ? domain : "local.", &d); - ConstructServiceName(&x->i.name, &n, &t, &d); - x->i.InterfaceAddr = zeroIPAddr; - - debugf("Client %d: provide_DNSServiceResolverResolve_rpc", client); - debugf("Client %d: Resolve Service: %##s", client, &x->i.name); - err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x); - - if (err) AbortClient(client); - else EnableDeathNotificationForClient(client); - - if (err) debugf("provide_DNSServiceResolverResolve_rpc: mDNS_StartResolveService error %d", err); - return(err); - } - -//************************************************************************************************************* -// Registration - -mDNSlocal void FreeDNSServiceRegistration(DNSServiceRegistration *x) - { - while (x->s.Extras) - { - ExtraResourceRecord *extras = x->s.Extras; - x->s.Extras = x->s.Extras->next; - if (extras->r.rdata != &extras->r.rdatastorage) - freeL("Extra RData", extras->r.rdata); - freeL("ExtraResourceRecord", extras); - } - - if (x->s.RR_TXT.rdata != &x->s.RR_TXT.rdatastorage) - freeL("TXT RData", x->s.RR_TXT.rdata); - - freeL("DNSServiceRegistration", x); - } - -mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) - { - DNSServiceRegistration *x = (DNSServiceRegistration*)sr->Context; - - switch (result) - { - case mStatus_NoError: debugf("RegCallback: %##s Name Registered", sr->RR_SRV.name.c); break; - case mStatus_NameConflict: debugf("RegCallback: %##s Name Conflict", sr->RR_SRV.name.c); break; - case mStatus_MemFree: debugf("RegCallback: %##s Memory Free", sr->RR_SRV.name.c); break; - default: debugf("RegCallback: %##s Unknown Result %d", sr->RR_SRV.name.c, result); break; - } - - if (result == mStatus_NoError) - { - kern_return_t status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "registration success"); - } - - if (result == mStatus_NameConflict) - { - // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered - // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well. - if (x->autoname) - mDNS_RenameAndReregisterService(m, sr, mDNSNULL); - else - { - kern_return_t status; - // AbortClient unlinks our DNSServiceRegistration from the list so we can safely free it - AbortClient(x->ClientMachPort); - status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT); - if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "registration conflict"); // Yes, this IS safe :-) - FreeDNSServiceRegistration(x); - } - } - - if (result == mStatus_MemFree) - { - if (x->autorename) - { - debugf("RegCallback renaming %#s to %#s", &x->name, &mDNSStorage.nicelabel); - x->autorename = mDNSfalse; - x->name = mDNSStorage.nicelabel; - mDNS_RenameAndReregisterService(m, &x->s, &x->name); - } - else - { - DNSServiceRegistration **r = &DNSServiceRegistrationList; - while (*r && *r != x) r = &(*r)->next; - if (*r) - { - debugf("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.name.c); - *r = (*r)->next; - } - debugf("RegCallback: Freeing DNSServiceRegistration %##s %d", sr->RR_SRV.name.c, x->ClientMachPort); - FreeDNSServiceRegistration(x); - } - } - } - -mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainlabel *n, domainname *t, domainname *d, mDNSIPPort port) - { - char name[256]; - int count = 0; - ResourceRecord *rr; - domainname srvname; - ConstructServiceName(&srvname, n, t, d); - mDNS_sprintf(name, "%##s", &srvname); - - for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) - if (rr->rrtype == kDNSType_SRV && rr->rdata->u.srv.port.NotAnInteger == port.NotAnInteger && SameDomainName(&rr->name, &srvname)) - count++; - - if (count) - { - debugf("Client %5d registering Service Record Set \"%##s\"; WARNING! now have %d instances port %d", - x->ClientMachPort, &srvname, count+1, (int)port.b[0] << 8 | port.b[1]); - LogErrorMessage("%5d: WARNING! Bogus client application has now registered %d identical instances of service %##s port %d", - x->ClientMachPort, count+1, &srvname, (int)port.b[0] << 8 | port.b[1]); - } - } - -mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client, - DNSCString name, DNSCString regtype, DNSCString domain, int notAnIntPort, DNSCString txtRecord) - { - mStatus err; - domainname t, d; - mDNSIPPort port; - unsigned char txtinfo[1024] = ""; - int data_len = 0; - int size = sizeof(RDataBody); - unsigned char *pstring = &txtinfo[data_len]; - char *ptr = txtRecord; - DNSServiceRegistration *x; - - // The OS X DNSServiceRegistrationCreate() API is defined using a C-string, - // but the mDNS_RegisterService() call actually requires a packed block of P-strings. - // Hence we have to convert the C-string to a P-string. - // ASCII-1 characters are allowed in the C-string as boundary markers, - // so that a single C-string can be used to represent one or more P-strings. - while (*ptr) - { - if (++data_len >= sizeof(txtinfo)) return(mStatus_BadParamErr); - if (*ptr == 1) // If this is our boundary marker, start a new P-string - { - pstring = &txtinfo[data_len]; - pstring[0] = 0; - ptr++; - } - else - { - if (pstring[0] == 255) return(mStatus_BadParamErr); - pstring[++pstring[0]] = *ptr++; - } - } - - data_len++; - if (size < data_len) - size = data_len; - - x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size); - if (!x) { debugf("provide_DNSServiceRegistrationCreate_rpc: No memory!"); return(mStatus_NoMemoryErr); } - x->ClientMachPort = client; - x->next = DNSServiceRegistrationList; - DNSServiceRegistrationList = x; - - x->autoname = (*name == 0); - x->autorename = mDNSfalse; - if (x->autoname) x->name = mDNSStorage.nicelabel; - else ConvertCStringToDomainLabel(name, &x->name); - ConvertCStringToDomainName(regtype, &t); - ConvertCStringToDomainName(*domain ? domain : "local.", &d); - port.NotAnInteger = notAnIntPort; - - debugf("Client %d: provide_DNSServiceRegistrationCreate_rpc", client); - debugf("Client %d: Register Service: %#s.%##s%##s %d %.30s", - client, &x->name, &t, &d, (int)port.b[0] << 8 | port.b[1], txtRecord); - if (port.NotAnInteger) CheckForDuplicateRegistrations(x, &x->name, &t, &d, port); - err = mDNS_RegisterService(&mDNSStorage, &x->s, &x->name, &t, &d, mDNSNULL, port, txtinfo, data_len, RegCallback, x); - - if (err) AbortClient(client); - else EnableDeathNotificationForClient(client); - - if (err) debugf("provide_DNSServiceRegistrationCreate_rpc: mDNS_RegisterService error %d", err); - else debugf("Made Service Record Set for %##s", &x->s.RR_SRV.name); - - return(err); - } - -void NetworkChanged(void) - { - DNSServiceRegistration *r; - for (r = DNSServiceRegistrationList; r; r=r->next) - if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c)) - { - debugf("NetworkChanged renaming %#s to %#s", &r->name, &mDNSStorage.nicelabel); - r->autorename = mDNStrue; - mDNS_DeregisterService(&mDNSStorage, &r->s); - } - } - -mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client, - int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference) - { - mStatus err; - DNSServiceRegistration *x = DNSServiceRegistrationList; - ExtraResourceRecord *extra; - int size = sizeof(RDataBody); - if (size < data_len) - size = data_len; - - // Find this registered service - while (x && x->ClientMachPort != client) x = x->next; - if (!x) - { - debugf("provide_DNSServiceRegistrationAddRecord_rpc bad client %X", client); - return(mStatus_BadReferenceErr); - } - - // Allocate storage for our new record - extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); - if (!extra) return(mStatus_NoMemoryErr); - - // Fill in type, length, and data - extra->r.rrtype = type; - extra->r.rdatastorage.MaxRDLength = size; - extra->r.rdatastorage.RDLength = data_len; - memcpy(&extra->r.rdatastorage.u.data, data, data_len); - - // And register it - err = mDNS_AddRecordToService(&mDNSStorage, &x->s, extra, &extra->r.rdatastorage, ttl); - *reference = (natural_t)extra; - debugf("Received a request to add the record of type: %d length: %d; returned reference %X", - type, data_len, *reference); - return(err); - } - -mDNSlocal void UpdateCallback(mDNS *const m, ResourceRecord *const rr, RData *OldRData) - { - if (OldRData != &rr->rdatastorage) - freeL("Old RData", OldRData); - } - -mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client, - natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) - { - mStatus err; - DNSServiceRegistration *x = DNSServiceRegistrationList; - ResourceRecord *rr; - RData *newrdata; - int size = sizeof(RDataBody); - if (size < data_len) - size = data_len; - - // Find this registered service - while (x && x->ClientMachPort != client) x = x->next; - if (!x) - { - debugf("provide_DNSServiceRegistrationUpdateRecord_rpc bad client %X", client); - return(mStatus_BadReferenceErr); - } - - // Find the record we're updating - if (!reference) // NULL reference means update the primary TXT record - rr = &x->s.RR_TXT; - else // Else, scan our list to make sure we're updating a valid record that was previously added - { - ExtraResourceRecord *e = x->s.Extras; - while (e && e != (ExtraResourceRecord*)reference) e = e->next; - if (!e) - { - debugf("provide_DNSServiceRegistrationUpdateRecord_rpc failed to find record %X", reference); - return(mStatus_BadReferenceErr); - } - rr = &e->r; - } - - // Allocate storage for our new data - newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size); - if (!newrdata) return(mStatus_NoMemoryErr); - - // Fill in new length, and data - newrdata->MaxRDLength = size; - newrdata->RDLength = data_len; - memcpy(&newrdata->u, data, data_len); - - // And update our record - err = mDNS_Update(&mDNSStorage, rr, ttl, newrdata, UpdateCallback); - if (err) - { - debugf("Received a request to update the record of length: %d for reference: %X; failed %d", - data_len, reference, err); - return(err); - } - - debugf("Received a request to update the record of length: %d for reference: %X", data_len, reference); - return(err); - } - -mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client, - natural_t reference) - { - mStatus err; - DNSServiceRegistration *x = DNSServiceRegistrationList; - ExtraResourceRecord *extra = (ExtraResourceRecord*)reference; - - // Find this registered service - while (x && x->ClientMachPort != client) x = x->next; - if (!x) - { - LogErrorMessage("DNSServiceRegistrationRemoveRecord Client %5d not found", client); - debugf("provide_DNSServiceRegistrationRemoveRecord_rpc bad client %X", client); - return(mStatus_BadReferenceErr); - } - - err = mDNS_RemoveRecordFromService(&mDNSStorage, &x->s, extra); - if (err) - { - LogErrorMessage("DNSServiceRegistrationRemoveRecord Client %5d does not have record %X", client, extra); - debugf("Received a request to remove the record of reference: %X (failed %d)", extra, err); - return(err); - } - - debugf("Received a request to remove the record of reference: %X", extra); - if (extra->r.rdata != &extra->r.rdatastorage) - freeL("Extra RData", extra->r.rdata); - freeL("ExtraResourceRecord", extra); - return(err); - } - -//************************************************************************************************************* -// Support Code - -mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) - { - mig_reply_error_t *request = msg; - mig_reply_error_t *reply; - mach_msg_return_t mr; - int options; - - /* allocate a reply buffer */ - reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0); - - /* call the MiG server routine */ - (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head); - - if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS)) - { - if (reply->RetCode == MIG_NO_REPLY) - { - /* - * This return code is a little tricky -- it appears that the - * demux routine found an error of some sort, but since that - * error would not normally get returned either to the local - * user or the remote one, we pretend it's ok. - */ - CFAllocatorDeallocate(NULL, reply); - return; - } - - /* - * destroy any out-of-line data in the request buffer but don't destroy - * the reply port right (since we need that to send an error message). - */ - request->Head.msgh_remote_port = MACH_PORT_NULL; - mach_msg_destroy(&request->Head); - } - - if (reply->Head.msgh_remote_port == MACH_PORT_NULL) - { - /* no reply port, so destroy the reply */ - if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) - mach_msg_destroy(&reply->Head); - CFAllocatorDeallocate(NULL, reply); - return; - } - - /* - * send reply. - * - * We don't want to block indefinitely because the client - * isn't receiving messages from the reply port. - * If we have a send-once right for the reply port, then - * this isn't a concern because the send won't block. - * If we have a send right, we need to use MACH_SEND_TIMEOUT. - * To avoid falling off the kernel's fast RPC path unnecessarily, - * we only supply MACH_SEND_TIMEOUT when absolutely necessary. - */ - - options = MACH_SEND_MSG; - if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) - options |= MACH_SEND_TIMEOUT; - - mr = mach_msg(&reply->Head, /* msg */ - options, /* option */ - reply->Head.msgh_size, /* send_size */ - 0, /* rcv_size */ - MACH_PORT_NULL, /* rcv_name */ - MACH_MSG_TIMEOUT_NONE, /* timeout */ - MACH_PORT_NULL); /* notify */ - - /* Has a message error occurred? */ - switch (mr) - { - case MACH_SEND_INVALID_DEST: - case MACH_SEND_TIMED_OUT: - /* the reply can't be delivered, so destroy it */ - mach_msg_destroy(&reply->Head); - break; - - default : - /* Includes success case. */ - break; - } - - CFAllocatorDeallocate(NULL, reply); - } - -mDNSlocal kern_return_t registerBootstrapService() - { - kern_return_t status; - mach_port_t service_send_port, service_rcv_port; - - debugf("Registering Bootstrap Service"); - - /* - * See if our service name is already registered and if we have privilege to check in. - */ - status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port); - if (status == KERN_SUCCESS) - { - /* - * If so, we must be a followup instance of an already defined server. In that case, - * the bootstrap port we inherited from our parent is the server's privilege port, so set - * that in case we have to unregister later (which requires the privilege port). - */ - server_priv_port = bootstrap_port; - restarting_via_mach_init = TRUE; - } - else if (status == BOOTSTRAP_UNKNOWN_SERVICE) - { - status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(), - FALSE /* relaunch immediately, not on demand */, &server_priv_port); - if (status != KERN_SUCCESS) return status; - - status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port); - if (status != KERN_SUCCESS) - { - mach_port_deallocate(mach_task_self(), server_priv_port); - return status; - } - - status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port); - if (status != KERN_SUCCESS) - { - mach_port_deallocate(mach_task_self(), server_priv_port); - mach_port_deallocate(mach_task_self(), service_send_port); - return status; - } - assert(service_send_port == service_rcv_port); - } - - /* - * We have no intention of responding to requests on the service port. We are not otherwise - * a Mach port-based service. We are just using this mechanism for relaunch facilities. - * So, we can dispose of all the rights we have for the service port. We don't destroy the - * send right for the server's privileged bootstrap port - in case we have to unregister later. - */ - mach_port_destroy(mach_task_self(), service_rcv_port); - return status; - } - -mDNSlocal kern_return_t destroyBootstrapService() - { - debugf("Destroying Bootstrap Service"); - return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL); - } - -mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) - { - debugf("ExitCallback: destroyBootstrapService"); - if (!debug_mode) - destroyBootstrapService(); - - debugf("ExitCallback: Aborting MIG clients"); - while (DNSServiceDomainEnumerationList) AbortClient(DNSServiceDomainEnumerationList->ClientMachPort); - while (DNSServiceBrowserList) AbortClient(DNSServiceBrowserList->ClientMachPort); - while (DNSServiceResolverList) AbortClient(DNSServiceResolverList->ClientMachPort); - while (DNSServiceRegistrationList) AbortClient(DNSServiceRegistrationList->ClientMachPort); - - debugf("ExitCallback: mDNS_Close"); - mDNS_Close(&mDNSStorage); - exit(0); - } - -mDNSlocal kern_return_t start(const char *bundleName, const char *bundleDir) - { - extern void (*NotifyClientNetworkChanged)(void); // Temp fix for catching name changes - mStatus err; - CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, &mDNSStorage, NULL, NULL, NULL }; - CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); - CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); - CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL); - mach_port_t m_port = CFMachPortGetPort(s_port); - kern_return_t status = bootstrap_register(bootstrap_port, DNS_SERVICE_DISCOVERY_SERVER, m_port); - CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0); - CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); - CFRunLoopSourceRef e_rls = CFMachPortCreateRunLoopSource(NULL, e_port, 0); - - if (status) - { - if (status == 1103) - LogErrorMessage("Bootstrap_register failed(): A copy of the daemon is apparently already running"); - else - LogErrorMessage("Bootstrap_register failed(): %s %d", mach_error_string(status), status); - return(status); - } - - // Note: Every CFRunLoopTimer has to be created with an initial fire time, and a repeat interval, or it becomes - // a one-shot timer and you can't use CFRunLoopTimerSetNextFireDate(timer, when) to schedule subsequent firings. - // Here we create it with an initial fire time 24 hours from now, and a repeat interval of 24 hours, with - // the intention that we'll actually reschedule it using CFRunLoopTimerSetNextFireDate(timer, when) as necessary. - DeliverInstanceTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, - CFAbsoluteTimeGetCurrent() + 24.0*60.0*60.0, 24.0*60.0*60.0, - 0, // no flags - 9, // low priority execution (after all packets, etc., have been handled). - DeliverInstanceTimerCallBack, &myCFRunLoopTimerContext); - if (!DeliverInstanceTimer) return(-1); - CFRunLoopAddTimer(CFRunLoopGetCurrent(), DeliverInstanceTimer, kCFRunLoopDefaultMode); - - err = mDNS_Init(&mDNSStorage, &PlatformStorage, rrcachestorage, RR_CACHE_SIZE, NULL, NULL); - if (err) { LogErrorMessage("Daemon start: mDNS_Init failed %ld", err); return(err); } - - client_death_port = CFMachPortGetPort(d_port); - exit_m_port = CFMachPortGetPort(e_port); - - CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode); - CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode); - CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode); - CFRelease(d_rls); - CFRelease(s_rls); - CFRelease(e_rls); - if (debug_mode) printf("Service registered with Mach Port %d\n", m_port); - - NotifyClientNetworkChanged = NetworkChanged; - - return(err); - } - -mDNSlocal void HandleSIG(int signal) - { - debugf(""); - debugf("HandleSIG"); - - // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit - mach_msg_return_t msg_result; - mach_msg_header_t header; - - header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - header.msgh_remote_port = exit_m_port; - header.msgh_local_port = MACH_PORT_NULL; - header.msgh_size = sizeof(header); - header.msgh_id = 0; - - msg_result = mach_msg_send(&header); - } - -mDNSexport int main(int argc, char **argv) - { - int i; - kern_return_t status; - FILE *fp; - - for (i=1; i, @@ -25,21 +40,970 @@ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't * understand why variable y is not of type "char*" just proves the point that poor code * layout leads people to unfortunate misunderstandings about how the C language really works.) - */ + + Change History (most recent first): + +$Log: mDNS.c,v $ +Revision 1.307 2003/09/09 20:13:30 cheshire + Don't send a Goodbye record if we never announced it +Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented +rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount + +Revision 1.306 2003/09/09 03:00:03 cheshire + Services take a long time to disappear when switching networks. +Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect + +Revision 1.305 2003/09/09 02:49:31 cheshire + Initial probes and queries not grouped on wake-from-sleep + +Revision 1.304 2003/09/09 02:41:19 cheshire + Don't send a Goodbye record if we never announced it + +Revision 1.303 2003/09/05 19:55:02 cheshire + Include address records when announcing SRV records + +Revision 1.302 2003/09/05 00:01:36 cheshire + Don't accelerate queries that have large KA lists + +Revision 1.301 2003/09/04 22:51:13 cheshire + Group probes and goodbyes better + +Revision 1.300 2003/09/03 02:40:37 cheshire + mDNSResponder complains about '_'s +Underscores are not supposed to be legal in standard DNS names, but IANA appears +to have allowed them in previous service name registrations, so we should too. + +Revision 1.299 2003/09/03 02:33:09 cheshire + CacheRecordRmv ERROR +Don't update m->NewQuestions until *after* CheckCacheExpiration(); + +Revision 1.298 2003/09/03 01:47:01 cheshire + Rendezvous services always in a state of flux +Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds + +Revision 1.297 2003/08/29 19:44:15 cheshire + Traffic reduction: Eliminate synchronized QUs when a new service appears +1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries + that already have at least one unique answer in the cache +2. For these queries, go straight to QM, skipping QU + +Revision 1.296 2003/08/29 19:08:21 cheshire + Traffic reduction: Eliminate huge KA lists after wake from sleep +Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time. + +Revision 1.295 2003/08/28 01:10:59 cheshire + Add syslog message to report when query is reset because of immediate answer burst + +Revision 1.294 2003/08/27 02:30:22 cheshire + Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() +One more change: "query->GotTXT" is now a straightforward bi-state boolean again + +Revision 1.293 2003/08/27 02:25:31 cheshire + Traffic Reduction: Inefficiencies in DNSServiceResolverResolve() + +Revision 1.292 2003/08/21 19:27:36 cheshire + Traffic reduction: No need to announce record for longer than TTL + +Revision 1.291 2003/08/21 18:57:44 cheshire + Synchronized queries on the network + +Revision 1.290 2003/08/21 02:25:23 cheshire +Minor changes to comments and debugf() messages + +Revision 1.289 2003/08/21 02:21:50 cheshire + Efficiency: Reduce repeated queries + +Revision 1.288 2003/08/20 23:39:30 cheshire + Review syslog messages, and remove as appropriate + +Revision 1.287 2003/08/20 20:47:18 cheshire +Fix compiler warning + +Revision 1.286 2003/08/20 02:18:51 cheshire + Cleanup: Review syslog messages + +Revision 1.285 2003/08/20 01:59:06 cheshire + rdatahash and rdnamehash not updated after changing rdata +Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place + +Revision 1.284 2003/08/19 22:20:00 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.283 2003/08/19 22:16:27 cheshire +Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case. + +Revision 1.282 2003/08/19 06:48:25 cheshire + Guard against excessive record updates +Each record starts with 10 UpdateCredits. +Every update consumes one UpdateCredit. +UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. +As the number of UpdateCredits declines, the number of announcements is similarly scaled back. +When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. + +Revision 1.281 2003/08/19 04:49:28 cheshire + Interaction between v4, v6 and dual-stack hosts not working quite right +1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6. +2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. +3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. + +Revision 1.280 2003/08/19 02:33:36 cheshire +Update comments + +Revision 1.279 2003/08/19 02:31:11 cheshire + mDNSResponder overenthusiastic with final expiration queries +Final expiration queries now only mark the question for sending on the particular interface +pertaining to the record that's expiring. + +Revision 1.278 2003/08/18 22:53:37 cheshire + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.277 2003/08/18 19:05:44 cheshire + UpdateRecord not working right +Added "newrdlength" field to hold new length of updated rdata + +Revision 1.276 2003/08/16 03:39:00 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.275 2003/08/16 02:51:27 cheshire + mDNSResponder takes too much RPRVT +Don't try to compute namehash etc, until *after* validating the name + +Revision 1.274 2003/08/16 01:12:40 cheshire + mDNSResponder takes too much RPRVT +Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a +simple C structure assignment of a domainname, because that object is defined to be 256 bytes long, +and in the process of copying it, the C compiler may run off the end of the rdata object into +unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a +call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid. + +Revision 1.273 2003/08/15 20:16:02 cheshire + mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.272 2003/08/14 19:29:04 cheshire + Include cache records in SIGINFO output +Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them + +Revision 1.271 2003/08/14 02:17:05 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.270 2003/08/13 17:07:28 ksekar +Bug #: : Extra RR linked to list even if registration fails - causes crash +Added check to result of mDNS_Register() before linking extra record into list. + +Revision 1.269 2003/08/12 19:56:23 cheshire +Update to APSL 2.0 + +Revision 1.268 2003/08/12 15:01:10 cheshire +Add comments + +Revision 1.267 2003/08/12 14:59:27 cheshire + Rate-limiting blocks some legitimate responses +When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine +whether to suppress the response, also check LastMCInterface to see if it matches. + +Revision 1.266 2003/08/12 12:47:16 cheshire +In mDNSCoreMachineSleep debugf message, display value of m->timenow + +Revision 1.265 2003/08/11 20:04:28 cheshire + Improve efficiency by restricting cases where we have to walk the entire cache + +Revision 1.264 2003/08/09 00:55:02 cheshire + mDNSResponder is taking 20-30% of the CPU +Don't scan the whole cache after every packet. + +Revision 1.263 2003/08/09 00:35:29 cheshire +Moved AnswerNewQuestion() later in the file, in preparation for next checkin + +Revision 1.262 2003/08/08 19:50:33 cheshire + Remove "Cache size now xxx" messages + +Revision 1.261 2003/08/08 19:18:45 cheshire + Only retrigger questions on platforms with the "PhantomInterfaces" bug + +Revision 1.260 2003/08/08 18:55:48 cheshire + Guard against time going backwards + +Revision 1.259 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.258 2003/08/08 16:22:05 cheshire + Need to check validity of TXT (and other) records +Remove unneeded LogMsg + +Revision 1.257 2003/08/07 01:41:08 cheshire + Ignore packets with invalid source address (all zeroes or all ones) + +Revision 1.256 2003/08/06 23:25:51 cheshire + Increase TTL for A/AAAA/SRV from one minute to four + +Revision 1.255 2003/08/06 23:22:50 cheshire +Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours) + +Revision 1.254 2003/08/06 21:33:39 cheshire +Fix compiler warnings on PocketPC 2003 (Windows CE) + +Revision 1.253 2003/08/06 20:43:57 cheshire + Need to check validity of TXT (and other) records +Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update() + +Revision 1.252 2003/08/06 20:35:47 cheshire +Enhance debugging routine GetRRDisplayString() so it can also be used to display +other RDataBody objects, not just the one currently attached the given ResourceRecord + +Revision 1.251 2003/08/06 19:07:34 cheshire + mDNSResponder not inhibiting multicast responses as much as it should +Was checking LastAPTime instead of LastMCTime + +Revision 1.250 2003/08/06 19:01:55 cheshire +Update comments + +Revision 1.249 2003/08/06 00:13:28 cheshire +Tidy up debugf messages + +Revision 1.248 2003/08/05 22:20:15 cheshire + Need to check IP TTL on responses + +Revision 1.247 2003/08/05 00:56:39 cheshire + mDNSResponder sending additional records, even after precursor record suppressed + +Revision 1.246 2003/08/04 19:20:49 cheshire +Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages + +Revision 1.245 2003/08/02 01:56:29 cheshire +For debugging: log message if we ever get more than one question in a truncated packet + +Revision 1.244 2003/08/01 23:55:32 cheshire +Fix for compiler warnings on Windows, submitted by Bob Bradley + +Revision 1.243 2003/07/25 02:26:09 cheshire +Typo: FIxed missing semicolon + +Revision 1.242 2003/07/25 01:18:41 cheshire +Fix memory leak on shutdown in mDNS_Close() (detected in Windows version) + +Revision 1.241 2003/07/23 21:03:42 cheshire +Only show "Found record..." debugf message in verbose mode + +Revision 1.240 2003/07/23 21:01:11 cheshire + Need Nagle-style algorithm to coalesce multiple packets into one +After sending a packet, suppress further sending for the next 100ms. + +Revision 1.239 2003/07/22 01:30:05 cheshire + Don't try to add the same question to the duplicate-questions list more than once + +Revision 1.238 2003/07/22 00:10:20 cheshire + ConvertDomainLabelToCString() needs to escape escape characters + +Revision 1.237 2003/07/19 03:23:13 cheshire + mDNSResponder needs to receive and cache larger records + +Revision 1.236 2003/07/19 03:04:55 cheshire +Fix warnings; some debugf message improvements + +Revision 1.235 2003/07/19 00:03:32 cheshire + ScheduleNextTask needs to be smarter after a no-op packet is received +ScheduleNextTask is quite an expensive operation. +We don't need to do all that work after receiving a no-op packet that didn't change our state. + +Revision 1.234 2003/07/18 23:52:11 cheshire +To improve consistency of field naming, global search-and-replace: +NextProbeTime -> NextScheduledProbe +NextResponseTime -> NextScheduledResponse + +Revision 1.233 2003/07/18 00:29:59 cheshire + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.232 2003/07/18 00:11:38 cheshire +Add extra case to switch statements to handle HINFO data for Get, Put and Display +(In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT) + +Revision 1.231 2003/07/18 00:06:37 cheshire +To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->" + +Revision 1.230 2003/07/17 18:16:54 cheshire + Rendezvous services always in a state of flux +In preparation for working on this, made some debugf messages a little more selective + +Revision 1.229 2003/07/17 17:35:04 cheshire + Rate-limit responses, to guard against packet flooding + +Revision 1.228 2003/07/16 20:50:27 cheshire + Need to implement "unicast response" request, using top bit of qclass + +Revision 1.227 2003/07/16 05:01:36 cheshire +Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for + Need to implement "unicast response" request, using top bit of qclass + +Revision 1.226 2003/07/16 04:51:44 cheshire +Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval' + +Revision 1.225 2003/07/16 04:46:41 cheshire +Minor wording cleanup: The correct DNS term is "response", not "reply" + +Revision 1.224 2003/07/16 04:39:02 cheshire +Textual cleanup (no change to functionality): +Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)" + +Revision 1.223 2003/07/16 00:09:22 cheshire +Textual cleanup (no change to functionality): +Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places; +replace with macro "TicksTTL(rr)" +Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" +replaced with macro "RRExpireTime(rr)" + +Revision 1.222 2003/07/15 23:40:46 cheshire +Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo() + +Revision 1.221 2003/07/15 22:17:56 cheshire + mDNSResponder is not being efficient when doing certain queries + +Revision 1.220 2003/07/15 02:12:51 cheshire +Slight tidy-up of debugf messages and comments + +Revision 1.219 2003/07/15 01:55:12 cheshire + Need to implement service registration with subtypes + +Revision 1.218 2003/07/14 16:26:06 cheshire + Duplicate query suppression not working right +Refinement: Don't record DS information for a question in the first quarter second +right after we send it -- in the case where a question happens to be accelerated by +the maximum allowed amount, we don't want it to then be suppressed because the previous +time *we* sent that question falls (just) within the valid duplicate suppression window. + +Revision 1.217 2003/07/13 04:43:53 cheshire + Services on multiple interfaces not always resolving +Minor refinement: No need to make address query broader than the original SRV query that provoked it + +Revision 1.216 2003/07/13 03:13:17 cheshire + Services on multiple interfaces not always resolving +If we get an identical SRV on a second interface, convert address queries to non-specific + +Revision 1.215 2003/07/13 02:28:00 cheshire + SendResponses didn't all its responses +Delete all references to RRInterfaceActive -- it's now superfluous + +Revision 1.214 2003/07/13 01:47:53 cheshire +Fix one error and one warning in the Windows build + +Revision 1.213 2003/07/12 04:25:48 cheshire +Fix minor signed/unsigned warnings + +Revision 1.212 2003/07/12 01:59:11 cheshire +Minor changes to debugf messages + +Revision 1.211 2003/07/12 01:47:01 cheshire + After name conflict, appended number should be higher than previous number + +Revision 1.210 2003/07/12 01:43:28 cheshire + Duplicate query suppression not working right +The correct cutoff time for duplicate query suppression is timenow less one-half the query interval. +The code was incorrectly using the last query time plus one-half the query interval. +This was only correct in the case where query acceleration was not in effect. + +Revision 1.209 2003/07/12 01:27:50 cheshire + Hostname conflict naming should not use two hyphens +Fix missing "-1" in RemoveLabelSuffix() + +Revision 1.208 2003/07/11 01:32:38 cheshire +Syntactic cleanup (no change to funcationality): Now that we only have one host name, +rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". + +Revision 1.207 2003/07/11 01:28:00 cheshire + No more local.arpa + +Revision 1.206 2003/07/11 00:45:02 cheshire + Client should get callback confirming successful host name registration + +Revision 1.205 2003/07/11 00:40:18 cheshire +Tidy up debug message in HostNameCallback() + +Revision 1.204 2003/07/11 00:20:32 cheshire + mDNSResponder should log a message after 16 unsuccessful probes + +Revision 1.203 2003/07/10 23:53:41 cheshire + Hostname conflict naming should not use two hyphens + +Revision 1.202 2003/07/04 02:23:20 cheshire + Responder too aggressive at flushing stale data +Changed mDNSResponder to require four unanswered queries before purging a record, instead of two. + +Revision 1.201 2003/07/04 01:09:41 cheshire + Need to implement subtype queries +Modified ConstructServiceName() to allow three-part service types + +Revision 1.200 2003/07/03 23:55:26 cheshire +Minor change to wording of syslog warning messages + +Revision 1.199 2003/07/03 23:51:13 cheshire +: Lots of "have given xxx answers" syslog warnings +Added more detailed debugging information + +Revision 1.198 2003/07/03 22:19:30 cheshire + Bug fix in 3274153 breaks TiVo +Make exception to allow _tivo_servemedia._tcp. + +Revision 1.197 2003/07/02 22:33:05 cheshire + mDNSResponder needs to start with a smaller cache and then grow it as needed +Minor refinements: +When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not +Allow cache to grow to 512 records before considering it a potential denial-of-service attack + +Revision 1.196 2003/07/02 21:19:45 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.195 2003/07/02 19:56:58 cheshire + mDNSResponder needs to start with a smaller cache and then grow it as needed +Minor refinement: m->rrcache_active was not being decremented when +an active record was deleted because its TTL expired + +Revision 1.194 2003/07/02 18:47:40 cheshire +Minor wording change to log messages + +Revision 1.193 2003/07/02 02:44:13 cheshire +Fix warning in non-debug build + +Revision 1.192 2003/07/02 02:41:23 cheshire + mDNSResponder needs to start with a smaller cache and then grow it as needed + +Revision 1.191 2003/07/02 02:30:51 cheshire +HashSlot() returns an array index. It can't be negative; hence it should not be signed. + +Revision 1.190 2003/06/27 00:03:05 vlubet + Merge of build failure fix for gcc 3.3 + +Revision 1.189 2003/06/11 19:24:03 cheshire + Crash in SendQueries/SendResponses when no active interfaces +Slight refinement to previous checkin + +Revision 1.188 2003/06/10 20:33:28 cheshire + Crash in SendQueries/SendResponses when no active interfaces + +Revision 1.187 2003/06/10 04:30:44 cheshire + Need to re-probe/re-announce on configuration change +Only interface-specific records were re-probing and re-announcing, not non-specific records. + +Revision 1.186 2003/06/10 04:24:39 cheshire + React when we observe other people query unsuccessfully for a record that's in our cache +Some additional refinements: +Don't try to do this for unicast-response queries +better tracking of Qs and KAs in multi-packet KA lists + +Revision 1.185 2003/06/10 03:52:49 cheshire +Update comments and debug messages + +Revision 1.184 2003/06/10 02:26:39 cheshire + mDNSResponder needs an mDNS_Reconfirm() function +Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines + +Revision 1.183 2003/06/09 18:53:13 cheshire +Simplify some debugf() statements (replaced block of 25 lines with 2 lines) + +Revision 1.182 2003/06/09 18:38:42 cheshire + Need to be more tolerant when there are mDNS proxies on the network +Only issue a correction if the TTL in the proxy packet is less than half the correct value. + +Revision 1.181 2003/06/07 06:45:05 cheshire + No need for multiple machines to all be sending the same queries + +Revision 1.180 2003/06/07 06:31:07 cheshire +Create little four-line helper function "FindIdenticalRecordInCache()" + +Revision 1.179 2003/06/07 06:28:13 cheshire +For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq" + +Revision 1.178 2003/06/07 06:25:12 cheshire +Update some comments + +Revision 1.177 2003/06/07 04:50:53 cheshire + React when we observe other people query unsuccessfully for a record that's in our cache + +Revision 1.176 2003/06/07 04:33:26 cheshire + When query produces zero results, call mDNS_Reconfirm() on any antecedent records +Minor change: Increment/decrement logic for q->CurrentAnswers should be in +CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord() + +Revision 1.175 2003/06/07 04:11:52 cheshire +Minor changes to comments and debug messages + +Revision 1.174 2003/06/07 01:46:38 cheshire + When query produces zero results, call mDNS_Reconfirm() on any antecedent records + +Revision 1.173 2003/06/07 01:22:13 cheshire + mDNSResponder needs an mDNS_Reconfirm() function + +Revision 1.172 2003/06/07 00:59:42 cheshire + Need some randomness to spread queries on the network + +Revision 1.171 2003/06/06 21:41:10 cheshire +For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines + +Revision 1.170 2003/06/06 21:38:55 cheshire +Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we +already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) + +Revision 1.169 2003/06/06 21:35:55 cheshire +Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget +(the target is a domain name, but not necessarily a host name) + +Revision 1.168 2003/06/06 21:33:31 cheshire +Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval" + +Revision 1.167 2003/06/06 21:30:42 cheshire + Don't delay queries for shared record types + +Revision 1.166 2003/06/06 17:20:14 cheshire +For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass +(Global search-and-replace; no functional change to code execution.) + +Revision 1.165 2003/06/04 02:53:21 cheshire +Add some "#pragma warning" lines so it compiles clean on Microsoft compilers + +Revision 1.164 2003/06/04 01:25:33 cheshire + Cannot perform multi-packet known-answer suppression messages +Display time interval between first and subsequent queries + +Revision 1.163 2003/06/03 19:58:14 cheshire + mDNS_DeregisterService() fixes: +When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet. +Guard against a couple of possible mDNS_DeregisterService() race conditions. + +Revision 1.162 2003/06/03 19:30:39 cheshire +Minor addition refinements for + Duplicate registrations not handled as efficiently as they should be + +Revision 1.161 2003/06/03 18:29:03 cheshire +Minor changes to comments and debugf() messages + +Revision 1.160 2003/06/03 05:02:16 cheshire + Duplicate registrations not handled as efficiently as they should be + +Revision 1.159 2003/06/03 03:31:57 cheshire + False self-conflict when there are duplicate registrations on one machine + +Revision 1.158 2003/06/02 22:57:09 cheshire +Minor clarifying changes to comments and log messages; +IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord() + +Revision 1.157 2003/05/31 00:09:49 cheshire + Add ability to discover what services are on a network + +Revision 1.156 2003/05/30 23:56:49 cheshire + Crash after error in mDNS_RegisterService() +Need to set "sr->Extras = mDNSNULL" before returning + +Revision 1.155 2003/05/30 23:48:00 cheshire + Announcements not properly grouped +Due to inconsistent setting of rr->LastAPTime at different places in the +code, announcements were not properly grouped into a single packet. +Fixed by creating a single routine called InitializeLastAPTime(). + +Revision 1.154 2003/05/30 23:38:14 cheshire + Fix error in IPv6 reverse-mapping PTR records +Wrote buffer[32] where it should have said buffer[64] + +Revision 1.153 2003/05/30 19:10:56 cheshire + ConstructServiceName needs to be more restrictive + +Revision 1.152 2003/05/29 22:39:16 cheshire + Don't truncate strings in the middle of a UTF-8 character + +Revision 1.151 2003/05/29 06:35:42 cheshire + mDNSCoreReceiveResponse() purging wrong record + +Revision 1.150 2003/05/29 06:25:45 cheshire + Need to call CheckCacheExpiration() *before* AnswerNewQuestion() + +Revision 1.149 2003/05/29 06:18:39 cheshire + Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv + +Revision 1.148 2003/05/29 06:11:34 cheshire + Report if there appear to be too many "Resolve" callbacks + +Revision 1.147 2003/05/29 06:01:18 cheshire +Change some debugf() calls to LogMsg() calls to help with debugging + +Revision 1.146 2003/05/28 21:00:44 cheshire +Re-enable "immediate answer burst" debugf message + +Revision 1.145 2003/05/28 20:57:44 cheshire + mDNSResponder reports "Cannot perform multi-packet +known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2 +version of mDNSResponder, so for now we should suppress this warning message. + +Revision 1.144 2003/05/28 18:05:12 cheshire + mDNSResponder allows invalid service registrations +Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names + +Revision 1.143 2003/05/28 04:31:29 cheshire + mDNSResponder not sending probes at the prescribed time + +Revision 1.142 2003/05/28 03:13:07 cheshire + mDNSResponder allows invalid service registrations +Require that the transport protocol be _udp or _tcp + +Revision 1.141 2003/05/28 02:19:12 cheshire + Misleading messages generated by iChat +Better fix: Only generate the log message for queries where the TC bit is set. + +Revision 1.140 2003/05/28 01:55:24 cheshire +Minor change to log messages + +Revision 1.139 2003/05/28 01:52:51 cheshire + Misleading messages generated by iChat + +Revision 1.138 2003/05/27 22:35:00 cheshire + mDNS_RegisterInterface needs to retrigger questions + +Revision 1.137 2003/05/27 20:04:33 cheshire + mDNSResponder crash in mDNS_vsnprintf() + +Revision 1.136 2003/05/27 18:50:07 cheshire + mDNS_StartResolveService doesn't inform client of port number changes + +Revision 1.135 2003/05/26 04:57:28 cheshire + Delay queries when there are already answers in the cache + +Revision 1.134 2003/05/26 04:54:54 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead +Accidentally deleted '%' case from the switch statement + +Revision 1.133 2003/05/26 03:21:27 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.132 2003/05/26 03:01:26 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.131 2003/05/26 00:42:05 cheshire + Temporarily include mDNSResponder version in packets + +Revision 1.130 2003/05/24 16:39:48 cheshire + SendResponses also needs to handle multihoming better + +Revision 1.129 2003/05/23 02:15:37 cheshire +Fixed misleading use of the term "duplicate suppression" where it should have +said "known answer suppression". (Duplicate answer suppression is something +different, and duplicate question suppression is yet another thing, so the use +of the completely vague term "duplicate suppression" was particularly bad.) + +Revision 1.128 2003/05/23 01:55:13 cheshire + After name change, mDNSResponder needs to re-probe for name uniqueness + +Revision 1.127 2003/05/23 01:02:15 ksekar +Bug #: : mDNSResponder needs to include unique id in default name + +Revision 1.126 2003/05/22 02:29:22 cheshire + SendQueries needs to handle multihoming better +Complete rewrite of SendQueries. Works much better now :-) + +Revision 1.125 2003/05/22 01:50:45 cheshire +Fix warnings, and improve log messages + +Revision 1.124 2003/05/22 01:41:50 cheshire +DiscardDeregistrations doesn't need InterfaceID parameter + +Revision 1.123 2003/05/22 01:38:55 cheshire +Change bracketing of #pragma mark + +Revision 1.122 2003/05/21 19:59:04 cheshire + ER: Tweak responder's default name conflict behavior +Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character + +Revision 1.121 2003/05/21 17:54:07 ksekar +Bug #: ER: Tweak responder's default name conflict behavior +New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)" + +Revision 1.120 2003/05/19 22:14:14 ksekar + mDNS probe denials/conflicts not detected unless conflict is of the same type + +Revision 1.119 2003/05/16 01:34:10 cheshire +Fix some warnings + +Revision 1.118 2003/05/14 18:48:40 cheshire + mDNSResponder should be smarter about reconfigurations +More minor refinements: +CFSocket.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory +mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away + +Revision 1.117 2003/05/14 07:08:36 cheshire + mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.116 2003/05/14 06:51:56 cheshire + Rendezvous doesn't refresh server info if changed during sleep + +Revision 1.115 2003/05/14 06:44:31 cheshire +Improve debugging message + +Revision 1.114 2003/05/07 01:47:03 cheshire + Also protect against NULL domainlabels + +Revision 1.113 2003/05/07 00:28:18 cheshire + Need to make mDNSResponder more defensive against bad clients + +Revision 1.112 2003/05/06 00:00:46 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.111 2003/05/05 23:42:08 cheshire + Resolves never succeed +Was setting "rr->LastAPTime = timenow - rr->LastAPTime" +instead of "rr->LastAPTime = timenow - rr->ThisAPInterval" + +Revision 1.110 2003/04/30 21:09:59 cheshire + mDNS_vsnprintf needs to be more defensive against invalid domain names + +Revision 1.109 2003/04/26 02:41:56 cheshire + Change timenow from a local variable to a structure member + +Revision 1.108 2003/04/25 01:45:56 cheshire + mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.107 2003/04/25 00:41:31 cheshire + Create single routine PurgeCacheResourceRecord(), to avoid bugs in future + +Revision 1.106 2003/04/22 03:14:45 cheshire + Include Include instrumented mDNSResponder in panther now + +Revision 1.105 2003/04/22 01:07:43 cheshire + DNSServiceRegistrationUpdateRecord should support a default ttl +If TTL parameter is zero, leave record TTL unchanged + +Revision 1.104 2003/04/21 19:15:52 cheshire +Fix some compiler warnings + +Revision 1.103 2003/04/19 02:26:35 cheshire +Bug #: Incorrect goodbye packet after conflict + +Revision 1.102 2003/04/17 03:06:28 cheshire +Bug #: No need to query again when a service goes away +Set UnansweredQueries to 2 when receiving a "goodbye" packet + +Revision 1.101 2003/04/15 20:58:31 jgraessl +Bug #: 3229014 +Added a hash to lookup records in the cache. + +Revision 1.100 2003/04/15 18:53:14 cheshire +Bug #: Bug in ScheduleNextTask +mDNS.c 1.94 incorrectly combined two "if" statements into one. + +Revision 1.99 2003/04/15 18:09:13 jgraessl +Bug #: 3228892 +Reviewed by: Stuart Cheshire +Added code to keep track of when the next cache item will expire so we can +call TidyRRCache only when necessary. + +Revision 1.98 2003/04/03 03:43:55 cheshire + Off-by-one error in probe rate limiting + +Revision 1.97 2003/04/02 01:48:17 cheshire + mDNSResponder sometimes suffers false self-conflicts when it sees its own packets +Additional fix pointed out by Josh: +Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state + +Revision 1.96 2003/04/01 23:58:55 cheshire +Minor comment changes + +Revision 1.95 2003/04/01 23:46:05 cheshire + mDNSResponder can get stuck in infinite loop after many location cycles +mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface +to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second +window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in +FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed. + +Revision 1.94 2003/03/29 01:55:19 cheshire + mDNSResponder sometimes suffers false self-conflicts when it sees its own packets +Solution: Major cleanup of packet timing and conflict handling rules + +Revision 1.93 2003/03/28 01:54:36 cheshire +Minor tidyup of IPv6 (AAAA) code + +Revision 1.92 2003/03/27 03:30:55 cheshire + Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.91 2003/03/15 04:40:36 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.90 2003/03/14 20:26:37 cheshire +Reduce debugging messages (reclassify some "debugf" as "verbosedebugf") + +Revision 1.89 2003/03/12 19:57:50 cheshire +Fixed typo in debug message + +Revision 1.88 2003/03/12 00:17:44 cheshire + GetFreeCacheRR needs to be more willing to throw away recent records + +Revision 1.87 2003/03/11 01:27:20 cheshire +Reduce debugging messages (reclassify some "debugf" as "verbosedebugf") + +Revision 1.86 2003/03/06 20:44:33 cheshire +Comment tidyup + +Revision 1.85 2003/03/05 03:38:35 cheshire +Bug #: 3185731 Bogus error message in console: died or deallocated, but no record of client can be found! +Fixed by leaving client in list after conflict, until client explicitly deallocates + +Revision 1.84 2003/03/05 01:27:30 cheshire +Bug #: 3185482 Different TTL for multicast versus unicast responses +When building unicast responses, record TTLs are capped to 10 seconds + +Revision 1.83 2003/03/04 23:48:52 cheshire +Bug #: 3188865 Double probes after wake from sleep +Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another + +Revision 1.82 2003/03/04 23:38:29 cheshire +Bug #: 3099194 mDNSResponder needs performance improvements +Only set rr->CRActiveQuestion to point to the +currently active representative of a question set + +Revision 1.81 2003/02/21 03:35:34 cheshire +Bug #: 3179007 mDNSResponder needs to include AAAA records in additional answer section + +Revision 1.80 2003/02/21 02:47:53 cheshire +Bug #: 3099194 mDNSResponder needs performance improvements +Several places in the code were calling CacheRRActive(), which searched the entire +question list every time, to see if this cache resource record answers any question. +Instead, we now have a field "CRActiveQuestion" in the resource record structure + +Revision 1.79 2003/02/21 01:54:07 cheshire +Bug #: 3099194 mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.78 2003/02/20 06:48:32 cheshire +Bug #: 3169535 Xserve RAID needs to do interface-specific registrations +Reviewed by: Josh Graessley, Bob Bradley + +Revision 1.77 2003/01/31 03:35:59 cheshire +Bug #: 3147097 mDNSResponder sometimes fails to find the correct results +When there were *two* active questions in the list, they were incorrectly +finding *each other* and *both* being marked as duplicates of another question + +Revision 1.76 2003/01/29 02:46:37 cheshire +Fix for IPv6: +A physical interface is identified solely by its InterfaceID (not by IP and type). +On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts. +In cases where the requested outbound protocol (v4 or v6) is not supported on +that InterfaceID, the platform support layer should simply discard that packet. + +Revision 1.75 2003/01/29 01:47:40 cheshire +Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity + +Revision 1.74 2003/01/28 05:26:25 cheshire +Bug #: 3147097 mDNSResponder sometimes fails to find the correct results +Add 'Active' flag for interfaces + +Revision 1.73 2003/01/28 03:45:12 cheshire +Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)" + +Revision 1.72 2003/01/28 01:49:48 cheshire +Bug #: 3147097 mDNSResponder sometimes fails to find the correct results +FindDuplicateQuestion() was incorrectly finding the question itself in the list, +and incorrectly marking it as a duplicate (of itself), so that it became inactive. + +Revision 1.71 2003/01/28 01:41:44 cheshire +Bug #: 3153091 Race condition when network change causes bad stuff +When an interface goes away, interface-specific questions on that interface become orphaned. +Orphan questions cause HaveQueries to return true, but there's no interface to send them on. +Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions() + +Revision 1.70 2003/01/23 19:00:20 cheshire +Protect against infinite loops in mDNS_Execute + +Revision 1.69 2003/01/21 22:56:32 jgraessl +Bug #: 3124348 service name changes are not properly handled +Submitted by: Stuart Cheshire +Reviewed by: Joshua Graessley +Applying changes for 3124348 to main branch. 3124348 changes went in to a +branch for SU. + +Revision 1.68 2003/01/17 04:09:27 cheshire +Bug #: 3141038 mDNSResponder Resolves are unreliable on multi-homed hosts + +Revision 1.67 2003/01/17 03:56:45 cheshire +Default 24-hour TTL is far too long. Changing to two hours. + +Revision 1.66 2003/01/13 23:49:41 jgraessl +Merged changes for the following fixes in to top of tree: +3086540 computer name changes not handled properly +3124348 service name changes are not properly handled +3124352 announcements sent in pairs, failing chattiness test + +Revision 1.65 2002/12/23 22:13:28 jgraessl +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.64 2002/11/26 20:49:06 cheshire +Bug #: 3104543 RFC 1123 allows the first character of a name label to be either a letter or a digit + +Revision 1.63 2002/09/21 20:44:49 zarzycki +Added APSL info + +Revision 1.62 2002/09/20 03:25:37 cheshire +Fix some compiler warnings + +Revision 1.61 2002/09/20 01:05:24 cheshire +Don't kill the Extras list in mDNS_DeregisterService() + +Revision 1.60 2002/09/19 23:47:35 cheshire +Added mDNS_RegisterNoSuchService() function for assertion of non-existance +of a particular named service + +Revision 1.59 2002/09/19 21:25:34 cheshire +mDNS_snprintf() doesn't need to be in a separate file + +Revision 1.58 2002/09/19 04:20:43 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.57 2002/09/17 01:07:08 cheshire +Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() + +Revision 1.56 2002/09/16 19:44:17 cheshire +Merge in license terms from Quinn's copy, in preparation for Darwin release +*/ + +#define TEST_LOCALONLY_FOR_EVERYTHING 0 #include "mDNSClientAPI.h" // Defines the interface provided to the client layer above #include "mDNSPlatformFunctions.h" // Defines the interface required of the supporting layer below -#include "mDNSsprintf.h" - -extern void LogErrorMessage(const char *format, ...); +// Disable certain benign warnings with Microsoft compilers #if(defined(_MSC_VER)) - // Disable warnings about Microsoft Visual Studio/C++ not understanding "pragma unused" - #pragma warning( disable:4068 ) + // Disable "conditional expression is constant" warning for debug macros. + // Otherwise, this generates warnings for the perfectly natural construct "while(1)" + // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know + #pragma warning(disable:4127) + + // Disable "const object should be initialized" + // We know that static/globals are defined to be zeroed in ANSI C, and to avoid this warning would require some + // *really* ugly chunk of zeroes and curly braces to initialize zeroRR and mDNSprintf_format_default to all zeroes + #pragma warning(disable:4132) + + // Disable "assignment within conditional expression". + // Other compilers understand the convention that if you place the assignment expression within an extra pair + // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary. + // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal + // to the compiler that the assignment is intentional, we have to just turn this warning off completely. + #pragma warning(disable:4706) #endif // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - DNS Protocol Constants #endif @@ -83,28 +1047,45 @@ typedef enum } DNS_Flags; // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Program Constants #endif -mDNSexport const ResourceRecord zeroRR = { 0 }; -mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; -mDNSexport const mDNSIPAddr zeroIPAddr = { { 0 } }; -mDNSexport const mDNSIPAddr onesIPAddr = { { 255, 255, 255, 255 } }; +mDNSexport const ResourceRecord zeroRR; +mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; +mDNSexport const mDNSv4Addr zeroIPAddr = { { 0 } }; +mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; +mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } }; +mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }; +mDNSlocal const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; + +mDNSexport const mDNSInterfaceID mDNSInterface_Any = { 0 }; +mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = { (mDNSInterfaceID)~0 }; #define UnicastDNSPortAsNumber 53 #define MulticastDNSPortAsNumber 5353 mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } }; -mDNSexport const mDNSIPAddr AllDNSLinkGroup = { { 224, 0, 0, 251 } }; -mDNSexport const mDNSIPAddr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; +mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; +mDNSexport const mDNSv4Addr AllDNSLinkGroup = { { 224, 0, 0, 251 } }; +mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } }; +mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; +mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; static const mDNSOpaque16 zeroID = { { 0, 0 } }; static const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; static const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; #define zeroDomainNamePtr ((domainname*)"") +// Any records bigger than this are considered 'large' records +#define SmallRecordLimit 1024 + +#define kDefaultTTLforUnique 240 +#define kDefaultTTLforShared (2*3600) + +#define kMaxUpdateCredits 10 + static const char *const mDNS_DomainTypeNames[] = { "_browse._mdns._udp.local.", @@ -113,60 +1094,413 @@ static const char *const mDNS_DomainTypeNames[] = "_default._register._mdns._udp.local." }; +#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC).c, (DST).c, DomainNameLength(&(SRC))) + // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Specialized mDNS version of vsnprintf +#endif + +static const struct mDNSprintf_format + { + unsigned leftJustify : 1; + unsigned forceSign : 1; + unsigned zeroPad : 1; + unsigned havePrecision : 1; + unsigned hSize : 1; + unsigned lSize : 1; + char altForm; + char sign; // +, - or space + unsigned int fieldWidth; + unsigned int precision; + } mDNSprintf_format_default; + +mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) + { + mDNSu32 nwritten = 0; + int c; + buflen--; // Pre-reserve one space in the buffer for the terminating nul + + for (c = *fmt; c != 0; c = *++fmt) + { + if (c != '%') + { + *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + } + else + { + unsigned int i=0, j; + // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for + // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. + // The size needs to be enough for a 256-byte domain name plus some error text. + #define mDNS_VACB_Size 300 + char mDNS_VACB[mDNS_VACB_Size]; + #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) + #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s)) + char *s = mDNS_VACB_Lim, *digits; + struct mDNSprintf_format F = mDNSprintf_format_default; + + while (1) // decode flags + { + c = *++fmt; + if (c == '-') F.leftJustify = 1; + else if (c == '+') F.forceSign = 1; + else if (c == ' ') F.sign = ' '; + else if (c == '#') F.altForm++; + else if (c == '0') F.zeroPad = 1; + else break; + } + + if (c == '*') // decode field width + { + int f = va_arg(arg, int); + if (f < 0) { f = -f; F.leftJustify = 1; } + F.fieldWidth = (unsigned int)f; + c = *++fmt; + } + else + { + for (; c >= '0' && c <= '9'; c = *++fmt) + F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); + } + + if (c == '.') // decode precision + { + if ((c = *++fmt) == '*') + { F.precision = va_arg(arg, unsigned int); c = *++fmt; } + else for (; c >= '0' && c <= '9'; c = *++fmt) + F.precision = (10 * F.precision) + (c - '0'); + F.havePrecision = 1; + } + + if (F.leftJustify) F.zeroPad = 0; + + conv: + switch (c) // perform appropriate conversion + { + unsigned long n; + case 'h' : F.hSize = 1; c = *++fmt; goto conv; + case 'l' : // fall through + case 'L' : F.lSize = 1; c = *++fmt; goto conv; + case 'd' : + case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long); + else n = (unsigned long)va_arg(arg, int); + if (F.hSize) n = (short) n; + if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } + else if (F.forceSign) F.sign = '+'; + goto decimal; + case 'u' : if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + F.sign = 0; + goto decimal; + decimal: if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.sign) --F.precision; + } + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0'); + for (; i < F.precision; i++) *--s = '0'; + if (F.sign) { *--s = F.sign; i++; } + break; + + case 'o' : if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + if (!F.havePrecision) + { + if (F.zeroPad) F.precision = F.fieldWidth; + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0'); + if (F.altForm && i && *s != '0') { *--s = '0'; i++; } + for (; i < F.precision; i++) *--s = '0'; + break; + + case 'a' : { + unsigned char *a = va_arg(arg, unsigned char *); + if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else + { + unsigned short *w = (unsigned short *)a; + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (F.altForm) + { + mDNSAddr *ip = (mDNSAddr*)a; + a = (unsigned char *)&ip->ip.v4; + w = (unsigned short *)&ip->ip.v6; + switch (ip->type) + { + case mDNSAddrType_IPv4: F.precision = 4; break; + case mDNSAddrType_IPv6: F.precision = 16; break; + default: F.precision = 0; break; + } + } + switch (F.precision) + { + case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d", + a[0], a[1], a[2], a[3]); break; + case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", + a[0], a[1], a[2], a[3], a[4], a[5]); break; + case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X", + w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]); break; + default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size " + "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; + } + } + } + break; + + case 'p' : F.havePrecision = F.lSize = 1; + F.precision = 8; + case 'X' : digits = "0123456789ABCDEF"; + goto hexadecimal; + case 'x' : digits = "0123456789abcdef"; + hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.altForm) F.precision -= 2; + } + if (F.precision < 1) F.precision = 1; + } + if (F.precision > mDNS_VACB_Size - 1) + F.precision = mDNS_VACB_Size - 1; + for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; + for (; i < F.precision; i++) *--s = '0'; + if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } + break; + + case 'c' : *--s = (char)va_arg(arg, int); i = 1; break; + + case 's' : s = va_arg(arg, char *); + if (!s) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else switch (F.altForm) + { + case 0: { char *a=s; i=0; while(*a++) i++; break; } // C string + case 1: i = (unsigned char) *s++; break; // Pascal string + case 2: { // DNS label-sequence name + unsigned char *a = (unsigned char *)s; + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (*a == 0) *s++ = '.'; // Special case for root DNS name + while (*a) + { + if (*a > 63) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<>", *a); break; } + if (s + *a >= &mDNS_VACB[254]) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<>"); break; } + s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%#s.", a); + a += 1 + *a; + } + i = (mDNSu32)(s - mDNS_VACB); + s = mDNS_VACB; // Reset s back to the start of the buffer + break; + } + } + if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character + { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } + break; + + case 'n' : s = va_arg(arg, char *); + if (F.hSize) * (short *) s = (short)nwritten; + else if (F.lSize) * (long *) s = (long)nwritten; + else * (int *) s = (int)nwritten; + continue; + + default: s = mDNS_VACB; + i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<>", c); + + case '%' : *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + break; + } + + if (i < F.fieldWidth && !F.leftJustify) // Pad on the left + do { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } while (i < --F.fieldWidth); + + if (i > buflen - nwritten) // Make sure we don't truncate in the middle of a UTF-8 character + { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } + for (j=0; j= buflen) goto exit; + + for (; i < F.fieldWidth; i++) // Pad on the right + { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } + } + } + exit: + *sbuffer++ = 0; + return(nwritten); + } + +mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) + { + mDNSu32 length; + + va_list ptr; + va_start(ptr,fmt); + length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr); + va_end(ptr); + + return(length); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - General Utility Functions #endif -#if DEBUGBREAKS -mDNSlocal char *DNSTypeName(mDNSu16 rrtype) +mDNSexport char *DNSTypeName(mDNSu16 rrtype) + { + switch (rrtype) + { + case kDNSType_A: return("Addr"); + case kDNSType_CNAME:return("CNAME"); + case kDNSType_NULL: return("NULL"); + case kDNSType_PTR: return("PTR"); + case kDNSType_HINFO:return("HINFO"); + case kDNSType_TXT: return("TXT"); + case kDNSType_AAAA: return("AAAA"); + case kDNSType_SRV: return("SRV"); + case kDNSQType_ANY: return("ANY"); + default: { + static char buffer[16]; + mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype); + return(buffer); + } + } + } + +mDNSexport char *GetRRDisplayString_rdb(mDNS *const m, const ResourceRecord *rr, RDataBody *rd) + { + char *ptr = m->MsgBuffer; + mDNSu32 length = mDNS_snprintf(m->MsgBuffer, 79, "%4d %##s %s ", rr->rdlength, rr->name.c, DNSTypeName(rr->rrtype)); + switch (rr->rrtype) + { + case kDNSType_A: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%.4a", &rd->ip); break; + case kDNSType_CNAME:// Same as PTR + case kDNSType_PTR: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->name); break; + case kDNSType_HINFO:// Display this the same as TXT (just show first string) + case kDNSType_TXT: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%#s", rd->txt.c); break; + case kDNSType_AAAA: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%.16a", &rd->ipv6); break; + case kDNSType_SRV: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->srv.target); break; + default: mDNS_snprintf(m->MsgBuffer+length, 79-length, "RDLen %d: %s", + rr->rdlength, rd->data); break; + } + for (ptr = m->MsgBuffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.'; + return(m->MsgBuffer); + } + +mDNSlocal mDNSu32 mDNSRandom(mDNSu32 max) + { + static mDNSu32 seed = 0; + mDNSu32 mask = 1; + + if (!seed) seed = (mDNSu32)mDNSPlatformTimeNow(); + while (mask < max) mask = (mask << 1) | 1; + do seed = seed * 21 + 1; while ((seed & mask) > max); + return (seed & mask); + } + +#define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger) +#define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3]) + +#define mDNSIPv4AddressIsZero(A) mDNSSameIPv4Address((A), zeroIPAddr) +#define mDNSIPv6AddressIsZero(A) mDNSSameIPv6Address((A), zerov6Addr) + +#define mDNSIPv4AddressIsOnes(A) mDNSSameIPv4Address((A), onesIPv4Addr) +#define mDNSIPv6AddressIsOnes(A) mDNSSameIPv6Address((A), onesIPv6Addr) + +#define mDNSAddressIsZero(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero((X)->ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsZero((X)->ip.v6)) ) + +#define mDNSAddressIsOnes(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes((X)->ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsOnes((X)->ip.v6)) ) + +#define mDNSAddressIsValid(X) ( \ + ((X)->type == mDNSAddrType_IPv4) ? !(mDNSIPv4AddressIsZero((X)->ip.v4) || mDNSIPv4AddressIsOnes((X)->ip.v4)) : \ + ((X)->type == mDNSAddrType_IPv6) ? !(mDNSIPv6AddressIsZero((X)->ip.v6) || mDNSIPv6AddressIsOnes((X)->ip.v6)) : mDNSfalse) + +mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2) + { + if (ip1->type == ip2->type) + { + switch (ip1->type) + { + case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4)); + case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6)); + } + } + return(mDNSfalse); + } + +mDNSlocal mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip) + { + switch(ip->type) + { + case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroup.NotAnInteger); + case mDNSAddrType_IPv6: return(mDNSBool)(ip->ip.v6.l[0] == AllDNSLinkGroupv6.l[0] && + ip->ip.v6.l[1] == AllDNSLinkGroupv6.l[1] && + ip->ip.v6.l[2] == AllDNSLinkGroupv6.l[2] && + ip->ip.v6.l[3] == AllDNSLinkGroupv6.l[3] ); + default: return(mDNSfalse); + } + } + +mDNSlocal const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf) + { + while (intf && !intf->InterfaceActive) intf = intf->next; + return(intf); + } + +mDNSlocal mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf) { - switch (rrtype) - { - case kDNSType_A: return("Address"); - case kDNSType_CNAME:return("CNAME"); - case kDNSType_PTR: return("PTR"); - case kDNSType_TXT: return("TXT"); - case kDNSType_SRV: return("SRV"); - default: { - static char buffer[16]; - mDNS_sprintf(buffer, "(%d)", rrtype); - return(buffer); - } - } + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + if (next) return(next->InterfaceID); else return(mDNSNULL); } -#endif -mDNSlocal mDNSu32 mDNSRandom(mDNSu32 max) +#define InitialQuestionInterval (mDNSPlatformOneSecond/2) +#define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) +#define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0) + +mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) { - static mDNSu32 seed = 1; - mDNSu32 mask = 1; - while (mask < max) mask = (mask << 1) | 1; - do seed = seed * 21 + 1; while ((seed & mask) > max); - return (seed & mask); + if (ActiveQuestion(q)) + if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0) + m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval); } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Domain Name Utility Functions #endif -// Returns length of a domain name INCLUDING the byte for the final null label -// i.e. for the root label "." it returns one -// For the FQDN "com." it returns 5 (length, three data bytes, final zero) -mDNSexport mDNSu32 DomainNameLength(const domainname *const name) - { - const mDNSu8 *src = name->c; - while (*src) - { - if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1); - src += 1 + *src; - if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); - } - return((mDNSu32)(src - name->c + 1)); - } +#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') +#define mDNSIsUpperCase(X) ((X) >= 'A' && (X) <= 'Z') +#define mDNSIsLowerCase(X) ((X) >= 'a' && (X) <= 'z') +#define mdnsIsLetter(X) (mDNSIsUpperCase(X) || mDNSIsLowerCase(X)) mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b) { @@ -181,8 +1515,8 @@ mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b) { mDNSu8 ac = *a++; mDNSu8 bc = *b++; - if (ac >= 'A' && ac <= 'Z') ac += 'a' - 'A'; - if (bc >= 'A' && bc <= 'Z') bc += 'a' - 'A'; + if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; + if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; if (ac != bc) return(mDNSfalse); } return(mDNStrue); @@ -206,6 +1540,23 @@ mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname return(mDNStrue); } +// Returns length of a domain name INCLUDING the byte for the final null label +// i.e. for the root label "." it returns one +// For the FQDN "com." it returns 5 (length byte, three data bytes, final zero) +// Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME) +// If the given domainname is invalid, result is 256 +mDNSexport mDNSu16 DomainNameLength(const domainname *const name) + { + const mDNSu8 *src = name->c; + while (*src) + { + if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1); + src += 1 + *src; + if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); + } + return((mDNSu16)(src - name->c + 1)); + } + // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte // for the final null label i.e. for the root label "." it returns one. // E.g. for the FQDN "foo.com." it returns 9 @@ -215,175 +1566,168 @@ mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname // of the child name, plus TWO bytes for the compression pointer. // E.g. for the name "foo.com." with parent "com.", it returns 6 // (length, three data bytes, two-byte compression pointer). -mDNSlocal mDNSu32 CompressedDomainNameLength(const domainname *const name, const domainname *parent) +mDNSlocal mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent) { const mDNSu8 *src = name->c; if (parent && parent->c[0] == 0) parent = mDNSNULL; while (*src) { if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1); - if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu32)(src - name->c + 2)); + if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu16)(src - name->c + 2)); src += 1 + *src; if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); } - return((mDNSu32)(src - name->c + 1)); + return((mDNSu16)(src - name->c + 1)); } -mDNSexport void AppendDomainLabelToName(domainname *const name, const domainlabel *const label) - { - int i; - mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; - const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME; - if (ptr + 1 + label->c[0] + 1 >= lim) return; - for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; - *ptr++ = 0; // Put the null root label on the end - } - -// AppendStringLabelToName appends a single label to an existing (possibly empty) domainname. +// AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname. // The C string contains the label as-is, with no escaping, etc. // Any dots in the name are literal dots, not label separators -mDNSexport void AppendStringLabelToName(domainname *const name, const char *cstr) - { - mDNSu8 *lengthbyte; - mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; - const mDNSu8 *lim = name->c + MAX_DOMAIN_NAME - 1; - if (lim > ptr + MAX_DOMAIN_LABEL + 1) - lim = ptr + MAX_DOMAIN_LABEL + 1; - lengthbyte = ptr++; - while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; - *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); - *ptr++ = 0; // Put the null root label on the end - } - -mDNSexport void AppendDomainNameToName(domainname *const name, const domainname *const append) - { - int i; - mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; - const mDNSu8 *src = append->c; - const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME; - while(src[0]) - { - if (ptr + 1 + src[0] + 1 >= lim) return; - for (i=0; i<=src[0]; i++) *ptr++ = src[i]; - *ptr = 0; // Put the null root label on the end - src += i; - } - } - -// AppendStringNameToName appends zero or more labels to an existing (possibly empty) domainname. -// The C string contains the labels separated by dots, but otherwise as-is, with no escaping, etc. -mDNSexport void AppendStringNameToName(domainname *const name, const char *cstr) +// If successful, AppendLiteralLabelString returns a pointer to the next unused byte +// in the domainname bufer (i.e., the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) +// AppendLiteralLabelString returns mDNSNULL. +mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr) { - mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; // Find end of current name - const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Find limit of how much we can add - while (*cstr) - { - mDNSu8 *const lengthbyte = ptr++; - const mDNSu8 *const lim2 = ptr + MAX_DOMAIN_LABEL; - const mDNSu8 *const lim3 = (lim < lim2) ? lim : lim2; - while (*cstr && *cstr != '.' && ptr < lim3) *ptr++ = (mDNSu8)*cstr++; - *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); - if (*cstr == '.') cstr++; - } + mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name + const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) + const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL; + const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2; + mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go - *ptr++ = 0; // Put the null root label on the end - } - -//#define IsThreeDigit(X) (IsDigit((X)[1]) && IsDigit((X)[2]) && IsDigit((X)[3])) -//#define ValidEscape(X) (X)[0] == '\\' && ((X)[1] == '\\' || (X)[1] == '\\' || IsThreeDigit(X)) - -#define mdnsIsLetter(X) (((X) >= 'A' && (X) <= 'Z') || ((X) >= 'a' && (X) <= 'z')) -#define mdnsIsDigit(X) (((X) >= '0' && (X) <= '9')) -#define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || \ - ((notfirst) && (mdnsIsDigit(X) || ((notlast) && (X) == '-'))) ) - -mDNSexport void ConvertCStringToDomainLabel(const char *src, domainlabel *label) - { - mDNSu8 * ptr = label->c + 1; // Where we're putting it - const mDNSu8 *const limit = ptr + MAX_DOMAIN_LABEL; // The maximum we can put - while (*src && ptr < limit) // While we have characters in the label... - { - mDNSu8 c = (mDNSu8)*src++; // Read the character - if (c == '\\') // If escape character, check next character - { - if (*src == '\\' || *src == '.') // If a second escape, or a dot, - c = (mDNSu8)*src++; // just use the second character - else if (mdnsIsDigit(src[0]) && mdnsIsDigit(src[1]) && mdnsIsDigit(src[2])) - { // else, if three decimal digits, - int v0 = src[0] - '0'; // then interpret as three-digit decimal - int v1 = src[1] - '0'; - int v2 = src[2] - '0'; - int val = v0 * 100 + v1 * 10 + v2; - if (val <= 255) { c = (mDNSu8)val; src += 3; } // If valid value, use it - } - } - *ptr++ = c; // Write the character - } - label->c[0] = (mDNSu8)(ptr - label->c - 1); + while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data + *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte + *ptr++ = 0; // Put the null root label on the end + if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input + else return(ptr); // Success: return new value of ptr } -mDNSexport mDNSu8 *ConvertCStringToDomainName(const char *const cstr, domainname *name) +// AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname. +// The C string is in conventional DNS syntax: +// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots. +// If successful, AppendDNSNameString returns a pointer to the next unused byte +// in the domainname bufer (i.e., the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) +// AppendDNSNameString returns mDNSNULL. +mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstr) { - const mDNSu8 *src = (const mDNSu8 *)cstr; // C string we're reading - mDNSu8 *ptr = name->c; // Where we're putting it - const mDNSu8 *const limit = ptr + MAX_DOMAIN_NAME; // The maximum we can put - - while (*src && ptr < limit) // While more characters, and space to put them... + mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name + const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) + while (*cstr && ptr < lim) // While more characters, and space to put them... { mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go - while (*src && *src != '.' && ptr < limit) // While we have characters in the label... + while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label... { - mDNSu8 c = *src++; // Read the character + mDNSu8 c = (mDNSu8)*cstr++; // Read the character if (c == '\\') // If escape character, check next character { - if (*src == '\\' || *src == '.') // If a second escape, or a dot, - c = *src++; // just use the second character - else if (mdnsIsDigit(src[0]) && mdnsIsDigit(src[1]) && mdnsIsDigit(src[2])) + if (*cstr == '\\' || *cstr == '.') // If a second escape, or a dot, + c = (mDNSu8)*cstr++; // just use the second character + else if (mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1]) && mdnsIsDigit(cstr[2])) { // else, if three decimal digits, - int v0 = src[0] - '0'; // then interpret as three-digit decimal - int v1 = src[1] - '0'; - int v2 = src[2] - '0'; + int v0 = cstr[0] - '0'; // then interpret as three-digit decimal + int v1 = cstr[1] - '0'; + int v2 = cstr[2] - '0'; int val = v0 * 100 + v1 * 10 + v2; - if (val <= 255) { c = (mDNSu8)val; src += 3; } // If valid value, use it + if (val <= 255) { c = (mDNSu8)val; cstr += 3; } // If valid value, use it } } *ptr++ = c; // Write the character } - if (*src) src++; // Skip over the trailing dot (if present) - if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort - *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); + if (*cstr) cstr++; // Skip over the trailing dot (if present) + if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort + return(mDNSNULL); + *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte } - if (ptr < limit) // If we didn't run out of space + *ptr++ = 0; // Put the null root label on the end + if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input + else return(ptr); // Success: return new value of ptr + } + +// AppendDomainLabel appends a single label to a name. +// If successful, AppendDomainLabel returns a pointer to the next unused byte +// in the domainname bufer (i.e., the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) +// AppendDomainLabel returns mDNSNULL. +mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label) + { + int i; + mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; + + // Check label is legal + if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL); + + // Check that ptr + length byte + data bytes + final zero does not exceed our limit + if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL); + + for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data + *ptr++ = 0; // Put the null root label on the end + return(ptr); + } + +mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append) + { + mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name + const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) + const mDNSu8 * src = append->c; + while(src[0]) { - *ptr++ = 0; // Put the final root label - return(ptr); // and return + int i; + if (ptr + 1 + src[0] > lim) return(mDNSNULL); + for (i=0; i<=src[0]; i++) *ptr++ = src[i]; + *ptr = 0; // Put the null root label on the end + src += i; } + return(ptr); + } - return(mDNSNULL); +// MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping). +// If successful, MakeDomainLabelFromLiteralString returns mDNStrue. +// If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then +// MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse. +// In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored. +// In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result. +mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr) + { + mDNSu8 * ptr = label->c + 1; // Where we're putting it + const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put + while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label + label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte + return(*cstr == 0); // Return mDNStrue if we successfully consumed all input } -//#define convertCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), -1) -//#define convertescapedCstringtodomainname(C,D) convertCstringtodomainname_withescape((C), (D), '\\') +// MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string. +// The C string is in conventional DNS syntax: +// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots. +// If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte +// in the domainname bufer (i.e., the next byte after the terminating zero). +// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes) +// MakeDomainNameFromDNSNameString returns mDNSNULL. +mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr) + { + name->c[0] = 0; // Make an empty domain name + return(AppendDNSNameString(name, cstr)); // And then add this string to it + } mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) { const mDNSu8 * src = label->c; // Domain label we're reading const mDNSu8 len = *src++; // Read length of this (non-null) label - const mDNSu8 *const end = src + len; // Work out where the label ends - if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort + const mDNSu8 *const end = src + len; // Work out where the label ends + if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort while (src < end) // While we have characters in the label { mDNSu8 c = *src++; if (esc) { - if (c == '.') // If character is a dot, + if (c == '.' || c == esc) // If character is a dot or the escape character *ptr++ = esc; // Output escape character else if (c <= ' ') // If non-printing ascii, { // Output decimal escape sequence *ptr++ = esc; - *ptr++ = (char) ('0' + (c / 100) ); - *ptr++ = (char) ('0' + (c / 10) % 10); + *ptr++ = (char) ('0' + (c / 100) ); + *ptr++ = (char) ('0' + (c / 10) % 10); c = (mDNSu8)('0' + (c ) % 10); } } @@ -422,6 +1766,8 @@ mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const n // RFC 1034 rules: // Host names must start with a letter, end with a letter or digit, // and have as interior characters only letters, digits, and hyphen. +// This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit +#define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || mdnsIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') ) mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel) { @@ -447,45 +1793,64 @@ mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], do } mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, - const domainlabel *const name, const domainname *const type, const domainname *const domain) + const domainlabel *name, const domainname *type, const domainname *const domain) { int i, len; mDNSu8 *dst = fqdn->c; - mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME; const mDNSu8 *src; + const char *errormsg; + + // In the case where there is no name (and ONLY in that case), + // a single-label subtype is allowed as the first label of a three-part "type" + if (!name) + { + const mDNSu8 *s2 = type->c + 1 + type->c[0]; + if (type->c[0] > 0 && type->c[0] < 0x40 && + s2[0] > 0 && s2[0] < 0x40 && + s2[1+s2[0]] > 0 && s2[1+s2[0]] < 0x40) + { + name = (domainlabel *)type; + type = (domainname *)s2; + } + } - if (name) + if (name && name->c[0]) { src = name->c; // Put the service name into the domain name len = *src; - if (len >= 0x40) { debugf("ConstructServiceName: service name too long"); return(0); } + if (len >= 0x40) { errormsg="Service instance name too long"; goto fail; } for (i=0; i<=len; i++) *dst++ = *src++; } + else + name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below src = type->c; // Put the service type into the domain name len = *src; - if (len == 0 || len >= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); } - if (dst + 1 + len + 1 >= max) { debugf("ConstructServiceName: service type too long"); return(0); } + if (len < 2 || len >= 0x40) { errormsg="Invalid service application protocol name"; goto fail; } + if (src[1] != '_') { errormsg="Service application protocol name must begin with underscore"; goto fail; } + for (i=2; i<=len; i++) + if (!mdnsIsLetter(src[i]) && !mdnsIsDigit(src[i]) && src[i] != '-' && src[i] != '_') + { errormsg="Service application protocol name must contain only letters, digits, and hyphens"; goto fail; } for (i=0; i<=len; i++) *dst++ = *src++; len = *src; - if (len == 0 || len >= 0x40) { debugf("ConstructServiceName: Invalid service name"); return(0); } - if (dst + 1 + len + 1 >= max) { debugf("ConstructServiceName: service type too long"); return(0); } + //if (len == 0 || len >= 0x40) { errormsg="Invalid service transport protocol name"; goto fail; } + if (!(len == 4 && src[1] == '_' && + (((src[2] | 0x20) == 'u' && (src[3] | 0x20) == 'd') || ((src[2] | 0x20) == 't' && (src[3] | 0x20) == 'c')) && + (src[4] | 0x20) == 'p')) + { errormsg="Service transport protocol name must be _udp or _tcp"; goto fail; } for (i=0; i<=len; i++) *dst++ = *src++; - if (*src) { debugf("ConstructServiceName: Service type must have only two labels"); return(0); } - - src = domain->c; // Put the service domain into the domain name - while (*src) - { - len = *src; - if (dst + 1 + len + 1 >= max) - { debugf("ConstructServiceName: service domain too long"); return(0); } - for (i=0; i<=len; i++) *dst++ = *src++; - } + if (*src) { errormsg="Service type must have only two labels"; goto fail; } - *dst++ = 0; // Put the null root label on the end + *dst = 0; + dst = AppendDomainName(fqdn, domain); + if (!dst) { errormsg="Service domain too long"; goto fail; } return(dst); + +fail: + LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c); + return(mDNSNULL); } mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn, @@ -526,26 +1891,80 @@ mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn, return(mDNStrue); } -mDNSlocal void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) +// Returns true if a rich text label ends in " (nnn)", or if an RFC 1034 +// name ends in "-nnn", where n is some decimal number. +mDNSlocal mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText) + { + mDNSu16 l = name->c[0]; + + if (RichText) + { + if (l < 4) return mDNSfalse; // Need at least " (2)" + if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')' + if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit + l--; + while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits + return (name->c[l] == '(' && name->c[l - 1] == ' '); + } + else + { + if (l < 2) return mDNSfalse; // Need at least "-2" + if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit + l--; + while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits + return (name->c[l] == '-'); + } + } + +// removes an auto-generated suffix (appended on a name collision) from a label. caller is +// responsible for ensuring that the label does indeed contain a suffix. returns the number +// from the suffix that was removed. +mDNSlocal mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText) { - long val = 0, multiplier = 1, divisor = 1, digits = 1; + mDNSu32 val = 0, multiplier = 1; + + // Chop closing parentheses from RichText suffix + if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--; // Get any existing numerical suffix off the name while (mdnsIsDigit(name->c[name->c[0]])) { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; } - - // If existing suffix, increment it, else start by renaming "Foo" as "Foo2" - if (multiplier > 1 && val < 999999) val++; else val = 2; - // Can only add spaces to rich text names, not RFC 1034 names - if (RichText && name->c[name->c[0]] != ' ' && name->c[0] < MAX_DOMAIN_LABEL) - name->c[++name->c[0]] = ' '; + // Chop opening parentheses or dash from suffix + if (RichText) + { + if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2; + } + else + { + if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1; + } + + return(val); + } + +// appends a numerical suffix to a label, with the number following a whitespace and enclosed +// in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label). +mDNSlocal void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText) + { + mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 3 characters ("-2") + if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)") - while (val >= divisor * 10) - { divisor *= 10; digits++; } + // Truncate trailing spaces from RichText names + if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--; + + while (val >= divisor * 10) { divisor *= 10; chars++; } + + if (name->c[0] > (mDNSu8)(MAX_DOMAIN_LABEL - chars)) + { + name->c[0] = (mDNSu8)(MAX_DOMAIN_LABEL - chars); + // If the following character is a UTF-8 continuation character, + // we just chopped a multi-byte UTF-8 character in the middle, so strip back to a safe truncation point + while (name->c[0] > 0 && (name->c[name->c[0]+1] & 0xC0) == 0x80) name->c[0]--; + } - if (name->c[0] > (mDNSu8)(MAX_DOMAIN_LABEL - digits)) - name->c[0] = (mDNSu8)(MAX_DOMAIN_LABEL - digits); + if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; } + else { name->c[++name->c[0]] = '-'; } while (divisor) { @@ -553,117 +1972,195 @@ mDNSlocal void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) val %= divisor; divisor /= 10; } + + if (RichText) name->c[++name->c[0]] = ')'; + } + +mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) + { + mDNSu32 val = 0; + + if (LabelContainsSuffix(name, RichText)) + val = RemoveLabelSuffix(name, RichText); + + // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate. + // If existing suffix in the range 2-9, increment it. + // If we've had ten conflicts already, there are probably too many hosts trying to use the same name, + // so add a random increment to improve the chances of finding an available name next time. + if (val == 0) val = 2; + else if (val < 10) val++; + else val += 1 + mDNSRandom(99); + + AppendLabelSuffix(name, val, RichText); } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Resource Record Utility Functions #endif -#define ResourceRecordIsValidAnswer(RR) ( ((RR)-> RecordType & kDNSRecordTypeActiveMask) && \ - ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->RecordType & kDNSRecordTypeActiveMask)) && \ - ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->RecordType & kDNSRecordTypeActiveMask)) && \ - ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->RecordType & kDNSRecordTypeActiveMask)) ) +#define RRIsAddressType(RR) ((RR)->resrec.rrtype == kDNSType_A || (RR)->resrec.rrtype == kDNSType_AAAA) -#define ResourceRecordIsValidInterfaceAnswer(RR, I) \ +#define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \ + ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ + ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \ + ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) ) + +#define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \ (ResourceRecordIsValidAnswer(RR) && \ - ((RR)->InterfaceAddr.NotAnInteger == 0 || (RR)->InterfaceAddr.NotAnInteger == (I).NotAnInteger)) + ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID))) + +#define RRUniqueOrKnownUnique(RR) ((RR)->RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeKnownUnique)) #define DefaultProbeCountForTypeUnique ((mDNSu8)3) #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) -#define DefaultAnnounceCountForTypeShared ((mDNSu8)10) -#define DefaultAnnounceCountForTypeUnique ((mDNSu8)2) - -#define DefaultAnnounceCountForRecordType(X) ((X) == kDNSRecordTypeShared ? DefaultAnnounceCountForTypeShared : \ - (X) == kDNSRecordTypeUnique ? DefaultAnnounceCountForTypeUnique : \ - (X) == kDNSRecordTypeVerified ? DefaultAnnounceCountForTypeUnique : \ - (X) == kDNSRecordTypeKnownUnique ? DefaultAnnounceCountForTypeUnique : (mDNSu8)0) - -#define DefaultSendIntervalForRecordType(X) ((X) == kDNSRecordTypeShared ? mDNSPlatformOneSecond : \ - (X) == kDNSRecordTypeUnique ? mDNSPlatformOneSecond/4 : \ - (X) == kDNSRecordTypeVerified ? mDNSPlatformOneSecond/4 : 0) - -#define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && time - (RR)->NextSendTime >= 0) -#define TimeToSendThisRecord(RR,time) \ - ((TimeToAnnounceThisRecord(RR,time) || (RR)->SendPriority) && ResourceRecordIsValidAnswer(RR)) - -mDNSlocal mDNSBool SameRData(const mDNSu16 rrtype, const RData *const r1, const RData *const r2) +// For records that have *never* been announced on the wire, their AnnounceCount will be set to InitialAnnounceCount (10). +// When de-registering these records we do not need to send any goodbye packet because we never announced them in the first +// place. If AnnounceCount is less than InitialAnnounceCount that means we have announced them at least once, so a goodbye +// packet is needed. For this reason, if we ever reset AnnounceCount (e.g. after an interface change) we set it to +// ReannounceCount (9), not InitialAnnounceCount. If we were to reset AnnounceCount back to InitialAnnounceCount that would +// imply that the record had never been announced on the wire (which is false) and if the client were then to immediately +// deregister that record before it had a chance to announce, we'd fail to send its goodbye packet (which would be a bug). +#define InitialAnnounceCount ((mDNSu8)10) +#define ReannounceCount ((mDNSu8)9) + +// Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not. +// This means that because the announce interval is doubled after sending the first packet, the first +// observed on-the-wire inter-packet interval between announcements is actually one second. +// The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent. +#define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4) +#define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2) +#define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2) + +#define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \ + (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \ + (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0) + +#define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0) +#define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR)) +#define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond) +#define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR)) + +#define MaxUnansweredQueries 4 + +mDNSlocal mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2) { - if (r1->RDLength != r2->RDLength) return(mDNSfalse); - switch(rrtype) + if (r1->rrtype != r2->rrtype) return(mDNSfalse); + if (r1->rdlength != r2->rdlength) return(mDNSfalse); + if (r1->rdatahash != r2->rdatahash) return(mDNSfalse); + if (r1->rdnamehash != r2->rdnamehash) return(mDNSfalse); + switch(r1->rrtype) { case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: return(SameDomainName(&r1->u.name, &r2->u.name)); + case kDNSType_PTR: return(SameDomainName(&r1->rdata->u.name, &r2->rdata->u.name)); - case kDNSType_SRV: return( r1->u.srv.priority == r2->u.srv.priority && - r1->u.srv.weight == r2->u.srv.weight && - r1->u.srv.port.NotAnInteger == r2->u.srv.port.NotAnInteger && - SameDomainName(&r1->u.srv.target, &r2->u.srv.target)); + case kDNSType_SRV: return(mDNSBool)( r1->rdata->u.srv.priority == r2->rdata->u.srv.priority && + r1->rdata->u.srv.weight == r2->rdata->u.srv.weight && + r1->rdata->u.srv.port.NotAnInteger == r2->rdata->u.srv.port.NotAnInteger && + SameDomainName(&r1->rdata->u.srv.target, &r2->rdata->u.srv.target) ); - default: return(mDNSPlatformMemSame(r1->u.data, r2->u.data, r1->RDLength)); + default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->rdata->u.data, r1->rdlength)); } } mDNSlocal mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) { - if (rr->InterfaceAddr.NotAnInteger && - q ->InterfaceAddr.NotAnInteger && - rr->InterfaceAddr.NotAnInteger != q->InterfaceAddr.NotAnInteger) return(mDNSfalse); + if (rr->InterfaceID && + q ->InterfaceID && + rr->InterfaceID != q->InterfaceID) return(mDNSfalse); // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. - if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->rrtype && q->rrtype != kDNSQType_ANY ) return(mDNSfalse); - if ( rr->rrclass != q->rrclass && q->rrclass != kDNSQClass_ANY) return(mDNSfalse); - return(SameDomainName(&rr->name, &q->name)); + if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->qtype && q->qtype != kDNSQType_ANY ) return(mDNSfalse); + if ( rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + return(rr->namehash == q->qnamehash && SameDomainName(&rr->name, &q->qname)); } -// SameResourceRecordSignature returns true if two resources records have the same interface, name, type, and class. -// -- i.e. if they would both be given in response to the same question. -// (TTL and rdata may differ) -mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2) +mDNSlocal mDNSu32 DomainNameHashValue(const domainname *const name) + { + mDNSu32 sum = 0; + const mDNSu8 *c; + + for (c = name->c; c[0] != 0 && c[1] != 0; c += 2) + { + sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) | + (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]); + sum = (sum<<3) | (sum>>29); + } + if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8); + return(sum); + } + +#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS) + +mDNSlocal mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb) { - if (!r1) { debugf("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); } - if (!r2) { debugf("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); } - if (r1->InterfaceAddr.NotAnInteger && - r2->InterfaceAddr.NotAnInteger && - r1->InterfaceAddr.NotAnInteger != r2->InterfaceAddr.NotAnInteger) return(mDNSfalse); - return (r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && SameDomainName(&r1->name, &r2->name)); + mDNSu32 sum = 0; + int i; + for (i=0; i+1 < rdlength; i+=2) + { + sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1]; + sum = (sum<<3) | (sum>>29); + } + if (i < rdlength) + { + sum += ((mDNSu32)(rdb->data[i])) << 8; + } + return(sum); } -// SameResourceRecordSignatureAnyInterface returns true if two resources records have the same name, type, and class. -// (InterfaceAddr, TTL and rdata may differ) -mDNSlocal mDNSBool SameResourceRecordSignatureAnyInterface(const ResourceRecord *const r1, const ResourceRecord *const r2) +// SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent +// (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match). +// TTL and rdata may differ. +// This is used for cache flush management: +// When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent +// When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed +mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2) { - if (!r1) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r1 is NULL"); return(mDNSfalse); } - if (!r2) { debugf("SameResourceRecordSignatureAnyInterface ERROR: r2 is NULL"); return(mDNSfalse); } - return (r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && SameDomainName(&r1->name, &r2->name)); + if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); } + if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); } + if (r1->InterfaceID && + r2->InterfaceID && + r1->InterfaceID != r2->InterfaceID) return(mDNSfalse); + return(mDNSBool)(r1->rrtype == r2->rrtype && r1->rrclass == r2->rrclass && r1->namehash == r2->namehash && SameDomainName(&r1->name, &r2->name)); } -// IdenticalResourceRecord returns true if two resources records have -// the same interface, name, type, class, and identical rdata (TTL may differ) -mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2) +// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if the +// authoratative record is in the probing state. Probes are sent with the wildcard type, so a response of +// any type should match, even if it is not the type the client plans to use. +mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr) { - if (!SameResourceRecordSignature(r1, r2)) return(mDNSfalse); - return(SameRData(r1->rrtype, r1->rdata, r2->rdata)); + if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); } + if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); } + if (pktrr->resrec.InterfaceID && + authrr->resrec.InterfaceID && + pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); + if (authrr->resrec.RecordType != kDNSRecordTypeUnique && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse); + return(mDNSBool)(pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && SameDomainName(&pktrr->resrec.name, &authrr->resrec.name)); } -// IdenticalResourceRecordAnyInterface returns true if two resources records have -// the same name, type, class, and identical rdata (InterfaceAddr and TTL may differ) -mDNSlocal mDNSBool IdenticalResourceRecordAnyInterface(const ResourceRecord *const r1, const ResourceRecord *const r2) +// IdenticalResourceRecord returns true if two resources records have +// the same name, type, class, and identical rdata (InterfaceID and TTL may differ) +mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2) { - if (!SameResourceRecordSignatureAnyInterface(r1, r2)) return(mDNSfalse); - return(SameRData(r1->rrtype, r1->rdata, r2->rdata)); + if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); } + if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); } + if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(&r1->name, &r2->name)) return(mDNSfalse); + return(SameRData(r1, r2)); } -// ResourceRecord *ds is the ResourceRecord from the duplicate suppression section of the query -// This is the information that the requester believes to be correct -// ResourceRecord *rr is the answer we are proposing to give, if not suppressed -// This is the information that we believe to be correct -mDNSlocal mDNSBool SuppressDuplicate(const ResourceRecord *const ds, const ResourceRecord *const rr) +// CacheRecord *ks is the CacheRecord from the known answer list in the query. +// This is the information that the requester believes to be correct. +// AuthRecord *rr is the answer we are proposing to give, if not suppressed. +// This is the information that we believe to be correct. +// We've already determined that we plan to give this answer on this interface +// (either the record is non-specific, or it is specific to this interface) +// so now we just need to check the name, type, class, rdata and TTL. +mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr) { - // If RR signature is different, or data is different, then don't suppress - if (!IdenticalResourceRecord(ds,rr)) return(mDNSfalse); + // If RR signature is different, or data is different, then don't suppress our answer + if (!IdenticalResourceRecord(&ka->resrec,&rr->resrec)) return(mDNSfalse); // If the requester's indicated TTL is less than half the real TTL, // we need to give our answer before the requester's copy expires. @@ -674,93 +2171,251 @@ mDNSlocal mDNSBool SuppressDuplicate(const ResourceRecord *const ds, const Resou // (If two responders on the network are offering the same information, // that's okay, and if they are offering the information with different TTLs, // the one offering the lower TTL should defer to the one offering the higher TTL.) - return(ds->rroriginalttl >= rr->rroriginalttl / 2); + return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2); } -mDNSlocal mDNSu32 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) +mDNSlocal mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) { + RDataBody *rd = &rr->rdata->u; const domainname *const name = estimate ? &rr->name : mDNSNULL; switch (rr->rrtype) { - case kDNSType_A: return(sizeof(rr->rdata->u.ip)); break; + case kDNSType_A: return(sizeof(rd->ip)); break; case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: return(CompressedDomainNameLength(&rr->rdata->u.name, name)); - case kDNSType_TXT: return(rr->rdata->RDLength); // TXT is not self-describing, so have to just trust rdlength - case kDNSType_AAAA: return(16); break; - case kDNSType_SRV: return(6 + CompressedDomainNameLength(&rr->rdata->u.srv.target, name)); + case kDNSType_PTR: return(CompressedDomainNameLength(&rd->name, name)); + case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]); + case kDNSType_NULL: // Same as TXT -- not self-describing, so have to just trust rdlength + case kDNSType_TXT: return(rr->rdlength); // TXT is not self-describing, so have to just trust rdlength + case kDNSType_AAAA: return(sizeof(rd->ipv6)); break; + case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name)); default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype); - return(rr->rdata->RDLength); + return(rr->rdlength); } } -// rr is a ResourceRecord in our cache -// (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd) -mDNSlocal DNSQuestion *CacheRRActive(const mDNS *const m, ResourceRecord *rr) +mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr) { - DNSQuestion *q; - for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions - if (q->ThisQInterval > 0 && !q->DuplicateOf && ResourceRecordAnswersQuestion(rr, q)) - return(q); - return(mDNSNULL); + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + { + if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); + } + else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr)) + { + if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval); + } } -mDNSlocal void SetTargetToHostName(const mDNS *const m, ResourceRecord *const rr) +#define GetRRDomainNameTarget(RR) ( \ + ((RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR) ? &(RR)->rdata->u.name : \ + ((RR)->rrtype == kDNSType_SRV ) ? &(RR)->rdata->u.srv.target : mDNSNULL ) + +mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) { - switch (rr->rrtype) + // To allow us to aggregate probes when a group of services are registered together, + // the first probe is delayed 1/4 second. This means the common-case behaviour is: + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; probe + // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) + + // If we have no probe suppression time set, or it is in the past, set it now + if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) { - case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: rr->rdata->u.name = m->hostname1; break; - case kDNSType_SRV: rr->rdata->u.srv.target = m->hostname1; break; - default: debugf("SetTargetToHostName: Dont' know how to set the target of rrtype %d", rr->rrtype); break; + m->SuppressProbes = (m->timenow + DefaultProbeIntervalForTypeUnique) | 1; + // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation + if (m->SuppressProbes - m->NextScheduledProbe >= 0) + m->SuppressProbes = m->NextScheduledProbe; + // If we already have a query scheduled to go out sooner, then use that time to get better aggregation + if (m->SuppressProbes - m->NextScheduledQuery >= 0) + m->SuppressProbes = m->NextScheduledQuery; } - rr->rdata->RDLength = GetRDLength(rr, mDNSfalse); - rr->rdestimate = GetRDLength(rr, mDNStrue); - // If we're in the middle of probing this record, we need to start again, - // because changing its rdata may change the outcome of the tie-breaker. - rr->ProbeCount = DefaultProbeCountForRecordType(rr->RecordType); - rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType); - rr->NextSendTime = mDNSPlatformTimeNow(); - rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType); - if (rr->RecordType == kDNSRecordTypeUnique && m->SuppressProbes) rr->NextSendTime = m->SuppressProbes; + // We announce to flush stale data from other caches. It is a reasonable assumption that any + // old stale copies will probably have the same TTL we're using, so announcing longer than + // this serves no purpose -- any stale copies of that record will have expired by then anyway. + rr->AnnounceUntil = m->timenow + TicksTTL(rr); + rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; + // Set LastMCTime to now, to inhibit multicast responses + // (no need to send additional multicast responses when we're announcing anyway) + rr->LastMCTime = m->timenow; + rr->LastMCInterface = mDNSInterfaceMark; + + // If this is a record type that's not going to probe, then delay its first announcement so that + // it will go out synchronized with the first announcement for the other records that *are* probing. + // This is a minor performance tweak that helps keep groups of related records synchronized together. + // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are + // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete. + // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated, + // because they will meet the criterion of being at least half-way to their scheduled announcement time. + if (rr->resrec.RecordType != kDNSRecordTypeUnique) + rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; + + SetNextAnnounceProbeTime(m, rr); } -mDNSlocal void UpdateHostNameTargets(const mDNS *const m) +mDNSlocal void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength) { - ResourceRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->HostTarget) - SetTargetToHostName(m, rr); + domainname *target; + if (NewRData) + { + rr->rdata = NewRData; + rr->rdlength = rdlength; + } + // Must not try to get target pointer until after updating rr->rdata + target = GetRRDomainNameTarget(rr); + rr->rdlength = GetRDLength(rr, mDNSfalse); + rr->rdestimate = GetRDLength(rr, mDNStrue); + rr->rdatahash = RDataHashValue(rr->rdlength, &rr->rdata->u); + rr->rdnamehash = target ? DomainNameHashValue(target) : 0; + } + +mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) + { + domainname *target = GetRRDomainNameTarget(&rr->resrec); + + if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype); + + if (target && SameDomainName(target, &m->hostname)) + debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name.c, target->c); + + if (target && !SameDomainName(target, &m->hostname)) + { + AssignDomainName(*target, m->hostname); + SetNewRData(&rr->resrec, mDNSNULL, 0); + + // If we're in the middle of probing this record, we need to start again, + // because changing its rdata may change the outcome of the tie-breaker. + // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.) + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + + // If we've announced this record, we really should send a goodbye packet for the old rdata before + // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records, + // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way. + if (rr->AnnounceCount < InitialAnnounceCount && rr->resrec.RecordType == kDNSRecordTypeShared) + debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + + if (rr->AnnounceCount < ReannounceCount) + rr->AnnounceCount = ReannounceCount; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m,rr); + } + } + +mDNSlocal void CompleteProbing(mDNS *const m, AuthRecord *const rr) + { + verbosedebugf("Probing for %##s (%s) complete", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + if (!rr->Acknowledged && rr->RecordCallback) + { + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + rr->Acknowledged = mDNStrue; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + rr->RecordCallback(m, rr, mStatus_NoError); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } + } + +#define ValidateDomainName(N) (DomainNameLength(N) <= MAX_DOMAIN_NAME) + +mDNSlocal mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd) + { + mDNSu16 len; + switch(rrtype) + { + case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); + + case kDNSType_NS: // Same as PTR + case kDNSType_MD: // Same as PTR + case kDNSType_MF: // Same as PTR + case kDNSType_CNAME:// Same as PTR + //case kDNSType_SOA not checked + case kDNSType_MB: // Same as PTR + case kDNSType_MG: // Same as PTR + case kDNSType_MR: // Same as PTR + //case kDNSType_NULL not checked (no specified format, so always valid) + //case kDNSType_WKS not checked + case kDNSType_PTR: len = DomainNameLength(&rd->u.name); + return(len <= MAX_DOMAIN_NAME && rdlength == len); + + case kDNSType_HINFO:// Same as TXT (roughly) + case kDNSType_MINFO:// Same as TXT (roughly) + case kDNSType_TXT: { + const mDNSu8 *ptr = rd->u.txt.c; + const mDNSu8 *end = rd->u.txt.c + rdlength; + while (ptr < end) ptr += 1 + ptr[0]; + return (ptr == end); + } + + case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr)); + + case kDNSType_MX: len = DomainNameLength(&rd->u.mx.exchange); + return(len <= MAX_DOMAIN_NAME && rdlength == 2+len); + + case kDNSType_SRV: len = DomainNameLength(&rd->u.srv.target); + return(len <= MAX_DOMAIN_NAME && rdlength == 6+len); + + default: return(mDNStrue); // Allow all other types without checking + } } -mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, ResourceRecord *const rr, const mDNSs32 timenow) +// Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified +#define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified)) +#define RecordIsLocalDuplicate(A,B) ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec)) + +mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) { - ResourceRecord **p = &m->ResourceRecords; + domainname *target = GetRRDomainNameTarget(&rr->resrec); + AuthRecord *r; + AuthRecord **p = &m->ResourceRecords; + AuthRecord **d = &m->DuplicateRecords; + AuthRecord **l = &m->LocalOnlyRecords; + +#if TEST_LOCALONLY_FOR_EVERYTHING + rr->resrec.InterfaceID = (mDNSInterfaceID)~0; +#endif + while (*p && *p != rr) p=&(*p)->next; - if (*p) + while (*d && *d != rr) d=&(*d)->next; + while (*l && *l != rr) l=&(*l)->next; + if (*d || *p || *l) { - debugf("Error! Tried to register a ResourceRecord that's already in the list"); + LogMsg("Error! Tried to register a AuthRecord %p %##s (%s) that's already in the list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); return(mStatus_AlreadyRegistered); } if (rr->DependentOn) { - if (rr->RecordType == kDNSRecordTypeUnique) - rr->RecordType = kDNSRecordTypeVerified; + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + rr->resrec.RecordType = kDNSRecordTypeVerified; else { - debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn && RecordType != kDNSRecordTypeUnique", - rr->name.c); + LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); return(mStatus_Invalid); } - if (rr->DependentOn->RecordType != kDNSRecordTypeUnique && rr->DependentOn->RecordType != kDNSRecordTypeVerified) + if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified))) { - debugf("mDNS_Register_internal: ERROR! %##s: rr->DependentOn->RecordType bad type %X", - rr->name.c, rr->DependentOn->RecordType); + LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType); return(mStatus_Invalid); } } + // If this resource record is referencing a specific interface, make sure it exists + if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != ((mDNSInterfaceID)~0)) + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == rr->resrec.InterfaceID) break; + if (!intf) + { + debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID); + return(mStatus_BadReferenceErr); + } + } + rr->next = mDNSNULL; // Field Group 1: Persistent metadata for Authoritative Records @@ -771,55 +2426,127 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, ResourceRecord *const rr // rr->Callback = already set in mDNS_SetupResourceRecord // rr->Context = already set in mDNS_SetupResourceRecord // rr->RecordType = already set in mDNS_SetupResourceRecord -// rr->HostTarget = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client // Field Group 2: Transient state for Authoritative Records rr->Acknowledged = mDNSfalse; - rr->ProbeCount = DefaultProbeCountForRecordType(rr->RecordType); - rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType); + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->AnnounceCount = InitialAnnounceCount; rr->IncludeInProbe = mDNSfalse; - rr->SendPriority = 0; - rr->Requester = zeroIPAddr; + rr->ImmedAnswer = mDNSNULL; + rr->ImmedAdditional = mDNSNULL; + rr->SendRNow = mDNSNULL; + rr->v4Requester = zeroIPAddr; + rr->v6Requester = zerov6Addr; rr->NextResponse = mDNSNULL; rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; - rr->LastSendTime = timenow - mDNSPlatformOneSecond; - rr->NextSendTime = timenow; - if (rr->RecordType == kDNSRecordTypeUnique && m->SuppressProbes) rr->NextSendTime = m->SuppressProbes; - rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType); + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m, rr); +// rr->AnnounceUntil = Set for us in InitializeLastAPTime() +// rr->LastAPTime = Set for us in InitializeLastAPTime() +// rr->LastMCTime = Set for us in InitializeLastAPTime() +// rr->LastMCInterface = Set for us in InitializeLastAPTime() rr->NewRData = mDNSNULL; + rr->newrdlength = 0; rr->UpdateCallback = mDNSNULL; + rr->UpdateCredits = kMaxUpdateCredits; + rr->NextUpdateCredit = 0; + rr->UpdateBlocked = 0; - // Field Group 3: Transient state for Cache Records - rr->NextDupSuppress = mDNSNULL; // Not strictly relevant for a local record - rr->TimeRcvd = 0; // Not strictly relevant for a local record - rr->LastUsed = 0; // Not strictly relevant for a local record - rr->UseCount = 0; // Not strictly relevant for a local record - rr->UnansweredQueries = 0; // Not strictly relevant for a local record - rr->Active = mDNSfalse; // Not strictly relevant for a local record - rr->NewData = mDNSfalse; // Not strictly relevant for a local record - - // Field Group 4: The actual information pertaining to this resource record -// rr->interface = already set in mDNS_SetupResourceRecord -// rr->name.c = MUST be set by client -// rr->rrtype = already set in mDNS_SetupResourceRecord -// rr->rrclass = already set in mDNS_SetupResourceRecord -// rr->rroriginalttl = already set in mDNS_SetupResourceRecord -// rr->rrremainingttl = already set in mDNS_SetupResourceRecord +// rr->resrec.interface = already set in mDNS_SetupResourceRecord +// rr->resrec.name.c = MUST be set by client +// rr->resrec.rrtype = already set in mDNS_SetupResourceRecord +// rr->resrec.rrclass = already set in mDNS_SetupResourceRecord +// rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord +// rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set if (rr->HostTarget) + { + if (target) target->c[0] = 0; SetTargetToHostName(m, rr); // This also sets rdlength and rdestimate for us + } else { - rr->rdata->RDLength = GetRDLength(rr, mDNSfalse); - rr->rdestimate = GetRDLength(rr, mDNStrue); + rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); + rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue); } -// rr->rdata = MUST be set by client - *p = rr; + if (!ValidateDomainName(&rr->resrec.name)) + { LogMsg("Attempt to register record with invalid name: %s", GetRRDisplayString(m, rr)); return(mStatus_Invalid); } + + // Don't do this until *after* we've set rr->resrec.rdlength + if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata)) + { LogMsg("Attempt to register record with invalid rdata: %s", GetRRDisplayString(m, rr)); return(mStatus_Invalid); } + + rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name); + rr->resrec.rdatahash = RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u); + rr->resrec.rdnamehash = target ? DomainNameHashValue(target) : 0; + + if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0)) + { + debugf("Adding %p %##s (%s) to LocalOnly list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + *l = rr; + if (!m->NewLocalOnlyRecords) m->NewLocalOnlyRecords = rr; + // If this is supposed to be unique, make sure we don't have any name conflicts + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + { + const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr; + for (r = m->LocalOnlyRecords; r; r=r->next) + { + const AuthRecord *s2 = r->RRSet ? r->RRSet : r; + if (s1 != s2 && SameResourceRecordSignature(&r->resrec, &rr->resrec) && !SameRData(&r->resrec, &rr->resrec)) + break; + } + if (r) // If we found a conflict, set DiscardLocalOnlyRecords so we'll deliver the callback + { + debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + m->DiscardLocalOnlyRecords = mDNStrue; + } + else // else no conflict, so set ProbeCount to zero and update RecordType as appropriate + { + rr->ProbeCount = 0; + if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; + } + } + } + else + { + // Now that's we've finished building our new record, make sure it's not identical to one we already have + for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break; + + if (r) + { + debugf("Adding %p %##s (%s) to duplicate list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + *d = rr; + // If the previous copy of this record is already verified unique, + // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. + // Setting ProbeCount to zero will cause SendQueries() to advance this record to + // kDNSRecordTypeVerified state and call the client callback at the next appropriate time. + if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified) + rr->ProbeCount = 0; + } + else + { + debugf("Adding %p %##s (%s) to active record list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + *p = rr; + } + } return(mStatus_NoError); } +mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) + { + m->ProbeFailTime = m->timenow; + m->NumFailedProbes++; + // If we've had ten or more probe failures, rate-limit to one every five seconds + // The result is ORed with 1 to make sure SuppressProbes is not accidentally set to zero + if (m->NumFailedProbes >= 10) m->SuppressProbes = (m->timenow + mDNSPlatformOneSecond * 5) | 1; + if (m->NumFailedProbes >= 16) + LogMsg("Name in use: %##s (%s); need to choose another (%d)", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), m->NumFailedProbes); + } + // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered @@ -827,74 +2554,145 @@ typedef enum { mDNS_Dereg_normal, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_ // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSlocal void mDNS_Deregister_internal(mDNS *const m, ResourceRecord *const rr, const mDNSs32 timenow, mDNS_Dereg_type drt) +mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt) { - mDNSu8 RecordType = rr->RecordType; - // If this is a shared record and we've announced it at least once, - // we need to retract that announcement before we delete the record - if (RecordType == kDNSRecordTypeShared && rr->AnnounceCount <= DefaultAnnounceCountForTypeShared) + mDNSu8 RecordType = rr->resrec.RecordType; + AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records + if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0)) p = &m->LocalOnlyRecords; + while (*p && *p != rr) p=&(*p)->next; + + if (*p) { - debugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); - rr->RecordType = kDNSRecordTypeDeregistering; - rr->rroriginalttl = 0; - rr->rrremainingttl = 0; + // We found our record on the main list. See if there are any duplicates that need special handling. + if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment + { + AuthRecord *r2 = m->DuplicateRecords; + while (r2) + { + if (RecordIsLocalDuplicate(r2, rr)) { mDNS_Deregister_internal(m, r2, drt); r2 = m->DuplicateRecords; } + else r2=r2->next; + } + } + else + { + // Before we delete the record (and potentially send a goodbye packet) + // first see if we have a record on the duplicate list ready to take over from it. + AuthRecord **d = &m->DuplicateRecords; + while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next; + if (*d) + { + AuthRecord *dup = *d; + debugf("Duplicate record %p taking over from %p %##s (%s)", dup, rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + *d = dup->next; // Cut replacement record from DuplicateRecords list + dup->next = rr->next; // And then... + rr->next = dup; // ... splice it in right after the record we're about to delete + dup->resrec.RecordType = rr->resrec.RecordType; + dup->ProbeCount = rr->ProbeCount; + dup->AnnounceCount = rr->AnnounceCount; + dup->ImmedAnswer = rr->ImmedAnswer; + dup->ImmedAdditional = rr->ImmedAdditional; + dup->v4Requester = rr->v4Requester; + dup->v6Requester = rr->v6Requester; + dup->ThisAPInterval = rr->ThisAPInterval; + dup->AnnounceUntil = rr->AnnounceUntil; + dup->LastAPTime = rr->LastAPTime; + dup->LastMCTime = rr->LastMCTime; + dup->LastMCInterface = rr->LastMCInterface; + if (RecordType == kDNSRecordTypeShared) rr->AnnounceCount = InitialAnnounceCount; + } + } } else { - // Find this record in our list of active records - ResourceRecord **p = &m->ResourceRecords; + // We didn't find our record on the main list; try the DuplicateRecords list instead. + p = &m->DuplicateRecords; while (*p && *p != rr) p=&(*p)->next; + // If we found our record on the duplicate list, then make sure we don't send a goodbye for it + if (*p && RecordType == kDNSRecordTypeShared) rr->AnnounceCount = InitialAnnounceCount; + if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + } + + if (!*p) + { + // No need to log an error message if we already know this is a potentially repeated deregistration + if (drt != mDNS_Dereg_repeat) + debugf("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + return(mStatus_BadReferenceErr); + } - if (*p) *p = rr->next; + // If this is a shared record and we've announced it at least once, + // we need to retract that announcement before we delete the record + if (RecordType == kDNSRecordTypeShared && rr->AnnounceCount < InitialAnnounceCount) + { + verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeDeregistering; + rr->resrec.rroriginalttl = 0; + rr->ImmedAnswer = mDNSInterfaceMark; + if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0)) + m->DiscardLocalOnlyRecords = mDNStrue; else { - // No need to give an error message if we already know this is a potentially repeated deregistration - if (drt != mDNS_Dereg_repeat) - debugf("mDNS_Deregister_internal: Record %##s (%s) not found in list", rr->name.c, DNSTypeName(rr->rrtype)); - return; + if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0) + m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10); } + } + else + { + *p = rr->next; // Cut this record from the list // If someone is about to look at this, bump the pointer forward - if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; + if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; + if (m->NewLocalOnlyRecords == rr) m->NewLocalOnlyRecords = rr->next; rr->next = mDNSNULL; if (RecordType == kDNSRecordTypeUnregistered) - debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered", rr->name.c, DNSTypeName(rr->rrtype)); + debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); else if (RecordType == kDNSRecordTypeDeregistering) - debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering", rr->name.c, DNSTypeName(rr->rrtype)); + debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); else { - debugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); - rr->RecordType = kDNSRecordTypeUnregistered; + verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeUnregistered; } if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared) - debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); + debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); // If we have an update queued up which never executed, give the client a chance to free that memory if (rr->NewRData) { - RData *OldRData = rr->rdata; - rr->rdata = rr->NewRData; // Update our rdata - rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... - if (rr->UpdateCallback) rr->UpdateCallback(m, rr, OldRData); // ... and let the client know + RData *OldRData = rr->resrec.rdata; + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, OldRData); // ... and let the client know } - if (RecordType == kDNSRecordTypeShared && rr->Callback) - rr->Callback(m, rr, mStatus_MemFree); + // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + // In this case the likely client action to the mStatus_MemFree message is to free the memory, + // so any attempt to touch rr after this is likely to lead to a crash. + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (RecordType == kDNSRecordTypeShared) + { + if (rr->RecordCallback) + rr->RecordCallback(m, rr, mStatus_MemFree); + } else if (drt == mDNS_Dereg_conflict) { - m->ProbeFailTime = timenow; - // If we've had ten probe failures, rate-limit to one every five seconds - // The result is ORed with 1 to make sure SuppressProbes is not accidentally set to zero - if (m->NumFailedProbes < 10) m->NumFailedProbes++; - else m->SuppressProbes = (timenow + mDNSPlatformOneSecond * 5) | 1; - if (rr->Callback) rr->Callback(m, rr, mStatus_NameConflict); + RecordProbeFailure(m, rr); + if (rr->RecordCallback) + rr->RecordCallback(m, rr, mStatus_NameConflict); } + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again } + return(mStatus_NoError); } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - #pragma mark - DNS Message Creation Functions @@ -970,8 +2768,20 @@ mDNSlocal mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message... { + if (*np > MAX_DOMAIN_LABEL) + { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); } + + // This check correctly allows for the final trailing root label: + // e.g. + // Suppose our domain name is exactly 255 bytes long, including the final trailing root label. + // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local"). + // We know that max will be at name->c[255] + // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our + // six bytes, then exit the loop, write the final terminating root label, and the domain + // name we've written is exactly 255 bytes long, exactly at the correct legal limit. + // If the name is one byte longer, then we fail the "if" test below, and correctly bail out. if (np + 1 + *np >= max) - { debugf("Malformed domain name (more than 255 characters)"); return(mDNSNULL); } + { LogMsg("Malformed domain name %##s (more than 255 bytes)", name->c); return(mDNSNULL); } if (base) pointer = FindCompressionPointer(base, searchlimit, np); if (pointer) // Use a compression pointer if we can @@ -1000,55 +2810,59 @@ mDNSlocal mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, return(mDNSNULL); } -mDNSlocal mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, - const mDNSu16 rrtype, const RData *const rdata) +mDNSlocal mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr) { - switch (rrtype) + switch (rr->rrtype) { - case kDNSType_A: if (rdata->RDLength != 4) + case kDNSType_A: if (rr->rdlength != 4) { - debugf("putRData: Illegal length %d for kDNSType_A", rdata->RDLength); + debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); } if (ptr + 4 > limit) return(mDNSNULL); - *ptr++ = rdata->u.ip.b[0]; - *ptr++ = rdata->u.ip.b[1]; - *ptr++ = rdata->u.ip.b[2]; - *ptr++ = rdata->u.ip.b[3]; + *ptr++ = rr->rdata->u.ip.b[0]; + *ptr++ = rr->rdata->u.ip.b[1]; + *ptr++ = rr->rdata->u.ip.b[2]; + *ptr++ = rr->rdata->u.ip.b[3]; return(ptr); case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rdata->u.name)); + case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name)); - case kDNSType_TXT: if (ptr + rdata->RDLength > limit) return(mDNSNULL); - mDNSPlatformMemCopy(rdata->u.data, ptr, rdata->RDLength); - return(ptr + rdata->RDLength); + case kDNSType_HINFO:// Same as TXT + case kDNSType_TXT: if (ptr + rr->rdlength > limit) return(mDNSNULL); + mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength); + return(ptr + rr->rdlength); - case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL); - *ptr++ = (mDNSu8)(rdata->u.srv.priority >> 8); - *ptr++ = (mDNSu8)(rdata->u.srv.priority ); - *ptr++ = (mDNSu8)(rdata->u.srv.weight >> 8); - *ptr++ = (mDNSu8)(rdata->u.srv.weight ); - *ptr++ = rdata->u.srv.port.b[0]; - *ptr++ = rdata->u.srv.port.b[1]; - return(putDomainNameAsLabels(msg, ptr, limit, &rdata->u.srv.target)); + case kDNSType_AAAA: if (rr->rdlength != sizeof(rr->rdata->u.ipv6)) + { + debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); + return(mDNSNULL); + } + if (ptr + sizeof(rr->rdata->u.ipv6) > limit) return(mDNSNULL); + mDNSPlatformMemCopy(&rr->rdata->u.ipv6, ptr, sizeof(rr->rdata->u.ipv6)); + return(ptr + sizeof(rr->rdata->u.ipv6)); - default: if (ptr + rdata->RDLength > limit) return(mDNSNULL); - debugf("putRData: Warning! Writing resource type %d as raw data", rrtype); - mDNSPlatformMemCopy(rdata->u.data, ptr, rdata->RDLength); - return(ptr + rdata->RDLength); + case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority >> 8); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority ); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight ); + *ptr++ = rr->rdata->u.srv.port.b[0]; + *ptr++ = rr->rdata->u.srv.port.b[1]; + return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.srv.target)); + + default: if (ptr + rr->rdlength > limit) return(mDNSNULL); + debugf("putRData: Warning! Writing resource type %d as raw data", rr->rrtype); + mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength); + return(ptr + rr->rdlength); } } -// Put a domain name, type, class, ttl, length, and type-specific data -// domainname is a fully-qualified name -// Only pass the "m" and "timenow" parameters in cases where the LastSendTime is to be updated, -// and the kDNSClass_UniqueRRSet bit set -mDNSlocal mDNSu8 *putResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, - mDNSu16 *count, ResourceRecord *rr, mDNS *const m, const mDNSs32 timenow) +mDNSlocal mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl) { mDNSu8 *endofrdata; - mDNSu32 actualLength; + mDNSu16 actualLength; const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes, @@ -1058,7 +2872,7 @@ mDNSlocal mDNSu8 *putResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, if (rr->RecordType == kDNSRecordTypeUnregistered) { - debugf("putResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered"); + LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); return(ptr); } @@ -1068,50 +2882,41 @@ mDNSlocal mDNSu8 *putResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, ptr[1] = (mDNSu8)(rr->rrtype ); ptr[2] = (mDNSu8)(rr->rrclass >> 8); ptr[3] = (mDNSu8)(rr->rrclass ); - ptr[4] = (mDNSu8)(rr->rrremainingttl >> 24); - ptr[5] = (mDNSu8)(rr->rrremainingttl >> 16); - ptr[6] = (mDNSu8)(rr->rrremainingttl >> 8); - ptr[7] = (mDNSu8)(rr->rrremainingttl ); - endofrdata = putRData(msg, ptr+10, limit, rr->rrtype, rr->rdata); - if (!endofrdata) { debugf("Ran out of space in putResourceRecord!"); return(mDNSNULL); } + ptr[4] = (mDNSu8)(ttl >> 24); + ptr[5] = (mDNSu8)(ttl >> 16); + ptr[6] = (mDNSu8)(ttl >> 8); + ptr[7] = (mDNSu8)(ttl ); + endofrdata = putRData(msg, ptr+10, limit, rr); + if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); return(mDNSNULL); } // Go back and fill in the actual number of data bytes we wrote // (actualLength can be less than rdlength when domain name compression is used) - actualLength = (mDNSu32)(endofrdata - ptr - 10); + actualLength = (mDNSu16)(endofrdata - ptr - 10); ptr[8] = (mDNSu8)(actualLength >> 8); ptr[9] = (mDNSu8)(actualLength ); - if (m) // If the 'm' parameter was passed in... - { - rr->LastSendTime = timenow; // ... then update LastSendTime - if (rr->RecordType & kDNSRecordTypeUniqueMask) // If it is supposed to be unique - { - const ResourceRecord *a = mDNSNULL; - // If we find a member of the same RRSet (same name/type/class) - // that hasn't been updated within the last quarter second, don't set the bit - for (a = m->ResourceRecords; a; a=a->next) - if (SameResourceRecordSignatureAnyInterface(rr, a)) - if (timenow - a->LastSendTime > mDNSPlatformOneSecond/4) - break; - if (a == mDNSNULL) - ptr[2] |= kDNSClass_UniqueRRSet >> 8; - } - } - (*count)++; return(endofrdata); } +#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) + +mDNSlocal mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 maxttl) + { + if (maxttl > rr->rroriginalttl) maxttl = rr->rroriginalttl; + return(PutResourceRecordTTL(msg, ptr, count, rr, maxttl)); + } + #if 0 mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, - mDNSu16 *count, const ResourceRecord *rr) + mDNSu16 *count, const AuthRecord *rr) { ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->name); if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL - ptr[0] = (mDNSu8)(rr->rrtype >> 8); // Put type - ptr[1] = (mDNSu8)(rr->rrtype ); - ptr[2] = (mDNSu8)(rr->rrclass >> 8); // Put class - ptr[3] = (mDNSu8)(rr->rrclass ); + ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type + ptr[1] = (mDNSu8)(rr->resrec.rrtype ); + ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class + ptr[3] = (mDNSu8)(rr->resrec.rrclass ); ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero ptr[8] = ptr[9] = 0; // RDATA length is zero (*count)++; @@ -1133,14 +2938,14 @@ mDNSlocal mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 * } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - DNS Message Parsing Functions #endif mDNSlocal const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end) { - mDNSu32 total = 0; + mDNSu16 total = 0; if (ptr < (mDNSu8*)msg || ptr >= end) { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } @@ -1232,128 +3037,117 @@ mDNSlocal const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 * return(ptr + pktrdlength); } -mDNSlocal const mDNSu8 *getResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, - const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow, mDNSu8 RecordType, ResourceRecord *rr, RData *RDataStorage) +#define GetLargeResourceRecord(m, msg, p, e, i, t, L) \ + (((L)->r.rdatastorage.MaxRDLength = MaximumRDSize), GetResourceRecord((m), (msg), (p), (e), (i), (t), &(L)->r, (RData*)&(L)->r.rdatastorage)) + +mDNSlocal const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, + const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, CacheRecord *rr, RData *RDataStorage) { mDNSu16 pktrdlength; rr->next = mDNSNULL; - - // Field Group 1: Persistent metadata for Authoritative Records - rr->Additional1 = mDNSNULL; - rr->Additional2 = mDNSNULL; - rr->DependentOn = mDNSNULL; - rr->RRSet = mDNSNULL; - rr->Callback = mDNSNULL; - rr->Context = mDNSNULL; - rr->RecordType = RecordType; - rr->HostTarget = mDNSfalse; - - // Field Group 2: Transient state for Authoritative Records - rr->Acknowledged = mDNSfalse; - rr->ProbeCount = 0; - rr->AnnounceCount = 0; - rr->IncludeInProbe = mDNSfalse; - rr->SendPriority = 0; - rr->Requester = zeroIPAddr; - rr->NextResponse = mDNSNULL; - rr->NR_AnswerTo = mDNSNULL; - rr->NR_AdditionalTo = mDNSNULL; - rr->LastSendTime = 0; - rr->NextSendTime = 0; - rr->NextSendInterval = 0; - rr->NewRData = mDNSNULL; - rr->UpdateCallback = mDNSNULL; + rr->resrec.RecordType = RecordType; - // Field Group 3: Transient state for Cache Records - rr->NextDupSuppress = mDNSNULL; - rr->TimeRcvd = timenow; - rr->LastUsed = timenow; + rr->NextInKAList = mDNSNULL; + rr->TimeRcvd = m->timenow; + rr->NextRequiredQuery = m->timenow; // Will be updated to the real value when we call SetNextCacheCheckTime() + rr->LastUsed = m->timenow; rr->UseCount = 0; + rr->CRActiveQuestion = mDNSNULL; rr->UnansweredQueries = 0; - rr->Active = mDNSfalse; - rr->NewData = mDNStrue; - - // Field Group 4: The actual information pertaining to this resource record - rr->InterfaceAddr = InterfaceAddr; - ptr = getDomainName(msg, ptr, end, &rr->name); - if (!ptr) { debugf("getResourceRecord: Malformed RR name"); return(mDNSNULL); } - - if (ptr + 10 > end) { debugf("getResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } - - rr->rrtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); - rr->rrclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSQClass_Mask; - rr->rroriginalttl = (mDNSu32)((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]); - if (rr->rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond) - rr->rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond; - rr->rrremainingttl = 0; + rr->LastUnansweredTime= 0; + rr->MPUnansweredQ = 0; + rr->MPLastUnansweredQT= 0; + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; + rr->NextInCFList = mDNSNULL; + + rr->resrec.InterfaceID = InterfaceID; + ptr = getDomainName(msg, ptr, end, &rr->resrec.name); + if (!ptr) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL); } + + if (ptr + 10 > end) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } + + rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); + rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask); + rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]); + if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond) + rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond; + // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for + // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly. pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); if (ptr[2] & (kDNSClass_UniqueRRSet >> 8)) - rr->RecordType |= kDNSRecordTypeUniqueMask; + rr->resrec.RecordType |= kDNSRecordTypePacketUniqueMask; ptr += 10; - if (ptr + pktrdlength > end) { debugf("getResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } if (RDataStorage) - rr->rdata = RDataStorage; + rr->resrec.rdata = RDataStorage; else { - rr->rdata = &rr->rdatastorage; - rr->rdata->MaxRDLength = sizeof(RDataBody); + rr->resrec.rdata = (RData*)&rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); } - switch (rr->rrtype) + switch (rr->resrec.rrtype) { - case kDNSType_A: rr->rdata->u.ip.b[0] = ptr[0]; - rr->rdata->u.ip.b[1] = ptr[1]; - rr->rdata->u.ip.b[2] = ptr[2]; - rr->rdata->u.ip.b[3] = ptr[3]; + case kDNSType_A: rr->resrec.rdata->u.ip.b[0] = ptr[0]; + rr->resrec.rdata->u.ip.b[1] = ptr[1]; + rr->resrec.rdata->u.ip.b[2] = ptr[2]; + rr->resrec.rdata->u.ip.b[3] = ptr[3]; break; - case kDNSType_CNAME:// CNAME is same as PTR - case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->rdata->u.name)) - { debugf("getResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); } - //debugf("%##s PTR %##s rdlen %d", rr->name.c, rr->rdata->u.name.c, pktrdlength); + case kDNSType_CNAME:// Same as PTR + case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.name)) + { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); } + //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength); break; - case kDNSType_TXT: if (pktrdlength > rr->rdata->MaxRDLength) + case kDNSType_NULL: //Same as TXT + case kDNSType_HINFO://Same as TXT + case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength) { - debugf("getResourceRecord: TXT rdata size (%d) exceeds storage (%d)", - pktrdlength, rr->rdata->MaxRDLength); + debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)", + DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); return(mDNSNULL); } - rr->rdata->RDLength = pktrdlength; - mDNSPlatformMemCopy(ptr, rr->rdata->u.data, pktrdlength); + rr->resrec.rdlength = pktrdlength; + mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength); break; - case kDNSType_SRV: rr->rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); - rr->rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); - rr->rdata->u.srv.port.b[0] = ptr[4]; - rr->rdata->u.srv.port.b[1] = ptr[5]; - if (!getDomainName(msg, ptr+6, end, &rr->rdata->u.srv.target)) - { debugf("getResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); } - //debugf("%##s SRV %##s rdlen %d", rr->name.c, rr->rdata->u.srv.target.c, pktrdlength); + case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6)); break; - default: if (pktrdlength > rr->rdata->MaxRDLength) + case kDNSType_SRV: rr->resrec.rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + rr->resrec.rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + rr->resrec.rdata->u.srv.port.b[0] = ptr[4]; + rr->resrec.rdata->u.srv.port.b[1] = ptr[5]; + if (!getDomainName(msg, ptr+6, end, &rr->resrec.rdata->u.srv.target)) + { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); } + //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength); + break; + + default: if (pktrdlength > rr->resrec.rdata->MaxRDLength) { - debugf("getResourceRecord: rdata %d size (%d) exceeds storage (%d)", - rr->rrtype, pktrdlength, rr->rdata->MaxRDLength); + debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)", + rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); return(mDNSNULL); } - if (rr->rrtype != kDNSType_AAAA) - debugf("getResourceRecord: Warning! Reading resource type %d as opaque data", rr->rrtype); + debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data", + rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype)); // Note: Just because we don't understand the record type, that doesn't // mean we fail. The DNS protocol specifies rdlength, so we can // safely skip over unknown records and ignore them. // We also grab a binary copy of the rdata anyway, since the caller // might know how to interpret it even if we don't. - rr->rdata->RDLength = pktrdlength; - mDNSPlatformMemCopy(ptr, rr->rdata->u.data, pktrdlength); + rr->resrec.rdlength = pktrdlength; + mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength); break; } - rr->rdata->RDLength = GetRDLength(rr, mDNSfalse); - rr->rdestimate = GetRDLength(rr, mDNStrue); + rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); + return(ptr + pktrdlength); } @@ -1365,16 +3159,17 @@ mDNSlocal const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, c return(ptr+4); } -mDNSlocal const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSIPAddr InterfaceAddr, +mDNSlocal const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, DNSQuestion *question) { - question->InterfaceAddr = InterfaceAddr; - ptr = getDomainName(msg, ptr, end, &question->name); + question->InterfaceID = InterfaceID; + ptr = getDomainName(msg, ptr, end, &question->qname); if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); } if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } - - question->rrtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type - question->rrclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class + + question->qnamehash = DomainNameHashValue(&question->qname); + question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type + question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class return(ptr+4); } @@ -1395,14 +3190,14 @@ mDNSlocal const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDN } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - #pragma mark - Packet Sending Functions #endif mDNSlocal mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - mDNSIPAddr src, mDNSIPPort srcport, mDNSIPAddr dst, mDNSIPPort dstport) + mDNSInterfaceID InterfaceID, mDNSIPPort srcport, const mDNSAddr *dst, mDNSIPPort dstport) { mStatus status; mDNSu16 numQuestions = msg->h.numQuestions; @@ -1422,7 +3217,7 @@ mDNSlocal mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, *ptr++ = (mDNSu8)(numAdditionals ); // Send the packet on the wire - status = mDNSPlatformSendUDP(m, msg, end, src, srcport, dst, dstport); + status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, srcport, dst, dstport); // Put all the integer values back the way they were before we return msg->h.numQuestions = numQuestions; @@ -1433,425 +3228,560 @@ mDNSlocal mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, return(status); } -mDNSlocal mDNSBool HaveResponses(const mDNS *const m, const mDNSs32 timenow) +mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr) { - ResourceRecord *rr; - if (m->SleepState) - { - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->RecordType == kDNSRecordTypeShared && rr->rrremainingttl == 0) - return(mDNStrue); - } - else - { - for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if (rr->RecordType == kDNSRecordTypeDeregistering) - return(mDNStrue); - - if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr) && timenow - rr->NextSendTime >= 0) - return(mDNStrue); - - if (rr->SendPriority >= kDNSSendPriorityAnswer && ResourceRecordIsValidAnswer(rr)) - return(mDNStrue); - } - } - return(mDNSfalse); + // Setting AnnounceCount to InitialAnnounceCount signals mDNS_Deregister_internal() + // that it should go ahead and immediately dispose of this registration + rr->resrec.RecordType = kDNSRecordTypeShared; + rr->AnnounceCount = InitialAnnounceCount; + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); } // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSlocal void DiscardDeregistrations(mDNS *const m, mDNSs32 timenow) +mDNSlocal void DiscardDeregistrations(mDNS *const m) { - if (m->CurrentRecord) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set"); + if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set"); m->CurrentRecord = m->ResourceRecords; while (m->CurrentRecord) { - ResourceRecord *rr = m->CurrentRecord; + AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (rr->RecordType == kDNSRecordTypeDeregistering) - { - rr->RecordType = kDNSRecordTypeShared; - rr->AnnounceCount = DefaultAnnounceCountForTypeShared+1; - mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal); - } + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + CompleteDeregistration(m, rr); } } -// This routine sends as many records as it can fit in a single DNS Response Message, in order of priority. -// If there are any deregistrations, announcements, or answers that don't fit, they are left in the work list for next time. -// If there are any additionals that don't fit, they are discarded -- they were optional anyway. -// NOTE: BuildResponse calls mDNS_Deregister_internal which can call a user callback, which may change +// Note about acceleration of announcements to facilitate automatic coalescing of +// multiple independent threads of announcements into a single synchronized thread: +// The announcements in the packet may be at different stages of maturity; +// One-second interval, two-second interval, four-second interval, and so on. +// After we've put in all the announcements that are due, we then consider +// whether there are other nearly-due announcements that are worth accelerating. +// To be eligible for acceleration, a record MUST NOT be older (further along +// its timeline) than the most mature record we've already put in the packet. +// In other words, younger records can have their timelines accelerated to catch up +// with their elder bretheren; this narrows the age gap and helps them eventually get in sync. +// Older records cannot have their timelines accelerated; this would just widen +// the gap between them and their younger bretheren and get them even more out of sync. + +// NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSlocal mDNSu8 *BuildResponse(mDNS *const m, - DNSMessage *const response, mDNSu8 *responseptr, const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow) +mDNSlocal void SendResponses(mDNS *const m) { - ResourceRecord *rr; - mDNSu8 *newptr; - int numDereg = 0; - int numAnnounce = 0; - int numAnswer = 0; - mDNSs32 minExistingAnnounceInterval = 0; + int pktcount = 0; + AuthRecord *rr, *r2; + mDNSs32 maxExistingAnnounceInterval = 0; + const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); - if (m->CurrentRecord) debugf("BuildResponse ERROR m->CurrentRecord already set"); - m->CurrentRecord = m->ResourceRecords; + m->NextScheduledResponse = m->timenow + 0x78000000; + + // *** + // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on + // *** - // If we're sleeping, only send deregistrations - if (m->SleepState) + // Run through our list of records, and decide which ones we're going to announce on all interfaces + for (rr = m->ResourceRecords; rr; rr=rr->next) { - while (m->CurrentRecord) + if (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) { - ResourceRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger && - rr->RecordType == kDNSRecordTypeShared && rr->rrremainingttl == 0 && - (newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, mDNSNULL, 0))) + if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; + else rr->NextUpdateCredit = (m->timenow + mDNSPlatformOneSecond * 60) | 1; + } + if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr)) + { + rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces + if (maxExistingAnnounceInterval < rr->ThisAPInterval) + maxExistingAnnounceInterval = rr->ThisAPInterval; + if (rr->UpdateBlocked) rr->UpdateBlocked = 0; + } + } + + // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one) + // Eligible records that are more than half-way to their announcement time are accelerated + for (rr = m->ResourceRecords; rr; rr=rr->next) + if ((rr->resrec.InterfaceID && rr->ImmedAnswer) || + (rr->ThisAPInterval <= maxExistingAnnounceInterval && + TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) && + ResourceRecordIsValidAnswer(rr))) + rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces + + // When sending SRV records (particularly when announcing a new service) automatically add the related Address record(s) + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV) + for (r2=m->ResourceRecords; r2; r2=r2->next) // Scan list of resource records + if (RRIsAddressType(r2) && // For all address records (A/AAAA) ... + ResourceRecordIsValidAnswer(r2) && // ... which are valid for answer ... + rr->LastMCTime - r2->LastMCTime >= 0 && // ... which we have not sent recently ... + rr->resrec.rdnamehash == r2->resrec.namehash && // ... whose name is the name of the SRV target + SameDomainName(&rr->resrec.rdata->u.srv.target, &r2->resrec.name) && + (rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID)) + r2->ImmedAnswer = mDNSInterfaceMark; // ... then mark this address record for sending too + + // If there's a record which is supposed to be unique that we're going to send, then make sure that we give + // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class + // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a + // record, then other RRSet members that have not been sent recently will get flushed out of client caches. + // -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface + // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + { + if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked + { + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2)) + if (r2->ImmedAnswer != mDNSInterfaceMark && r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(&r2->resrec, &rr->resrec)) + r2->ImmedAnswer = rr->ImmedAnswer; + } + else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked { - numDereg++; - responseptr = newptr; - rr->rrremainingttl = rr->rroriginalttl; + for (r2 = m->ResourceRecords; r2; r2=r2->next) + if (ResourceRecordIsValidAnswer(r2)) + if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(&r2->resrec, &rr->resrec)) + r2->ImmedAdditional = rr->ImmedAdditional; } } - } - else + + // Now set SendRNow state appropriately + for (rr = m->ResourceRecords; rr; rr=rr->next) { - // 1. Look for deregistrations we need to send - while (m->CurrentRecord) + if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces { - ResourceRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger) + rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; + rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer + rr->LastMCTime = m->timenow; + rr->LastMCInterface = rr->ImmedAnswer; + // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done + if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) { - if (rr->NewRData) // If we have new data for this record - { - RData *OldRData = rr->rdata; - if (ResourceRecordIsValidAnswer(rr)) // First see if we have to de-register the old data - { - rr->rrremainingttl = 0; // Clear rroriginalttl before putting record - newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, mDNSNULL, 0); - if (newptr) - { - numDereg++; - responseptr = newptr; - } - rr->rrremainingttl = rr->rroriginalttl; // Now restore rroriginalttl - } - rr->rdata = rr->NewRData; // Update our rdata - rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... - if (rr->UpdateCallback) rr->UpdateCallback(m, rr, OldRData); // ... and let the client know - } - if (rr->RecordType == kDNSRecordTypeDeregistering && - (newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, mDNSNULL, 0))) - { - numDereg++; - responseptr = newptr; - rr->RecordType = kDNSRecordTypeShared; - rr->AnnounceCount = DefaultAnnounceCountForTypeShared+1; - mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal); - } + rr->AnnounceCount--; + rr->ThisAPInterval *= 2; + rr->LastAPTime = m->timenow; + if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0; + debugf("Announcing %##s (%s) %d", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount); } } - - // 2. Look for announcements we are due to send in the next second - for (rr = m->ResourceRecords; rr; rr=rr->next) + else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface: { - if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger && - rr->AnnounceCount && ResourceRecordIsValidAnswer(rr) && - timenow + mDNSPlatformOneSecond - rr->NextSendTime >= 0) - { - newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, m, timenow); - if (newptr) - { - numAnnounce++; - responseptr = newptr; - } - // If we were able to put the record, then update the state variables - // If we were unable to put the record because it is too large to fit, even though - // there are no other answers in the packet, then pretend we succeeded anyway, - // or we'll end up in an infinite loop trying to send a record that will never fit - if (response->h.numAnswers == 0) debugf("BuildResponse announcements failed"); - if (newptr || response->h.numAnswers == 0) - { - if (minExistingAnnounceInterval < rr->NextSendInterval) - minExistingAnnounceInterval = rr->NextSendInterval; - rr->SendPriority = 0; - rr->Requester = zeroIPAddr; - rr->AnnounceCount--; - rr->NextSendTime += rr->NextSendInterval; - if (rr->NextSendTime - (timenow + rr->NextSendInterval/2) < 0) - rr->NextSendTime = (timenow + rr->NextSendInterval/2); - rr->NextSendInterval *= 2; - } - } + rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface + rr->ImmedAdditional = mDNSNULL; // No need to send as additional too + rr->LastMCTime = m->timenow; + rr->LastMCInterface = rr->ImmedAnswer; } + SetNextAnnounceProbeTime(m, rr); + } + + // *** + // *** 2. Loop through interface list, sending records as appropriate + // *** + + while (intf) + { + int numDereg = 0; + int numAnnounce = 0; + int numAnswer = 0; + DNSMessage response; + mDNSu8 *responseptr = response.data; + mDNSu8 *newptr; + InitializeDNSMessage(&response.h, zeroID, ResponseFlags); - // 2a. Look for additional announcements that are worth accelerating - // They must be (a) at least half-way to their next announcement and - // (b) at an interval equal or less than any of the ones we've already put in + // First Pass. Look for: + // 1. Deregistering records that need to send their goodbye packet + // 2. Updated records that need to retract their old data + // 3. Answers and announcements we need to send + // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can + // send this packet and then try again. + // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway, + // because otherwise we'll end up in an infinite loop trying to send a record that will never fit. for (rr = m->ResourceRecords; rr; rr=rr->next) - { - if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger && - rr->AnnounceCount && ResourceRecordIsValidAnswer(rr) && - timenow - (rr->LastSendTime + rr->NextSendInterval/4) >= 0 && - rr->NextSendInterval <= minExistingAnnounceInterval) + if (rr->SendRNow == intf->InterfaceID) + { + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { + newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0); + if (!newptr && response.h.numAnswers) break; + numDereg++; + responseptr = newptr; + } + else if (rr->NewRData) // If we have new data for this record { - newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, m, timenow); - if (newptr) + RData *OldRData = rr->resrec.rdata; + mDNSu16 oldrdlength = rr->resrec.rdlength; + // See if we should send a courtesy "goodbye" the old data before we replace it. + // We compare with "InitialAnnounceCount-1" instead of "InitialAnnounceCount" because by the time + // we get to this place in this routine we've we've already decremented rr->AnnounceCount + if (ResourceRecordIsValidAnswer(rr) && rr->AnnounceCount < InitialAnnounceCount-1) { - numAnnounce++; + newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0); + if (!newptr && response.h.numAnswers) break; + numDereg++; responseptr = newptr; } - // If we were able to put the record, then update the state variables - // If we were unable to put the record because it is too large to fit, even though - // there are no other answers in the packet, then pretend we succeeded anyway, - // or we'll end up in an infinite loop trying to send a record that will never fit - if (response->h.numAnswers == 0) debugf("BuildResponse announcements failed"); - if (newptr || response->h.numAnswers == 0) + // Now try to see if we can fit the update in the same packet (not fatal if we can't) + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); + newptr = PutResourceRecord(&response, responseptr, &response.h.numAnswers, &rr->resrec); + if (newptr) responseptr = newptr; + SetNewRData(&rr->resrec, OldRData, oldrdlength); + } + else + { + // If this record is supposed to be unique, see if we've sent its whole set + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) { - rr->SendPriority = 0; - rr->Requester = zeroIPAddr; - rr->AnnounceCount--; - rr->NextSendTime = timenow + rr->NextSendInterval; - rr->NextSendInterval *= 2; + // Try to find another member of this set that we're still planning to send on this interface + const AuthRecord *a; + for (a = m->ResourceRecords; a; a=a->next) + if (a->SendRNow == intf->InterfaceID && a != rr && SameResourceRecordSignature(&a->resrec, &rr->resrec)) break; + if (a == mDNSNULL) // If no more members of this set found + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it } + newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (!newptr && response.h.numAnswers) break; + if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++; + responseptr = newptr; } - } + // If sending on all interfaces, go to next interface; else we're finished now + if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any) + rr->SendRNow = GetNextActiveInterfaceID(intf); + else + rr->SendRNow = mDNSNULL; + } - // 3. Look for answers we need to send + // Second Pass. Add additional records, if there's space. + newptr = responseptr; for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger && - rr->SendPriority >= kDNSSendPriorityAnswer && ResourceRecordIsValidAnswer(rr)) + if (rr->ImmedAdditional == intf->InterfaceID) + { + // Since additionals are optional, we clear ImmedAdditional anyway, even if we subsequently find it doesn't fit in the packet + rr->ImmedAdditional = mDNSNULL; + if (newptr && ResourceRecordIsValidAnswer(rr)) { - newptr = putResourceRecord(response, responseptr, &response->h.numAnswers, rr, m, timenow); - if (newptr) - { - numAnswer++; - responseptr = newptr; - } - // If we were able to put the record, then update the state variables - // If we were unable to put the record because it is too large to fit, even though - // there are no other answers in the packet then pretend we succeeded anyway, - // or we'll end up in an infinite loop trying to send a record that will never fit - if (response->h.numAnswers == 0) debugf("BuildResponse answers failed"); - if (newptr || response->h.numAnswers == 0) + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) { - rr->SendPriority = 0; - rr->Requester = zeroIPAddr; + // Try to find another member of this set that we're still planning to send on this interface + const AuthRecord *a; + for (a = m->ResourceRecords; a; a=a->next) + if (a->ImmedAdditional == intf->InterfaceID && SameResourceRecordSignature(&a->resrec, &rr->resrec)) break; + if (a == mDNSNULL) // If no more members of this set found + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it } + newptr = PutResourceRecord(&response, newptr, &response.h.numAdditionals, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state } - - // 4. Add additionals, if there's space - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger && - rr->SendPriority == kDNSSendPriorityAdditional) - { - if (ResourceRecordIsValidAnswer(rr) && - (newptr = putResourceRecord(response, responseptr, &response->h.numAdditionals, rr, m, timenow))) - responseptr = newptr; - rr->SendPriority = 0; // Clear SendPriority anyway, even if we didn't put the additional in the packet - rr->Requester = zeroIPAddr; } + if (newptr) responseptr = newptr; + + if (response.h.numAnswers > 0) // We *never* send a packet with only additionals in it + { + debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", + numDereg, numDereg == 1 ? "" : "s", + numAnnounce, numAnnounce == 1 ? "" : "s", + numAnswer, numAnswer == 1 ? "" : "s", + response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); + mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v4, MulticastDNSPort); + mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v6, MulticastDNSPort); + if (!m->SuppressSending) m->SuppressSending = (m->timenow + mDNSPlatformOneSecond/10) | 1; // OR with one to ensure non-zero + if (++pktcount >= 1000) + { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; } + // There might be more things to send on this interface, so go around one more time and try again. + } + else // Nothing more to send on this interface; go to next + { + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + #if MDNS_DEBUGMSGS && 0 + const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p"; + debugf(msg, intf, next); + #endif + intf = next; + } } - if (numDereg || numAnnounce || numAnswer || response->h.numAdditionals) - verbosedebugf("BuildResponse Built %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s", - numDereg, numDereg == 1 ? "" : "s", - numAnnounce, numAnnounce == 1 ? "" : "s", - numAnswer, numAnswer == 1 ? "" : "s", - response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); + // *** + // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables + // *** - return(responseptr); + if (m->CurrentRecord) LogMsg("SendResponses: ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + + if (rr->NewRData) + { + RData *OldRData = rr->resrec.rdata; + SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, OldRData); // ... and let the client know + } + + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + CompleteDeregistration(m, rr); + else + { + rr->ImmedAnswer = mDNSNULL; + rr->v4Requester = zeroIPAddr; + rr->v6Requester = zerov6Addr; + } + } + verbosedebugf("SendResponses: Next in %d ticks", m->NextScheduledResponse - m->timenow); } -mDNSlocal void SendResponses(mDNS *const m, const mDNSs32 timenow) +// Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache, +// so we want to be lazy about how frequently we do it. +// 1. If a cache record is currently referenced by *no* active questions, +// then we don't mind expiring it up to a minute late (who will know?) +// 2. Else, if a cache record is due for some of its final expiration queries, +// we'll allow them to be late by up to 2% of the TTL +// 3. Else, if a cache record has completed all its final expiration queries without success, +// and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late +// 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets), +// so allow at most 1/10 second lateness +#define CacheCheckGracePeriod(RR) ( \ + ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ + ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ + ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10)) + +// Note: MUST call SetNextCacheCheckTime any time we change: +// rr->TimeRcvd +// rr->resrec.rroriginalttl +// rr->UnansweredQueries +// rr->CRActiveQuestion +mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr) { - DNSMessage response; - DNSMessageHeader baseheader; - mDNSu8 *baselimit, *responseptr; - NetworkInterfaceInfo *intf; - ResourceRecord *rr, *r2; + rr->NextRequiredQuery = RRExpireTime(rr); - // Run through our list of records, - // and if there's a record which is supposed to be unique that we're proposing to give as an answer, - // then make sure that the whole RRSet with that name/type/class is also marked for answering. - // Otherwise, if we set the kDNSClass_UniqueRRSet bit on a record, then other RRSet members - // that have not been sent recently will get flushed out of client caches. - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->RecordType & kDNSRecordTypeUniqueMask) - if (TimeToSendThisRecord(rr,timenow)) - for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (r2 != rr && timenow - r2->LastSendTime > mDNSPlatformOneSecond/4) - if (SameResourceRecordSignatureAnyInterface(rr, r2)) - r2->SendPriority = kDNSSendPriorityAnswer; + // If we have an active question, then see if we want to schedule a refresher query for this record. + // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL. + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + { + rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries); + rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50); + verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond); + } + + if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0) + m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)); + } - // First build the generic part of the message - InitializeDNSMessage(&response.h, zeroID, ResponseFlags); - baselimit = BuildResponse(m, &response, response.data, zeroIPAddr, timenow); - baseheader = response.h; +#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 45) +#define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5) +#define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) - for (intf = m->HostInterfaces; intf; intf = intf->next) +mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) + { + if (interval < kMinimumReconfirmTime) + interval = kMinimumReconfirmTime; + if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below + interval = 0x10000000; + + // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration + if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3)) { - // Restore the header to the counts for the generic records - response.h = baseheader; - // Now add any records specific to this interface - responseptr = BuildResponse(m, &response, baselimit, intf->ip, timenow); - if (response.h.numAnswers > 0) // We *never* send a packet with only additionals in it - { - mDNSSendDNSMessage(m, &response, responseptr, intf->ip, MulticastDNSPort, AllDNSLinkGroup, MulticastDNSPort); - debugf("SendResponses Sent %d Answer%s, %d Additional%s on %.4a", - response.h.numAnswers, response.h.numAnswers == 1 ? "" : "s", - response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", &intf->ip); - } + // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts + interval += mDNSRandom(interval/3); + rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3; + rr->resrec.rroriginalttl = interval * 4 / mDNSPlatformOneSecond; + SetNextCacheCheckTime(m, rr); } + debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, GetRRDisplayString(m, rr)); + return(mStatus_NoError); } -#define TimeToSendThisQuestion(Q,time) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf && (time) - (Q)->NextQTime >= 0) +#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond) -mDNSlocal mDNSBool HaveQueries(const mDNS *const m, const mDNSs32 timenow) +// BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr. +// It also appends to the list of known answer records that need to be included, +// and updates the forcast for the size of the known answer section. +mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, + CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) { - ResourceRecord *rr; - DNSQuestion *q; - - // 1. See if we've got any cache records in danger of expiring - for (rr = m->rrcache; rr; rr=rr->next) - if (rr->UnansweredQueries < 2) - { - mDNSs32 onetenth = ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond) / 10; - mDNSs32 t0 = rr->TimeRcvd + (mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond; - mDNSs32 t1 = t0 - onetenth; - mDNSs32 t2 = t1 - onetenth; + mDNSBool ucast = q->LargeAnswers || q->ThisQInterval <= InitialQuestionInterval*2; + mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); + const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; + mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); + if (!newptr) + { + debugf("BuildQuestion: No more space in this packet for question %##s", q->qname.c); + return(mDNSfalse); + } + else if (newptr + *answerforecast >= limit) + { + verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q->qname.c, newptr + *answerforecast - query->data); + query->h.numQuestions--; + return(mDNSfalse); + } + else + { + mDNSu32 forecast = *answerforecast; + CacheRecord *rr; + CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update + + for (rr=m->rrcache_hash[HashSlot(&q->qname)]; rr; rr=rr->next) // If we have a resource record in our cache, + if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface + rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not already in the known answer list + rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question + rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry + rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery + { + *ka = rr; // Link this record into our known answer chain + ka = &rr->NextInKAList; + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + forecast += 12 + rr->resrec.rdestimate; + // If we're trying to put more than one question in this packet, and it doesn't fit + // then undo that last question and try again next time + if (query->h.numQuestions > 1 && newptr + forecast >= limit) + { + debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d", + q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data); + query->h.numQuestions--; + ka = *kalistptrptr; // Go back to where we started and retract these answer records + while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; } + return(mDNSfalse); // Return false, so we'll try again in the next packet + } + } - if (timenow - t1 >= 0 || (rr->UnansweredQueries < 1 && timenow - t2 >= 0)) + // Traffic reduction: + // If we already have at least one unique answer in the cache, + // OR we have so many shared answers that the KA list is too big to fit in one packet + // The we suppress queries number 3 and 5: + // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies) + // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally) + // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress) + // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally) + // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress) + // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally) + if (q->UniqueAnswers || newptr + forecast >= limit) + if (q->ThisQInterval == InitialQuestionInterval * 8 || q->ThisQInterval == InitialQuestionInterval * 32) { - DNSQuestion *q = CacheRRActive(m, rr); - if (q) q->NextQTime = timenow; + query->h.numQuestions--; + ka = *kalistptrptr; // Go back to where we started and retract these answer records + while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; } + return(mDNStrue); // Return true: pretend we succeeded, even though we actually suppressed this question } - } - - // 2. Scan our list of questions to see if it's time to send any of them - for (q = m->ActiveQuestions; q; q=q->next) - if (TimeToSendThisQuestion(q, timenow)) - return(mDNStrue); - // 3. Scan our list of Resource Records to see if we need to send any probe questions - for (rr = m->ResourceRecords; rr; rr=rr->next) // Scan our list of records - if (rr->RecordType == kDNSRecordTypeUnique && timenow - rr->NextSendTime >= 0) - return(mDNStrue); + // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return + *queryptr = newptr; // Update the packet pointer + *answerforecast = forecast; // Update the forecast + *kalistptrptr = ka; // Update the known answer list pointer + if (ucast) m->ExpectUnicastResponse = m->timenow; - return(mDNSfalse); + for (rr=m->rrcache_hash[HashSlot(&q->qname)]; rr; rr=rr->next) // For every resource record in our cache, + if (rr->resrec.InterfaceID == q->SendQNow && // received on this interface + rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList && // which is not in the known answer list + ResourceRecordAnswersQuestion(&rr->resrec, q)) // which answers our question + { + rr->UnansweredQueries++; // indicate that we're expecting a response + rr->LastUnansweredTime = m->timenow; + SetNextCacheCheckTime(m, rr); + } + + return(mDNStrue); + } } -// BuildProbe puts a probe question into a DNS Query packet and if successful, updates the value of queryptr. -// It also sets the record's IncludeInProbe flag so that we know to add an Update Record too -// and updates the forcast for the size of the duplicate suppression (answer) section. -// NOTE: BuildProbe can call a user callback, which may change the record list and/or question list. -// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSlocal void BuildProbe(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, - ResourceRecord *rr, mDNSu32 *answerforecast, const mDNSs32 timenow) +mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q) { - if (rr->ProbeCount == 0) - { - rr->RecordType = kDNSRecordTypeVerified; - rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType); - debugf("Probing for %##s (%s) complete", rr->name.c, DNSTypeName(rr->rrtype)); - if (!rr->Acknowledged && rr->Callback) - { rr->Acknowledged = mDNStrue; rr->Callback(m, rr, mStatus_NoError); } - } - else - { - const mDNSu8 *const limit = query->data + ((query->h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); - mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &rr->name, kDNSQType_ANY, rr->rrclass); - // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) - mDNSu32 forecast = *answerforecast + 12 + rr->rdestimate; - if (newptr && newptr + forecast < limit) - { - *queryptr = newptr; - *answerforecast = forecast; - rr->ProbeCount--; // Only decrement ProbeCount if we successfully added the record to the packet - rr->IncludeInProbe = mDNStrue; - rr->NextSendTime = timenow + rr->NextSendInterval; - } - else + mDNSu32 slot; + CacheRecord *rr; + domainname *target; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) + if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdnamehash == q->qnamehash && SameDomainName(target, &q->qname)) + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); + } + +// Only DupSuppressInfos newer than the specified 'time' are allowed to remain active +mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time) + { + int i; + for (i=0; iIPv4Available; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query + mDNSBool v6 = !intf->IPv6Available; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query + for (i=0; iInterfaceID) { - debugf("BuildProbe retracting Question %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); - query->h.numQuestions--; + if (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue; + else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue; + if (v4 && v6) return(mDNStrue); } + return(mDNSfalse); + } + +mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type) + { + int i, j; + + // See if we have this one in our list somewhere already + for (i=0; i= DupSuppressInfoSize) + { + i = 0; + for (j=1; j= 0) ? (T) : (EARLIEST) ) - -// BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr. -// It also appends to the list of duplicate suppression records that need to be included, -// and updates the forcast for the size of the duplicate suppression (answer) section. -mDNSlocal void BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, - ResourceRecord ***dups_ptr, mDNSu32 *answerforecast, const mDNSs32 timenow) +mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) { - const mDNSu8 *const limit = query->data + (query->h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); - mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->name, q->rrtype, q->rrclass); - if (!newptr) - debugf("BuildQuestion: No more space for queries"); - else + // If more than 90% of the way to the query time, we should unconditionally accelerate it + if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10)) + return(mDNStrue); + + // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet + if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2)) { - mDNSu32 forecast = *answerforecast; - ResourceRecord *rr; - ResourceRecord **d = *dups_ptr; - mDNSs32 nst = timenow + q->NextQInterval; - - // If we have a resource record in our cache, - // which is not already in the duplicate suppression list - // which answers our question, - // then add it to the duplicate suppression list - for (rr=m->rrcache; rr; rr=rr->next) - if (rr->NextDupSuppress == mDNSNULL && d != &rr->NextDupSuppress && - ResourceRecordAnswersQuestion(rr, q)) + // We forecast: qname (n) type (2) class (2) + mDNSu32 forecast = DomainNameLength(&q->qname) + 4; + CacheRecord *rr; + for (rr=m->rrcache_hash[HashSlot(&q->qname)]; rr; rr=rr->next) // If we have a resource record in our cache, + if (rr->resrec.rdlength <= SmallRecordLimit && // which is small enough to sensibly fit in the packet + ResourceRecordAnswersQuestion(&rr->resrec, q) && // which answers our question + rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 && // and it is less than half-way to expiry + rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery { - // Work out the latest time we should ask about this record to refresh it before it expires - mDNSs32 onetenth = ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond) / 10; - mDNSs32 t0 = rr->TimeRcvd + (mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond; - mDNSs32 t3 = t0 - onetenth*3; - - // If we'll ask again at least twice before it expires, okay to suppress it this time - if (t3 - nst >= 0) - { - *d = rr; // Link this record into our duplicate suppression chain - d = &rr->NextDupSuppress; - // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) - forecast += 12 + rr->rdestimate; - } - else - rr->UnansweredQueries++; + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + forecast += 12 + rr->resrec.rdestimate; + if (forecast >= 512) return(mDNSfalse); // If this would add 512 bytes or more to the packet, don't accelerate } - - // If we're trying to put more than one question in this packet, and it doesn't fit - // then undo that last question and try again next time - if (query->h.numQuestions > 1 && newptr + forecast >= limit) - { - debugf("BuildQuestion retracting question %##s answerforecast %d", q->name.c, *answerforecast); - query->h.numQuestions--; - d = *dups_ptr; // Go back to where we started and retract these answer records - while (*d) { ResourceRecord *rr = *d; *d = mDNSNULL; d = &rr->NextDupSuppress; } - } - else - { - *queryptr = newptr; // Update the packet pointer - *answerforecast = forecast; // Update the forecast - *dups_ptr = d; // Update the dup suppression pointer - q->NextQTime = nst; - q->ThisQInterval = q->NextQInterval; - q->NextQInterval = GetNextQInterval(q->ThisQInterval); - } + return(mDNStrue); } + + return(mDNSfalse); } // How Standard Queries are generated: // 1. The Question Section contains the question -// 2. The Additional Section contains answers we already know, to suppress duplicate replies +// 2. The Additional Section contains answers we already know, to suppress duplicate responses // How Probe Queries are generated: // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because @@ -1861,733 +3791,1051 @@ mDNSlocal void BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr // because if some other host is probing at the same time, we each want to know what the other is // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't. -mDNSlocal mDNSu8 *BuildQueryPacketQuestions(mDNS *const m, DNSMessage *query, mDNSu8 *queryptr, - ResourceRecord ***dups_ptr, mDNSu32 *answerforecast, - const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow) +mDNSlocal void SendQueries(mDNS *const m) { + int pktcount = 0; DNSQuestion *q; + // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval + mDNSs32 maxExistingQuestionInterval = 0; + const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); + CacheRecord *KnownAnswerList = mDNSNULL; + + // 1. If time for a query, work out what we need to do + if (m->timenow - m->NextScheduledQuery >= 0) + { + mDNSu32 slot; + CacheRecord *rr; + m->NextScheduledQuery = m->timenow + 0x78000000; + + // We're expecting to send a query anyway, so see if any expiring cache records are close enough + // to their NextRequiredQuery to be worth batching them together with this one + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + if (m->timenow + TicksTTL(rr)/50 - rr->NextRequiredQuery >= 0) + { + q = rr->CRActiveQuestion; + ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID); + if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID; + else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark; + } + + // Scan our list of questions to see which ones we're definitely going to send + for (q = m->Questions; q; q=q->next) + if (TimeToSendThisQuestion(q, m->timenow)) + { + q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + if (maxExistingQuestionInterval < q->ThisQInterval) + maxExistingQuestionInterval = q->ThisQInterval; + } - // See which questions need to go out right now - for (q = m->ActiveQuestions; q; q=q->next) - if (q->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger && - TimeToSendThisQuestion(q, timenow)) - BuildQuestion(m, query, &queryptr, q, dups_ptr, answerforecast, timenow); + // Scan our list of questions + // (a) to see if there are any more that are worth accelerating, and + // (b) to update the state variables for all the questions we're going to send + for (q = m->Questions; q; q=q->next) + { + if (q->SendQNow || (ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q))) + { + // If at least halfway to next query time, advance to next interval + // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval + if (m->timenow - (q->LastQTime + q->ThisQInterval/2) >= 0) + { + q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + q->ThisQInterval *= 2; + if (q->ThisQInterval > MaxQuestionInterval) + q->ThisQInterval = MaxQuestionInterval; + else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * 8) + { + debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype)); + ReconfirmAntecedents(m, q); // If sending third query, and no answers yet, time to begin doubting the source + } + } - // See which questions are more than half way to their NextSendTime, and send them too, if we have space - for (q = m->ActiveQuestions; q; q=q->next) - if (q->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger && - TimeToSendThisQuestion(q, timenow + q->ThisQInterval/2)) - BuildQuestion(m, query, &queryptr, q, dups_ptr, answerforecast, timenow); + // Mark for sending. (If no active interfaces, then don't even try.) + q->SendOnAll = (q->SendQNow == mDNSInterfaceMark); + if (q->SendOnAll) + { + q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID; + q->LastQTime = m->timenow; + } - return(queryptr); - } + // If we recorded a duplicate suppression for this question less than half an interval ago, + // then we consider it recent enough that we don't need to do an identical query ourselves. + ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); -mDNSlocal mDNSu8 *BuildQueryPacketAnswers(DNSMessage *query, mDNSu8 *queryptr, - ResourceRecord **dups_ptr, const mDNSs32 timenow) - { - while (*dups_ptr) + q->LastQTxTime = m->timenow; + q->RecentAnswers = 0; + } + // For all questions (not just the ones we're sending) check what the next scheduled event will be + SetNextQueryTime(m,q); + } + } + + // 2. Scan our authoritative RR list to see what probes we might need to send + if (m->timenow - m->NextScheduledProbe >= 0) { - ResourceRecord *rr = *dups_ptr; - mDNSu32 timesincercvd = (mDNSu32)(timenow - rr->TimeRcvd); - mDNSu8 *newptr; - // Need to update rrremainingttl correctly before we put this cache record in the packet - rr->rrremainingttl = rr->rroriginalttl - timesincercvd / mDNSPlatformOneSecond; - newptr = putResourceRecord(query, queryptr, &query->h.numAnswers, rr, mDNSNULL, 0); - if (newptr) + m->NextScheduledProbe = m->timenow + 0x78000000; + + if (m->CurrentRecord) LogMsg("SendQueries: ERROR m->CurrentRecord already set"); + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) { - *dups_ptr = rr->NextDupSuppress; - rr->NextDupSuppress = mDNSNULL; - queryptr = newptr; + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (rr->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing... + { + // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly + if (m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) + { + SetNextAnnounceProbeTime(m, rr); + } + // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly + else if (rr->ProbeCount) + { + // Mark for sending. (If no active interfaces, then don't even try.) + rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID; + rr->LastAPTime = m->timenow; + rr->ProbeCount--; + SetNextAnnounceProbeTime(m, rr); + } + // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced + else + { + AuthRecord *r2; + rr->resrec.RecordType = kDNSRecordTypeVerified; + rr->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique; + rr->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique; + SetNextAnnounceProbeTime(m, rr); + // If we have any records on our duplicate list that match this one, they have now also completed probing + for (r2 = m->DuplicateRecords; r2; r2=r2->next) + if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr)) + r2->ProbeCount = 0; + CompleteProbing(m, rr); + } + } } - else + m->CurrentRecord = m->DuplicateRecords; + while (m->CurrentRecord) { - debugf("BuildQueryPacketAnswers: Put %d answers; No more space for duplicate suppression", - query->h.numAnswers); - query->h.flags.b[0] |= kDNSFlag0_TC; - break; + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0) + CompleteProbing(m, rr); } } - return(queryptr); - } -mDNSlocal mDNSu8 *BuildQueryPacketProbes(mDNS *const m, DNSMessage *query, mDNSu8 *queryptr, - mDNSu32 *answerforecast, const mDNSIPAddr InterfaceAddr, const mDNSs32 timenow) - { - if (m->CurrentRecord) debugf("BuildQueryPacketProbes ERROR m->CurrentRecord already set"); - m->CurrentRecord = m->ResourceRecords; - while (m->CurrentRecord) + // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface + while (intf) { - ResourceRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger && - rr->RecordType == kDNSRecordTypeUnique && timenow - rr->NextSendTime >= 0) - BuildProbe(m, query, &queryptr, rr, answerforecast, timenow); - } - return(queryptr); - } + AuthRecord *rr; + DNSMessage query; + mDNSu8 *queryptr = query.data; + InitializeDNSMessage(&query.h, zeroID, QueryFlags); + if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); + if (!KnownAnswerList) + { + // Start a new known-answer list + CacheRecord **kalistptr = &KnownAnswerList; + mDNSu32 answerforecast = 0; + + // Put query questions in this packet + for (q = m->Questions; q; q=q->next) + if (q->SendQNow == intf->InterfaceID) + { + debugf("SendQueries: %s question for %##s (%s) at %lu forecast total %lu", + SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ", + q->qname.c, DNSTypeName(q->qtype), queryptr - query.data, queryptr + answerforecast - query.data); + // If we're suppressing this question, or we successfully put it, update its SendQNow state + if (SuppressOnThisInterface(q->DupSuppress, intf) || + BuildQuestion(m, &query, &queryptr, q, &kalistptr, &answerforecast)) + q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); + } -mDNSlocal mDNSu8 *BuildQueryPacketUpdates(mDNS *const m, DNSMessage *query, mDNSu8 *queryptr) - { - ResourceRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->IncludeInProbe) + // Put probe questions in this packet + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->SendRNow == intf->InterfaceID) + { + mDNSBool ucast = rr->ProbeCount >= DefaultProbeCountForTypeUnique-1; + mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); + const mDNSu8 *const limit = query.data + ((query.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); + mDNSu8 *newptr = putQuestion(&query, queryptr, limit, &rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); + // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) + mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate; + if (newptr && newptr + forecast < limit) + { + queryptr = newptr; + answerforecast = forecast; + rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); + rr->IncludeInProbe = mDNStrue; + verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount); + } + else + { + verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + query.h.numQuestions--; + } + } + } + + // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) + while (KnownAnswerList) { - mDNSu8 *newptr = putResourceRecord(query, queryptr, &query->h.numAuthorities, rr, mDNSNULL, 0); - rr->IncludeInProbe = mDNSfalse; + CacheRecord *rr = KnownAnswerList; + mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; + mDNSu8 *newptr = PutResourceRecordTTL(&query, queryptr, &query.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd); if (newptr) + { + verbosedebugf("SendQueries: Put %##s (%s) at %lu - %lu", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), queryptr - query.data, newptr - query.data); queryptr = newptr; + KnownAnswerList = rr->NextInKAList; + rr->NextInKAList = mDNSNULL; + } else { - debugf("BuildQueryPacketUpdates: How did we fail to have space for the Update record %##s (%s)?", - rr->name.c, DNSTypeName(rr->rrtype)); + // If we ran out of space and we have more than one question in the packet, that's an error -- + // we shouldn't have put more than one question if there was a risk of us running out of space. + if (query.h.numQuestions > 1) + LogMsg("SendQueries: Put %d answers; No more space for known answers", query.h.numAnswers); + query.h.flags.b[0] |= kDNSFlag0_TC; break; } } - return(queryptr); - } -mDNSlocal void SendQueries(mDNS *const m, const mDNSs32 timenow) - { - ResourceRecord *NextDupSuppress = mDNSNULL; - do - { - DNSMessage query; - DNSMessageHeader baseheader; - mDNSu8 *baselimit = query.data; - NetworkInterfaceInfo *intf; - - // First build the generic part of the message - InitializeDNSMessage(&query.h, zeroID, QueryFlags); - if (!NextDupSuppress) + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->IncludeInProbe) + { + mDNSu8 *newptr = PutResourceRecord(&query, queryptr, &query.h.numAuthorities, &rr->resrec); + rr->IncludeInProbe = mDNSfalse; + if (newptr) queryptr = newptr; + else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + } + + if (queryptr > query.data) { - ResourceRecord **dups = &NextDupSuppress; - mDNSu32 answerforecast = 0; - baselimit = BuildQueryPacketQuestions(m, &query, baselimit, &dups, &answerforecast, zeroIPAddr, timenow); - baselimit = BuildQueryPacketProbes(m, &query, baselimit, &answerforecast, zeroIPAddr, timenow); + if ((query.h.flags.b[0] & kDNSFlag0_TC) && query.h.numQuestions > 1) + LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet\n", query.h.numQuestions); + debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", + query.h.numQuestions, query.h.numQuestions == 1 ? "" : "s", + query.h.numAnswers, query.h.numAnswers == 1 ? "" : "s", + query.h.numAuthorities, query.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); + mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v4, MulticastDNSPort); + mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, MulticastDNSPort, &AllDNSLinkGroup_v6, MulticastDNSPort); + if (!m->SuppressSending) m->SuppressSending = (m->timenow + mDNSPlatformOneSecond/10) | 1; // OR with one to ensure non-zero + if (++pktcount >= 1000) + { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; } + // There might be more records left in the known answer list, or more questions to send + // on this interface, so go around one more time and try again. } - baselimit = BuildQueryPacketAnswers(&query, baselimit, &NextDupSuppress, timenow); - baselimit = BuildQueryPacketUpdates(m, &query, baselimit); - baseheader = query.h; - - if (NextDupSuppress) debugf("SendQueries: NextDupSuppress still set... Will continue in next packet"); - - for (intf = m->HostInterfaces; intf; intf = intf->next) + else // Nothing more to send on this interface; go to next { - ResourceRecord *NextDupSuppress2 = mDNSNULL; - do - { - // Restore the header to the counts for the generic records - mDNSu8 *queryptr = baselimit; - query.h = baseheader; - // Now add any records specific to this interface, if we can - if (query.h.numAnswers == 0 && query.h.numAuthorities == 0 && !NextDupSuppress) - { - if (!NextDupSuppress2) - { - ResourceRecord **dups2 = &NextDupSuppress2; - mDNSu32 answerforecast2 = 0; - queryptr = BuildQueryPacketQuestions(m, &query, queryptr, &dups2, &answerforecast2, intf->ip, timenow); - queryptr = BuildQueryPacketProbes(m, &query, queryptr, &answerforecast2, intf->ip, timenow); - } - queryptr = BuildQueryPacketAnswers(&query, queryptr, &NextDupSuppress2, timenow); - queryptr = BuildQueryPacketUpdates(m, &query, queryptr); - } - - if (queryptr > query.data) - { - mDNSSendDNSMessage(m, &query, queryptr, intf->ip, MulticastDNSPort, AllDNSLinkGroup, MulticastDNSPort); - debugf("SendQueries Sent %d Question%s %d Answer%s %d Update%s on %.4a", - query.h.numQuestions, query.h.numQuestions == 1 ? "" : "s", - query.h.numAnswers, query.h.numAnswers == 1 ? "" : "s", - query.h.numAuthorities, query.h.numAuthorities == 1 ? "" : "s", &intf->ip); - } - } while (NextDupSuppress2); + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + #if MDNS_DEBUGMSGS && 0 + const char *const msg = next ? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p"; + debugf(msg, intf, next); + #endif + intf = next; } - } while (NextDupSuppress); + } } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - RR List Management & Task Management #endif -// rr is a new ResourceRecord just received into our cache -// (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd) -mDNSlocal void TriggerImmediateQuestions(mDNS *const m, const ResourceRecord *const rr, const mDNSs32 timenow) - { - // If we just received a new record off the wire that we've never seen before, we want to ask our question again - // soon, and keep doing that repeatedly (with duplicate suppression) until we stop getting any more responses - mDNSs32 needquery = timenow + mDNSPlatformOneSecond; - DNSQuestion *q; - for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions - if (q->ThisQInterval > 0 && !q->DuplicateOf && q->NextQTime - needquery > 0 && ResourceRecordAnswersQuestion(rr, q)) - { - q->NextQTime = needquery; - // As long as responses are still coming in, don't do the exponential backoff - q->NextQInterval = q->ThisQInterval; - } - } - // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, ResourceRecord *rr, const mDNSs32 timenow) +mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord) { - mDNSu32 timesincercvd = (mDNSu32)(timenow - rr->TimeRcvd); - if (rr->rroriginalttl <= timesincercvd / mDNSPlatformOneSecond) rr->rrremainingttl = 0; - else rr->rrremainingttl = rr->rroriginalttl - timesincercvd / mDNSPlatformOneSecond; + verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)", + q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); -#if DEBUGBREAKS - if (rr->rrremainingttl) - { - if (rr->rrtype == kDNSType_TXT) - debugf("AnswerQuestionWithResourceRecord Add %##s TXT %#.20s remaining ttl %d", - rr->name.c, rr->rdata->u.txt.c, rr->rrremainingttl); - else - debugf("AnswerQuestionWithResourceRecord Add %##s (%s) remaining ttl %d", - rr->name.c, DNSTypeName(rr->rrtype), rr->rrremainingttl); - } - else + rr->LastUsed = m->timenow; + rr->UseCount++; + if (ActiveQuestion(q) && rr->CRActiveQuestion != q) { - if (rr->rrtype == kDNSType_TXT) - debugf("AnswerQuestionWithResourceRecord Del %##s TXT %#.20s UnansweredQueries %d", - rr->name.c, rr->rdata->u.txt.c, rr->UnansweredQueries); - else - debugf("AnswerQuestionWithResourceRecord Del %##s (%s) UnansweredQueries %d", - rr->name.c, DNSTypeName(rr->rrtype), rr->UnansweredQueries); + if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count + rr->CRActiveQuestion = q; // We know q is non-null + SetNextCacheCheckTime(m, rr); } -#endif - rr->LastUsed = timenow; - rr->UseCount++; - if (q->Callback) q->Callback(m, q, rr); + // CAUTION: MUST NOT do anything more with q after calling q->Callback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv() + // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions + // being deleted out from under them. + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (q->QuestionCallback) + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again } -// AnswerLocalQuestions is called from mDNSCoreReceiveResponse, -// and from TidyRRCache, which is called from mDNSCoreTask and from mDNSCoreReceiveResponse -// AnswerLocalQuestions is *never* called directly as a result of a client API call +// CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. // If new questions are created as a result of invoking client callbacks, they will be added to // the end of the question list, and m->NewQuestions will be set to indicate the first new question. -// rr is a ResourceRecord in our cache -// (kDNSRecordTypePacketAnswer/kDNSRecordTypePacketAdditional/kDNSRecordTypePacketUniqueAns/kDNSRecordTypePacketUniqueAdd) -// NOTE: AnswerLocalQuestions calls AnswerQuestionWithResourceRecord which can call a user callback, which may change -// the record list and/or question list. +// rr is a new CacheRecord just received into our cache +// (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). +// NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback, +// which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSlocal void AnswerLocalQuestions(mDNS *const m, ResourceRecord *rr, const mDNSs32 timenow) +mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) { - if (m->CurrentQuestion) debugf("AnswerLocalQuestions ERROR m->CurrentQuestion already set"); - m->CurrentQuestion = m->ActiveQuestions; + if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = m->Questions; while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) { DNSQuestion *q = m->CurrentQuestion; m->CurrentQuestion = q->next; - if (ResourceRecordAnswersQuestion(rr, q)) - AnswerQuestionWithResourceRecord(m, q, rr, timenow); + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + // If this question is one that's actively sending queries, and it's received ten answers within one second of sending the last + // query packet, then that indicates some radical network topology change, so reset its exponential backoff back to the start. + // We must be at least at the eight-second interval to do this. If we're at the four-second interval, or less, + // there's not much benefit accelerating because we will anyway send another query within a few seconds. + // The first reset query is sent out randomized over the next four seconds to reduce possible synchronization between machines. + if (ActiveQuestion(q) && ++q->RecentAnswers >= 10 && + q->ThisQInterval > InitialQuestionInterval*16 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) + { + LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence", + q->qname.c, DNSTypeName(q->qtype)); + q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); + q->ThisQInterval = InitialQuestionInterval; + SetNextQueryTime(m,q); + } + verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); + q->CurrentAnswers++; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); + // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() + } } m->CurrentQuestion = mDNSNULL; } -mDNSlocal void AnswerNewQuestion(mDNS *const m, const mDNSs32 timenow) +// CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute +// If new questions are created as a result of invoking client callbacks, they will be added to +// the end of the question list, and m->NewQuestions will be set to indicate the first new question. +// rr is an existing cache CacheRecord that just expired and is being deleted +// (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique). +// NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback, +// which may change the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) { - ResourceRecord *rr; - DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer - m->NewQuestions = q->next; // Advance NewQuestions to the next (if any) - - if (m->lock_rrcache) debugf("AnswerNewQuestion ERROR! Cache already locked!"); - // This should be safe, because calling the client's question callback may cause the - // question list to be modified, but should not ever cause the rrcache list to be modified. - // If the client's question callback deletes the question, then m->CurrentQuestion will - // be advanced, and we'll exit out of the loop - m->lock_rrcache = 1; - if (m->CurrentQuestion) debugf("AnswerNewQuestion ERROR m->CurrentQuestion already set"); - m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted - for (rr=m->rrcache; rr && m->CurrentQuestion == q; rr=rr->next) - if (ResourceRecordAnswersQuestion(rr, q)) + if (m->CurrentQuestion) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) { - mDNSu32 SecsSinceRcvd = ((mDNSu32)(timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; - if (rr->rroriginalttl <= SecsSinceRcvd) rr->rrremainingttl = 0; - else rr->rrremainingttl = rr->rroriginalttl - SecsSinceRcvd; - - // We only give positive responses to new questions. - // Since this question is new, it has not received any answers yet, so there's no point - // telling it about records that are going away that it never heard about in the first place. - if (rr->rrremainingttl > 0) - AnswerQuestionWithResourceRecord(m, q, rr, timenow); - // MUST NOT touch q again after calling AnswerQuestionWithResourceRecord() + verbosedebugf("CacheRecordRmv %p %##s (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + if (q->CurrentAnswers == 0) + LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q, q->qname.c, DNSTypeName(q->qtype)); + else + { + q->CurrentAnswers--; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; + } + if (q->CurrentAnswers == 0) + { + debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype)); + ReconfirmAntecedents(m, q); + } + AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse); + // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() } + } m->CurrentQuestion = mDNSNULL; - m->lock_rrcache = 0; } -mDNSlocal void FlushCacheRecords(mDNS *const m, mDNSIPAddr InterfaceAddr, const mDNSs32 timenow) +mDNSlocal void ReleaseCacheRR(mDNS *const m, CacheRecord *r) { - mDNSu32 count = 0; - ResourceRecord *rr; - for (rr = m->rrcache; rr; rr=rr->next) - { - if (rr->InterfaceAddr.NotAnInteger == InterfaceAddr.NotAnInteger) - { - // If the record's interface matches the one we're flushing, - // then pretend we just received a 'goodbye' packet for this record. - rr->TimeRcvd = timenow - mDNSPlatformOneSecond * 60; - rr->UnansweredQueries = 2; - rr->rroriginalttl = 0; - count++; - } - } - - if (count) debugf("FlushCacheRecords Flushing %d Cache Entries on interface %.4a", count, &InterfaceAddr); + if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->rdatastorage) + mDNSPlatformMemFree(r->resrec.rdata); + r->resrec.rdata = mDNSNULL; + r->next = m->rrcache_free; + m->rrcache_free = r; + m->rrcache_totalused--; } -// TidyRRCache -// Throw away any cache records that have passed their TTL -// First we prepare a list of records to delete, and pull them off the rrcache list -// Then we go through the list of records to delete, calling the user's question callbacks if necessary -// We do it in two phases like this to guard against the user's question callbacks modifying -// the rrcache list while we're walking it. -mDNSlocal void TidyRRCache(mDNS *const m, const mDNSs32 timenow) +mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) { - mDNSu32 count = 0; - ResourceRecord **rr = &m->rrcache; - ResourceRecord *deletelist = mDNSNULL; - - if (m->lock_rrcache) { debugf("TidyRRCache ERROR! Cache already locked!"); return; } + CacheRecord **rp = &(m->rrcache_hash[slot]); + + if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; } m->lock_rrcache = 1; - - while (*rr) + + while (*rp) { - mDNSu32 timesincercvd = (mDNSu32)(timenow - (*rr)->TimeRcvd); - if ((*rr)->rroriginalttl > timesincercvd / mDNSPlatformOneSecond) - rr=&(*rr)->next; // If TTL is greater than time elapsed, save this record - else + CacheRecord *const rr = *rp; + mDNSs32 event = RRExpireTime(rr); + if (m->timenow - event >= 0) // If expired, delete it + { + *rp = rr->next; // Cut it from the list + verbosedebugf("CheckCacheExpiration: Deleting %s", GetRRDisplayString(m, rr)); + if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away + { + CacheRecordRmv(m, rr); + m->rrcache_active--; + } + m->rrcache_used[slot]--; + ReleaseCacheRR(m, rr); + } + else // else, not expired; see if we need to query { - ResourceRecord *r = *rr; // Else, - *rr = r->next; // detatch this record from the cache list - r->next = deletelist; // and move it onto the list of things to delete - deletelist = r; - count++; + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + { + if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query + event = rr->NextRequiredQuery; // then just record when we want the next query + else // else trigger our question to go out now + { + // Set NextScheduledQuery to timenow so that SendQueries() will run. + // SendQueries() will see that we have records close to expiration, and send FEQs for them. + m->NextScheduledQuery = m->timenow; + // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(), + // which will correctly update m->NextCacheCheck for us + event = m->timenow + 0x3FFFFFFF; + } + } + if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0) + m->NextCacheCheck = (event + CacheCheckGracePeriod(rr)); + rp = &rr->next; } } - - if (count) verbosedebugf("TidyRRCache Deleting %d Expired Cache Entries", count); - m->lock_rrcache = 0; - - while (deletelist) - { - ResourceRecord *r = deletelist; - verbosedebugf("TidyRRCache: Deleted %##s (%s)", r->name.c, DNSTypeName(r->rrtype)); - deletelist = deletelist->next; - AnswerLocalQuestions(m, r, timenow); - r->next = m->rrcache_free; // and move it back to the free list - m->rrcache_free = r; - m->rrcache_used--; - } } -mDNSlocal ResourceRecord *GetFreeCacheRR(mDNS *const m, const mDNSs32 timenow) +mDNSlocal void AnswerNewQuestion(mDNS *const m) { - ResourceRecord *r = m->rrcache_free; + mDNSBool ShouldQueryImmediately = mDNStrue; + CacheRecord *rr; + DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer + mDNSu32 slot = HashSlot(&q->qname); + + verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - if (m->lock_rrcache) { debugf("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } + CheckCacheExpiration(m, slot); + m->NewQuestions = q->next; // Advance NewQuestions to the next *after* calling CheckCacheExpiration(); + + if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!"); + // This should be safe, because calling the client's question callback may cause the + // question list to be modified, but should not ever cause the rrcache list to be modified. + // If the client's question callback deletes the question, then m->CurrentQuestion will + // be advanced, and we'll exit out of the loop m->lock_rrcache = 1; - - if (r) // If there are records in the free list, take one - { - m->rrcache_free = r->next; - m->rrcache_used++; - if (m->rrcache_used >= m->rrcache_report) + if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted + for (rr=m->rrcache_hash[slot]; rr; rr=rr->next) + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) { - debugf("RR Cache now using %d records", m->rrcache_used); - m->rrcache_report *= 2; + // SecsSinceRcvd is whole number of elapsed seconds, rounded down + mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; + if (rr->resrec.rroriginalttl <= SecsSinceRcvd) + { + LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)", + rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + continue; // Go to next one in loop + } + + // If this record set is marked unique, then that means we can reasonably assume we have the whole set + // -- we don't need to rush out on the network and query immediately to see if there are more answers out there + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ShouldQueryImmediately = mDNSfalse; + q->CurrentAnswers++; + if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); + // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } - } - else // Else search for a candidate to recycle + + if (ShouldQueryImmediately && m->CurrentQuestion == q) { - ResourceRecord **rr = &m->rrcache; - ResourceRecord **best = mDNSNULL; - mDNSs32 bestage = -1; + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + m->NextScheduledQuery = m->timenow; + } + m->CurrentQuestion = mDNSNULL; + m->lock_rrcache = 0; + } - while (*rr) - { - mDNSs32 timesincercvd = timenow - (*rr)->TimeRcvd; +mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, mDNSBool AddRecord) + { + // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it + if (AddRecord) rr->AnnounceCount = InitialAnnounceCount - 1; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (q->QuestionCallback) + q->QuestionCallback(m, q, &rr->resrec, AddRecord); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } - // Records we've only just received are not candidates for deletion - if (timesincercvd > 0) - { - // Work out a weighted age, which is the number of seconds since this record was last used, - // divided by the number of times it has been used (we want to keep frequently used records longer). - mDNSs32 count = (*rr)->UseCount < 100 ? 1 + (mDNSs32)(*rr)->UseCount : 100; - mDNSs32 age = (timenow - (*rr)->LastUsed) / count; - mDNSu8 rtype = ((*rr)->RecordType) & ~kDNSRecordTypeUniqueMask; - if (rtype == kDNSRecordTypePacketAnswer) age /= 2; // Keep answer records longer than additionals +mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) + { + DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer + m->NewLocalOnlyQuestions = q->next; // Advance NewQuestions to the next (if any) - // Records that answer still-active questions are not candidates for deletion - if (bestage < age && !CacheRRActive(m, *rr)) { best = rr; bestage = age; } - } + debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted - rr=&(*rr)->next; + m->CurrentRecord = m->LocalOnlyRecords; + while (m->CurrentRecord && m->CurrentRecord != m->NewLocalOnlyRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue); + // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord() + if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } + } + + m->CurrentQuestion = mDNSNULL; + } - if (best) +mDNSlocal void AnswerLocalOnlyQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord) + { + if (m->CurrentQuestion) LogMsg("AnswerLocalOnlyQuestions ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = m->LocalOnlyQuestions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) { - r = *best; // Remember the record we chose - *best = r->next; // And detatch it from the free list + debugf("AnswerLocalOnlyQuestions %p %##s (%s) %lu", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); + AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); + // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() } } + m->CurrentQuestion = mDNSNULL; + } - m->lock_rrcache = 0; +mDNSlocal void DiscardLocalOnlyRecords(mDNS *const m) + { + AuthRecord *rr = m->LocalOnlyRecords; + while (rr) + { + if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) + { AnswerLocalOnlyQuestions(m, rr, mDNSfalse); CompleteDeregistration(m, rr); return; } + if (rr->ProbeCount) { mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); return; } + rr=rr->next; + } + m->DiscardLocalOnlyRecords = mDNSfalse; + } - if (r) mDNSPlatformMemZero(r, sizeof(*r)); - return(r); +mDNSlocal void AnswerForNewLocalOnlyRecords(mDNS *const m) + { + AuthRecord *rr = m->NewLocalOnlyRecords; + m->NewLocalOnlyRecords = m->NewLocalOnlyRecords->next; + AnswerLocalOnlyQuestions(m, rr, mDNStrue); } -mDNSlocal void ScheduleNextTask(const mDNS *const m) +mDNSlocal CacheRecord *GetFreeCacheRR(mDNS *const m, mDNSu16 RDLength) { - const mDNSs32 timenow = mDNSPlatformTimeNow(); - mDNSs32 nextevent = timenow + 0x78000000; - const char *msg = "No Event", *sign=""; - mDNSs32 interval, fraction; + CacheRecord *r = mDNSNULL; - DNSQuestion *q; - ResourceRecord *rr; - - if (m->mDNSPlatformStatus != mStatus_NoError) - return; + if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); } + m->lock_rrcache = 1; - // 1. If sleeping, do nothing - if (m->SleepState) + // If we have no free records, ask the client layer to give us some more memory + if (!m->rrcache_free && m->MainCallback) { - debugf("ScheduleNextTask: Sleeping"); - return; + if (m->rrcache_totalused != m->rrcache_size) + LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu", + m->rrcache_totalused, m->rrcache_size); + + // We don't want to be vulnerable to a malicious attacker flooding us with an infinite + // number of bogus records so that we keep growing our cache until the machine runs out of memory. + // To guard against this, if we're actively using less than 1/32 of our cache, then we + // purge all the unused records and recycle them, instead of allocating more memory. + if (m->rrcache_size >= 512 && m->rrcache_size / 32 > m->rrcache_active) + debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu", + m->rrcache_size, m->rrcache_active); + else + m->MainCallback(m, mStatus_GrowCache); } - // 2. If we have new questions added to the list, we need to answer them from cache ASAP - if (m->NewQuestions) - { - nextevent = timenow; - msg = "New Questions"; - } - else + // If we still have no free records, recycle all the records we can. + // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass. + if (!m->rrcache_free) { - // 3. Scan cache to see if any resource records are going to expire - for (rr = m->rrcache; rr; rr=rr->next) - { - mDNSs32 onetenth = ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond) / 10; - mDNSs32 t0 = rr->TimeRcvd + (mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond; - mDNSs32 t1 = t0 - onetenth; - mDNSs32 t2 = t1 - onetenth; - if (rr->UnansweredQueries < 1 && nextevent - t2 > 0 && CacheRRActive(m, rr)) - { - nextevent = t2; - msg = "Penultimate Query"; - } - else if (rr->UnansweredQueries < 2 && nextevent - t1 > 0 && CacheRRActive(m, rr)) - { - nextevent = t1; - msg = "Final Expiration Query"; - } - else if (nextevent - t0 > 0) - { - nextevent = t0; - msg = "Cache Tidying"; - } - } - - // 4. If we're suppressing sending right now, don't bother searching for packet generation events -- - // but do make sure we come back at the end of the suppression time to check again - if (m->SuppressSending) - { - if (nextevent - m->SuppressSending > 0) - { - nextevent = m->SuppressSending; - msg = "Send Suppressed Packets"; - } - } - else + #if MDNS_DEBUGMSGS + mDNSu32 oldtotalused = m->rrcache_totalused; + #endif + mDNSu32 slot; + CacheRecord **rr; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) { - // 5. Scan list of active questions to see if we need to send any queries - for (q = m->ActiveQuestions; q; q=q->next) - if (TimeToSendThisQuestion(q, nextevent)) - { - nextevent = q->NextQTime; - msg = "Send Questions"; - } - - // 6. Scan list of local resource records to see if we have any - // deregistrations, probes, announcements, or replies to send - for (rr = m->ResourceRecords; rr; rr=rr->next) + rr = &(m->rrcache_hash[slot]); + while (*rr) { - if (rr->RecordType == kDNSRecordTypeDeregistering) - { - nextevent = timenow; - msg = "Send Deregistrations"; - } - else if (rr->SendPriority >= kDNSSendPriorityAnswer && ResourceRecordIsValidAnswer(rr)) - { - nextevent = timenow; - msg = "Send Answers"; - } - else if (rr->RecordType == kDNSRecordTypeUnique && nextevent - rr->NextSendTime > 0) - { - nextevent = rr->NextSendTime; - msg = "Send Probes"; - } - else if (rr->AnnounceCount && nextevent - rr->NextSendTime > 0 && ResourceRecordIsValidAnswer(rr)) + // Records that answer still-active questions are not candidates for deletion + if ((*rr)->CRActiveQuestion) + rr=&(*rr)->next; + else { - nextevent = rr->NextSendTime; - msg = "Send Announcements"; + CacheRecord *r = *rr; + *rr = (*rr)->next; // Cut record from list + m->rrcache_used[slot]--; // Decrement counts + ReleaseCacheRR(m, r); } } } + #if MDNS_DEBUGMSGS + debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused); + #endif + } + + if (m->rrcache_free) // If there are records in the free list, take one + { + r = m->rrcache_free; + m->rrcache_free = r->next; + } + + if (r) + { + if (++m->rrcache_totalused >= m->rrcache_report) + { + debugf("RR Cache now using %ld records", m->rrcache_totalused); + if (m->rrcache_report < 100) m->rrcache_report += 10; + else m->rrcache_report += 100; + } + mDNSPlatformMemZero(r, sizeof(*r)); + r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage + + if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage + { + r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); + if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; + else { ReleaseCacheRR(m, r); r = mDNSNULL; } + } } - interval = nextevent - timenow; - if (interval < 0) { interval = -interval; sign = "-"; } - fraction = interval % mDNSPlatformOneSecond; - verbosedebugf("ScheduleNextTask: Next event: <%s> in %s%d.%03d seconds", msg, sign, - interval / mDNSPlatformOneSecond, fraction * 1000 / mDNSPlatformOneSecond); + m->lock_rrcache = 0; + + return(r); + } - mDNSPlatformScheduleTask(m, nextevent); +mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) + { + // Make sure we mark this record as thoroughly expired -- we don't ever want to give + // a positive answer using an expired record (e.g. from an interface that has gone away). + // We don't want to clear CRActiveQuestion here, because that would leave the record subject to + // summary deletion without giving the proper callback to any questions that are monitoring it. + // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries. + rr->TimeRcvd = m->timenow - mDNSPlatformOneSecond * 60; + rr->UnansweredQueries = MaxUnansweredQueries; + rr->resrec.rroriginalttl = 0; + SetNextCacheCheckTime(m, rr); } -mDNSlocal mDNSs32 mDNS_Lock(mDNS *const m) +mDNSlocal void mDNS_Lock(mDNS *const m) { + // MUST grab the platform lock FIRST! mDNSPlatformLock(m); - ++m->mDNS_busy; - return(mDNSPlatformTimeNow()); + + // Normally, mDNS_reentrancy is zero and so is mDNS_busy + // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too + // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one + // If mDNS_busy != mDNS_reentrancy that's a bad sign + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // If this is an initial entry into the mDNSCore code, set m->timenow + // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set + if (m->mDNS_busy == 0) + { + if (m->timenow) + LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNSPlatformTimeNow() + m->timenow_adjust); + m->timenow = mDNSPlatformTimeNow() + m->timenow_adjust; + if (m->timenow == 0) m->timenow = 1; + } + else if (m->timenow == 0) + { + LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); + m->timenow = mDNSPlatformTimeNow() + m->timenow_adjust; + if (m->timenow == 0) m->timenow = 1; + } + + if (m->timenow_last - m->timenow > 0) + { + m->timenow_adjust += m->timenow_last - m->timenow; + LogMsg("mDNSPlatformTimeNow went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust); + m->timenow = m->timenow_last; + } + m->timenow_last = m->timenow; + + // Increment mDNS_busy so we'll recognise re-entrant calls + m->mDNS_busy++; + } + +mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) + { + mDNSs32 e = m->timenow + 0x78000000; + if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) return(e); + if (m->NewQuestions) return(m->timenow); + if (m->NewLocalOnlyQuestions) return(m->timenow); + if (m->NewLocalOnlyRecords) return(m->timenow); + if (m->DiscardLocalOnlyRecords) return(m->timenow); + if (m->SuppressSending) return(m->SuppressSending); + if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; + if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; + if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; + if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; + return(e); } mDNSlocal void mDNS_Unlock(mDNS *const m) { - // Upon unlocking, we've usually added some new work to the task list. - // If we don't decrement mDNS_busy to zero, then we don't have to worry about calling - // ScheduleNextTask(), because the last lock holder will do it for us on the way out. - if (--m->mDNS_busy == 0) ScheduleNextTask(m); + // Decrement mDNS_busy + m->mDNS_busy--; + + // Check for locking failures + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow + if (m->mDNS_busy == 0) + { + m->NextScheduledEvent = GetNextScheduledEvent(m); + if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero"); + m->timenow = 0; + } + + // MUST release the platform lock LAST! mDNSPlatformUnlock(m); } -mDNSexport void mDNSCoreTask(mDNS *const m) +mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) { - const mDNSs32 timenow = mDNS_Lock(m); + mDNS_Lock(m); // Must grab lock before trying to read m->timenow + + if (m->timenow - m->NextScheduledEvent >= 0) + { + int i; - verbosedebugf("mDNSCoreTask"); - if (m->mDNS_busy > 1) debugf("mDNSCoreTask: Locking failure! mDNS already busy"); - if (m->CurrentQuestion) debugf("mDNSCoreTask: ERROR! m->CurrentQuestion already set"); + verbosedebugf("mDNS_Execute"); + if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set"); + + // 1. If we're past the probe suppression time, we can clear it + if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0; + + // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter + if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0; + + // 3. Purge our cache of stale old records + if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0) + { + mDNSu32 slot; + m->NextCacheCheck = m->timenow + 0x3FFFFFFF; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) CheckCacheExpiration(m, slot); + } - if (m->SuppressProbes && timenow - m->SuppressProbes >= 0) - m->SuppressProbes = 0; + // 4. See if we can answer any of our new local questions from the cache + for (i=0; m->NewQuestions && i<1000; i++) AnswerNewQuestion(m); + if (i >= 1000) debugf("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); + + for (i=0; m->DiscardLocalOnlyRecords && i<1000; i++) DiscardLocalOnlyRecords(m); + if (i >= 1000) debugf("mDNS_Execute: DiscardLocalOnlyRecords exceeded loop limit"); - if (m->NumFailedProbes && timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) - m->NumFailedProbes = 0; + for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m); + if (i >= 1000) debugf("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit"); - // 1. See if we can answer any of our new local questions from the cache - while (m->NewQuestions) AnswerNewQuestion(m, timenow); + for (i=0; m->NewLocalOnlyRecords && i<1000; i++) AnswerForNewLocalOnlyRecords(m); + if (i >= 1000) debugf("mDNS_Execute: AnswerLocalOnlyQuestions exceeded loop limit"); - // 2. See what packets we need to send - if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) - { - // If the platform code is currently non-operational, - // then we'll just complete deregistrations immediately, - // without waiting for the goodbye packet to be sent - DiscardDeregistrations(m, timenow); - } - else if (m->SuppressSending == 0 || timenow - m->SuppressSending >= 0) - { - int i; - // If the platform code is ready, - // and we're not suppressing packet generation right now - // send our responses, probes, and questions - m->SuppressSending = 0; - for (i=0; i<100 && HaveResponses(m, timenow); i++) SendResponses(m, timenow); - if (i >= 100) LogErrorMessage("mDNSCoreTask: HaveResponses returned true %d times", i); - for (i=0; i<100 && HaveQueries (m, timenow); i++) SendQueries (m, timenow); - if (i >= 100) LogErrorMessage("mDNSCoreTask: HaveQueries returned true %d times", i); - } + // 5. See what packets we need to send + if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) DiscardDeregistrations(m); + else if (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0) + { + // If the platform code is ready, and we're not suppressing packet generation right now + // then send our responses, probes, and questions. + // We check the cache first, because there might be records close to expiring that trigger questions to refresh them + // We send queries next, because there might be final-stage probes that complete their probing here, causing + // them to advance to announcing state, and we want those to be included in any announcements we send out. + // Finally, we send responses, including the previously mentioned records that just completed probing + m->SuppressSending = 0; + + // 6. Send Query packets. This may cause some probing records to advance to announcing state + if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m); + if (m->timenow - m->NextScheduledQuery >= 0) + { + LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second"); + m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond; + } + if (m->timenow - m->NextScheduledProbe >= 0) + { + LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second"); + m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond; + } + + // 7. Send Response packets, including probing records just advanced to announcing state + if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m); + if (m->timenow - m->NextScheduledResponse >= 0) + { + LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second"); + m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond; + } + } - if (m->rrcache_size) TidyRRCache(m, timenow); + m->RandomQueryDelay = 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary + } - mDNS_Unlock(m); + // Note about multi-threaded systems: + // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(), + // performing mDNS API operations that change our next scheduled event time. + // + // On multi-threaded systems (like the current Windows implementation) that have a single main thread + // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility + // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will + // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one + // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful + // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it + // does, the state of the signal will be noticed, causing the blocking primitive to return immediately + // without blocking. This avoids the race condition between the signal from the other thread arriving + // just *before* or just *after* the main thread enters the blocking primitive. + // + // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven, + // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to + // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer + // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale + // by the time it gets to the timer callback function). + + mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value + return(m->NextScheduledEvent); } -mDNSexport void mDNSCoreSleep(mDNS *const m, mDNSBool sleepstate) +// Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep. +// Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up. +// Normally, the platform support layer below mDNSCore should call this, not the client layer above. +// Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call +// mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just +// found itself in a new network environment. For example, if the Ethernet hardware indicates that the +// cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse) +// to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc. +// While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network +// traffic, so it should only be called when there is legitimate reason to believe the machine +// may have become attached to a new network. +mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) { - ResourceRecord *rr; - const mDNSs32 timenow = mDNS_Lock(m); + AuthRecord *rr; + + mDNS_Lock(m); m->SleepState = sleepstate; - debugf("mDNSCoreSleep: %d", sleepstate); + LogMsg("mDNSResponder %s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow); if (sleepstate) { - // First mark all the records we need to deregister + // Mark all the records we need to deregister and send them for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->RecordType == kDNSRecordTypeShared && rr->AnnounceCount <= DefaultAnnounceCountForTypeShared) - rr->rrremainingttl = 0; - while (HaveResponses(m, timenow)) SendResponses(m, timenow); + if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->AnnounceCount < InitialAnnounceCount) + rr->ImmedAnswer = mDNSInterfaceMark; + SendResponses(m); } else { DNSQuestion *q; + mDNSu32 slot; + CacheRecord *cr; + + // 1. Retrigger all our questions + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (ActiveQuestion(q)) + { + q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + q->LastQTime = m->timenow - q->ThisQInterval; + q->RecentAnswers = 0; + ExpireDupSuppressInfo(q->DupSuppress, m->timenow); + m->NextScheduledQuery = m->timenow; + } + // 2. Re-validate our cache records + m->NextCacheCheck = m->timenow; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for (cr = m->rrcache_hash[slot]; cr; cr=cr->next) + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForCableDisconnect); + + // 3. Retrigger probing and announcing for all our authoritative records for (rr = m->ResourceRecords; rr; rr=rr->next) { - if (rr->RecordType == kDNSRecordTypeVerified) rr->RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForRecordType(rr->RecordType); - rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType); - rr->NextSendTime = timenow; - rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType); + if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + if (rr->AnnounceCount < ReannounceCount) + rr->AnnounceCount = ReannounceCount; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m, rr); } - for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions - if (q->ThisQInterval > 0 && !q->DuplicateOf) - { - q->NextQTime = timenow; - q->ThisQInterval = mDNSPlatformOneSecond; // MUST NOT be zero for an active question - q->NextQInterval = mDNSPlatformOneSecond; - } + } mDNS_Unlock(m); } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Packet Reception Functions #endif -mDNSlocal mDNSBool AddRecordToResponseList(ResourceRecord **nrp, - ResourceRecord *rr, const mDNSu8 *answerto, ResourceRecord *additionalto) +mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add) { - if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) + if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse) { - *nrp = rr; - rr->NR_AnswerTo = answerto; - rr->NR_AdditionalTo = additionalto; - return(mDNStrue); + **nrpp = rr; + // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo) + // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does + // The referenced record will definitely be acceptable (by recursive application of this rule) + if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo; + rr->NR_AdditionalTo = add; + *nrpp = &rr->NextResponse; } - else debugf("AddRecordToResponseList: %##s (%s) already in list", rr->name.c, DNSTypeName(rr->rrtype)); - return(mDNSfalse); + debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo) mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end, - const mDNSIPAddr InterfaceAddr, DNSMessage *const reply, ResourceRecord *ResponseRecords) + const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords) { - const mDNSu8 *const limit = reply->data + sizeof(reply->data); + mDNSu8 *responseptr = response->data; + const mDNSu8 *const limit = response->data + sizeof(response->data); const mDNSu8 *ptr = query->data; - mDNSu8 *responseptr = reply->data; - ResourceRecord *rr; + AuthRecord *rr; + mDNSu32 maxttl = 0x70000000; int i; // Initialize the response fields so we can answer the questions - InitializeDNSMessage(&reply->h, query->h.id, ResponseFlags); + InitializeDNSMessage(&response->h, query->h.id, ResponseFlags); // *** // *** 1. Write out the list of questions we are actually going to answer with this packet // *** - for (i=0; ih.numQuestions; i++) // For each question... + if (LegacyQuery) { - DNSQuestion q; - ptr = getQuestion(query, ptr, end, InterfaceAddr, &q); // get the question... - if (!ptr) return(mDNSNULL); - - for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers + maxttl = 10; + for (i=0; ih.numQuestions; i++) // For each question... { - if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question - { // then put the question in the question section - responseptr = putQuestion(reply, responseptr, limit, &q.name, q.rrtype, q.rrclass); - if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); } - break; // break out of the ResponseRecords loop, and go on to the next question + DNSQuestion q; + ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question... + if (!ptr) return(mDNSNULL); + + for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers + { + if (rr->NR_AnswerTo == ptr) // If we're going to generate a record answering this question + { // then put the question in the question section + responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass); + if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); } + break; // break out of the ResponseRecords loop, and go on to the next question + } } } + + if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); } } - if (reply->h.numQuestions == 0) { debugf("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); } + // *** + // *** 2. Write Answers + // *** + for (rr=ResponseRecords; rr; rr=rr->NextResponse) + if (rr->NR_AnswerTo) + { + mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, maxttl); + if (p) responseptr = p; + else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; } + } // *** - // *** 2. Write answers and additionals + // *** 3. Write Additionals // *** for (rr=ResponseRecords; rr; rr=rr->NextResponse) - { - if (MustSendRecord(rr)) + if (rr->NR_AdditionalTo && !rr->NR_AnswerTo) { - if (rr->NR_AnswerTo) - { - mDNSu8 *p = putResourceRecord(reply, responseptr, &reply->h.numAnswers, rr, mDNSNULL, 0); - if (p) responseptr = p; - else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); reply->h.flags.b[0] |= kDNSFlag0_TC; } - } - else - { - mDNSu8 *p = putResourceRecord(reply, responseptr, &reply->h.numAdditionals, rr, mDNSNULL, 0); - if (p) responseptr = p; - else debugf("GenerateUnicastResponse: No more space for additionals"); - } + mDNSu8 *p = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, maxttl); + if (p) responseptr = p; + else debugf("GenerateUnicastResponse: No more space for additionals"); } - } + return(responseptr); } -// ResourceRecord *pktrr is the ResourceRecord from the response packet we've witnessed on the network -// ResourceRecord *rr is our ResourceRecord +// AuthRecord *our is our Resource Record +// CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network // Returns 0 if there is no conflict // Returns +1 if there was a conflict and we won // Returns -1 if there was a conflict and we lost and have to rename -mDNSlocal int CompareRData(ResourceRecord *pkt, ResourceRecord *our) +mDNSlocal int CompareRData(AuthRecord *our, CacheRecord *pkt) { - mDNSu8 pktdata[256], *pktptr = pktdata, *pktend; mDNSu8 ourdata[256], *ourptr = ourdata, *ourend; - if (!pkt) { debugf("CompareRData ERROR: pkt is NULL"); return(+1); } - if (!our) { debugf("CompareRData ERROR: our is NULL"); return(+1); } - - pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), pkt->rrtype, pkt->rdata); - ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), our->rrtype, our->rdata); - while (pktptr < pktend && ourptr < ourend && *pktptr == *ourptr) { pktptr++; ourptr++; } - if (pktptr >= pktend && ourptr >= ourend) return(0); // If data identical, not a conflict - - if (pktptr >= pktend) return(-1); // Packet data is substring; We lost - if (ourptr >= ourend) return(+1); // Our data is substring; We won - if (*pktptr < *ourptr) return(-1); // Packet data is numerically lower; We lost - if (*pktptr > *ourptr) return(+1); // Our data is numerically lower; We won + mDNSu8 pktdata[256], *pktptr = pktdata, *pktend; + if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); } + if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); } + + ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec); + pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec); + while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; } + if (ourptr >= ourend && pktptr >= pktend) return(0); // If data identical, not a conflict + + if (ourptr >= ourend) return(-1); // Our data ran out first; We lost + if (pktptr >= pktend) return(+1); // Packet data ran out first; We won + if (*pktptr > *ourptr) return(-1); // Our data is numerically lower; We lost + if (*pktptr < *ourptr) return(+1); // Packet data is numerically lower; We won debugf("CompareRData: How did we get here?"); return(-1); } -// Find the canonical DependentOn record for this RR received in a packet. +// See if we have an authoritative record that's identical to this packet record, +// whose canonical DependentOn record is the specified master record. // The DependentOn pointer is typically used for the TXT record of service registrations // It indicates that there is no inherent conflict detection for the TXT record // -- it depends on the SRV record to resolve name conflicts -// If we find any identical ResourceRecord in our authoritative list, then follow its DependentOn -// pointers (if any) to make sure we return the canonical DependentOn record +// If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn +// pointer chain (if any) to make sure we reach the canonical DependentOn record // If the record has no DependentOn, then just return that record's pointer // Returns NULL if we don't have any local RRs that are identical to the one from the packet -mDNSlocal const ResourceRecord *FindDependentOn(const mDNS *const m, const ResourceRecord *const pktrr) +mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master) { - const ResourceRecord *rr; - for (rr = m->ResourceRecords; rr; rr=rr->next) + const AuthRecord *r1; + for (r1 = m->ResourceRecords; r1; r1=r1->next) { - if (IdenticalResourceRecordAnyInterface(rr, pktrr)) + if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) { - while (rr->DependentOn) rr = rr->DependentOn; - return(rr); + const AuthRecord *r2 = r1; + while (r2->DependentOn) r2 = r2->DependentOn; + if (r2 == master) return(mDNStrue); } } - return(mDNSNULL); + for (r1 = m->DuplicateRecords; r1; r1=r1->next) + { + if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec)) + { + const AuthRecord *r2 = r1; + while (r2->DependentOn) r2 = r2->DependentOn; + if (r2 == master) return(mDNStrue); + } + } + return(mDNSfalse); } // Find the canonical RRSet pointer for this RR received in a packet. -// If we find any identical ResourceRecord in our authoritative list, then follow its RRSet +// If we find any identical AuthRecord in our authoritative list, then follow its RRSet // pointers (if any) to make sure we return the canonical member of this name/type/class // Returns NULL if we don't have any local RRs that are identical to the one from the packet -mDNSlocal const ResourceRecord *FindRRSet(const mDNS *const m, const ResourceRecord *const pktrr) +mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr) { - const ResourceRecord *rr; + const AuthRecord *rr; for (rr = m->ResourceRecords; rr; rr=rr->next) { - if (IdenticalResourceRecordAnyInterface(rr, pktrr)) + if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec)) { while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet; return(rr); @@ -2605,15 +4853,15 @@ mDNSlocal const ResourceRecord *FindRRSet(const mDNS *const m, const ResourceRec // TXT records, and that record is marked as dependent on 'our', its SRV record). // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record // are members of the same RRSet, then this is not a conflict. -mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const ResourceRecord *const our, const ResourceRecord *const pktrr) +mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr) { - const ResourceRecord *ourset = our->RRSet ? our->RRSet : our; + const AuthRecord *ourset = our->RRSet ? our->RRSet : our; // If not supposed to be unique, not a conflict - if (!(our->RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse); + if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse); // If a dependent record, not a conflict - if (our->DependentOn || FindDependentOn(m, pktrr) == our) return(mDNSfalse); + if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse); // If the pktrr matches a member of ourset, not a conflict if (FindRRSet(m, pktrr) == ourset) return(mDNSfalse); @@ -2626,7 +4874,7 @@ mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const ResourceRecord *c // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, - DNSQuestion *q, ResourceRecord *our, const mDNSs32 timenow) + DNSQuestion *q, AuthRecord *our) { int i; const mDNSu8 *ptr = LocateAuthorities(query, end); @@ -2634,48 +4882,60 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q for (i = 0; i < query->h.numAuthorities; i++) { - ResourceRecord pktrr; - ptr = getResourceRecord(query, ptr, end, q->InterfaceAddr, 0, 0, &pktrr, mDNSNULL); + LargeCacheRecord pkt; + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, &pkt); if (!ptr) break; - if (ResourceRecordAnswersQuestion(&pktrr, q)) + if (ResourceRecordAnswersQuestion(&pkt.r.resrec, q)) { FoundUpdate = mDNStrue; - if (PacketRRConflict(m, our, &pktrr)) + if (PacketRRConflict(m, our, &pkt.r)) { - int result = (int)pktrr.rrclass - (int)our->rrclass; - if (!result) result = (int)pktrr.rrtype - (int)our->rrtype; - if (!result) result = CompareRData(&pktrr, our); + int result = (int)our->resrec.rrclass - (int)pkt.r.resrec.rrclass; + if (!result) result = (int)our->resrec.rrtype - (int)pkt.r.resrec.rrtype; + if (!result) result = CompareRData(our, &pkt.r); switch (result) { - case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->name.c, DNSTypeName(our->rrtype)); + case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); break; case 0: break; - case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->name.c, DNSTypeName(our->rrtype)); - mDNS_Deregister_internal(m, our, timenow, mDNS_Dereg_conflict); + case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); + mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict); return; } } } } if (!FoundUpdate) - debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->name.c, DNSTypeName(our->rrtype)); + debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); + } + +mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr) + { + CacheRecord *rr; + for (rr = m->rrcache_hash[HashSlot(&pktrr->name)]; rr; rr=rr->next) + if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break; + return(rr); } // ProcessQuery examines a received query to see if we have any answers to give mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, - const mDNSIPAddr srcaddr, const mDNSIPAddr InterfaceAddr, - DNSMessage *const replyunicast, mDNSBool replymulticast, const mDNSs32 timenow) + const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, + DNSMessage *const response) { - ResourceRecord *ResponseRecords = mDNSNULL; - ResourceRecord **nrp = &ResponseRecords; + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated + CacheRecord **eap = &ExpectedAnswers; + DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet + DNSQuestion **dqp = &DupQuestions; mDNSBool delayresponse = mDNSfalse; - mDNSBool answers = mDNSfalse; + mDNSBool HaveUnicastAnswer = mDNSfalse; const mDNSu8 *ptr = query->data; mDNSu8 *responseptr = mDNSNULL; - ResourceRecord *rr, *rr2; + AuthRecord *rr, *rr2; int i; - // If TC flag is set, it means we should expect additional duplicate suppression info may be coming in another packet. + // If TC flag is set, it means we should expect that additional known answers may be coming in another packet. if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNStrue; // *** @@ -2683,36 +4943,102 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** for (i=0; ih.numQuestions; i++) // For each question... { + mDNSBool QuestionNeedsMulticastResponse; int NumAnswersForThisQuestion = 0; - DNSQuestion q; - ptr = getQuestion(query, ptr, end, InterfaceAddr, &q); // get the question... + DNSQuestion pktq, *q; + ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question... if (!ptr) goto exit; + + // The only queries that *need* a multicast response are: + // * Queries sent via multicast + // * from port 5353 + // * that don't have the kDNSQClass_UnicastResponse bit set + // These queries need multicast responses because other clients will: + // * suppress their own identical questions when they see these questions, and + // * expire their cache records if they don't see the expected responses + // For other queries, we may still choose to send the occasional multicast response anyway, + // to keep our neighbours caches warm, and for ongoing conflict detection. + QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse); + // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later + pktq.qclass &= ~kDNSQClass_UnicastResponse; // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe // can result in user callbacks which may change the record list and/or question list. // Also note: we just mark potential answer records here, without trying to build the // "ResponseRecords" list, because we don't want to risk user callbacks deleting records - // from that list while we're in the middle of trying to build it. - if (m->CurrentRecord) debugf("ProcessQuery ERROR m->CurrentRecord already set"); + // from that list while we're in the middle of trying to build it. + if (m->CurrentRecord) LogMsg("ProcessQuery ERROR m->CurrentRecord already set"); m->CurrentRecord = m->ResourceRecords; while (m->CurrentRecord) { rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (ResourceRecordAnswersQuestion(rr, &q)) + if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq)) { - if (rr->RecordType == kDNSRecordTypeUnique) - ResolveSimultaneousProbe(m, query, end, &q, rr, timenow); + if (rr->resrec.RecordType == kDNSRecordTypeUnique) + ResolveSimultaneousProbe(m, query, end, &pktq, rr); else if (ResourceRecordIsValidAnswer(rr)) { NumAnswersForThisQuestion++; - if (!rr->NR_AnswerTo) rr->NR_AnswerTo = ptr; // Mark as potential answer + // Notes: + // NR_AnswerTo pointing into query packet means "answer via unicast" + // (may also choose to do multicast as well) + // NR_AnswerTo == ~0 means "definitely answer via multicast" (can't downgrade to unicast later) + if (QuestionNeedsMulticastResponse) + { + // We only mark this question for sending if it is at least one second since the last time we multicast it + // on this interface. If it is more than a second, or LastMCInterface is different, then we should multicast it. + // This is to guard against the case where someone blasts us with queries as fast as they can. + if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 || + (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) + rr->NR_AnswerTo = (mDNSu8*)~0; + } + else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = ptr; } } } - // If we couldn't answer this question, someone else might be able to, - // so use random delay on response to reduce collisions - if (NumAnswersForThisQuestion == 0) delayresponse = mDNStrue; + + // We only do the following accelerated cache expiration processing and duplicate question suppression processing + // for multicast queries with multicast responses. + // For any query generating a unicast response we don't do this because we can't assume we will see the response + if (QuestionNeedsMulticastResponse) + { + CacheRecord *rr; + // If we couldn't answer this question, someone else might be able to, + // so use random delay on response to reduce collisions + if (NumAnswersForThisQuestion == 0) delayresponse = mDNStrue; + + // Make a list indicating which of our own cache records we expect to see updated as a result of this query + // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated + for (rr = m->rrcache_hash[HashSlot(&pktq.qname)]; rr; rr=rr->next) + if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && rr->resrec.rdlength <= SmallRecordLimit) + if (!rr->NextInKAList && eap != &rr->NextInKAList) + { + *eap = rr; + eap = &rr->NextInKAList; + if (rr->MPUnansweredQ == 0 || m->timenow - rr->MPLastUnansweredQT >= mDNSPlatformOneSecond) + { + // Although MPUnansweredQ is only really used for multi-packet query processing, + // we increment it for both single-packet and multi-packet queries, so that it stays in sync + // with the MPUnansweredKA value, which by necessity is incremented for both query types. + rr->MPUnansweredQ++; + rr->MPLastUnansweredQT = m->timenow; + rr->MPExpectingKA = mDNStrue; + } + } + + // Check if this question is the same as any of mine. + // We only do this for non-truncated queries. Right now it would be too complicated to try + // to keep track of duplicate suppression state between multiple packets, especially when we + // can't guarantee to receive all of the Known Answer packets that go with a particular query. + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) + for (q = m->Questions; q; q=q->next) + if (ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) + if (!q->InterfaceID || q->InterfaceID == InterfaceID) + if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) + if (q->qtype == pktq.qtype && q->qclass == pktq.qclass && q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) + { *dqp = q; dqp = &q->NextInDQList; } + } } // *** @@ -2720,11 +5046,10 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** for (rr = m->ResourceRecords; rr; rr=rr->next) // Now build our list of potential answers if (rr->NR_AnswerTo) // If we marked the record... - if (AddRecordToResponseList(nrp, rr, rr->NR_AnswerTo, mDNSNULL)) // ... add it to the list - { - nrp = &rr->NextResponse; - if (rr->RecordType == kDNSRecordTypeShared) delayresponse = mDNStrue; - } + { + AddRecordToResponseList(&nrp, rr, mDNSNULL); // ... add it to the list + if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNStrue; + } // *** // *** 3. Add additional records @@ -2733,50 +5058,89 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { // (Note: This is an "if", not a "while". If we add a record, we'll find it again // later in the "for" loop, and we will follow further "additional" links then.) - if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceAddr) && - AddRecordToResponseList(nrp, rr->Additional1, mDNSNULL, rr)) - nrp = &rr->Additional1->NextResponse; + if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID)) + AddRecordToResponseList(&nrp, rr->Additional1, rr); - if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceAddr) && - AddRecordToResponseList(nrp, rr->Additional2, mDNSNULL, rr)) - nrp = &rr->Additional2->NextResponse; + if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) + AddRecordToResponseList(&nrp, rr->Additional2, rr); // For SRV records, automatically add the Address record(s) for the target host - if (rr->rrtype == kDNSType_SRV) + if (rr->resrec.rrtype == kDNSType_SRV) for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records - if (rr2->rrtype == kDNSType_A && // For all records type "A" ... - ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceAddr) && // ... which are valid for answer ... - SameDomainName(&rr->rdata->u.srv.target, &rr2->name) && // ... whose name is the name of the SRV target - AddRecordToResponseList(nrp, rr2, mDNSNULL, rr)) - nrp = &rr2->NextResponse; + if (RRIsAddressType(rr2) && // For all address records (A/AAAA) ... + ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... + rr->resrec.rdnamehash == rr2->resrec.namehash && + SameDomainName(&rr->resrec.rdata->u.srv.target, &rr2->resrec.name)) // ... whose name is the name of the SRV target + AddRecordToResponseList(&nrp, rr2, rr); } // *** - // *** 4. Parse Answer Section and cancel any records disallowed by duplicate suppression + // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list // *** for (i=0; ih.numAnswers; i++) // For each record in the query's answer section... { // Get the record... - ResourceRecord pktrr, *rr; - ptr = getResourceRecord(query, ptr, end, InterfaceAddr, timenow, kDNSRecordTypePacketAnswer, &pktrr, mDNSNULL); + LargeCacheRecord pkt; + AuthRecord *rr; + CacheRecord *ourcacherr; + ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); if (!ptr) goto exit; - // See if it suppresses any of our planned answers + // See if this Known-Answer suppresses any of our currently planned answers for (rr=ResponseRecords; rr; rr=rr->NextResponse) - if (MustSendRecord(rr) && SuppressDuplicate(&pktrr, rr)) + if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&pkt.r, rr)) { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } - // And see if it suppresses any previously scheduled answers + // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression) for (rr=m->ResourceRecords; rr; rr=rr->next) { - // If this record has been requested by exactly one client, and that client is - // the same one sending this query, then allow inter-packet duplicate suppression - if (rr->Requester.NotAnInteger && rr->Requester.NotAnInteger == srcaddr.NotAnInteger) - if (SuppressDuplicate(&pktrr, rr)) + // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression + if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&pkt.r, rr)) + { + if (srcaddr->type == mDNSAddrType_IPv4) { - rr->SendPriority = 0; - rr->Requester = zeroIPAddr; + if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zeroIPAddr; } + else if (srcaddr->type == mDNSAddrType_IPv6) + { + if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr; + } + if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) rr->ImmedAnswer = mDNSNULL; + } + } + + // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always, + // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list). + ourcacherr = FindIdenticalRecordInCache(m, &pkt.r.resrec); + if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) + { + ourcacherr->MPUnansweredKA++; + ourcacherr->MPExpectingKA = mDNSfalse; + } + + // Having built our ExpectedAnswers list from the questions in this packet, we can definitively + // remove from our ExpectedAnswers list any records that are suppressed in the very same packet. + // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them. + eap = &ExpectedAnswers; + while (*eap) + { + CacheRecord *rr = *eap; + if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec)) + { *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; } + else eap = &rr->NextInKAList; + } + + // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query. + if (!ourcacherr) + { + dqp = &DupQuestions; + while (*dqp) + { + DNSQuestion *q = *dqp; + if (ResourceRecordAnswersQuestion(&pkt.r.resrec, q)) + { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } + else dqp = &q->NextInDQList; + } } } @@ -2792,42 +5156,52 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** for (rr=ResponseRecords; rr; rr=rr->NextResponse) { - if (MustSendRecord(rr)) + if (rr->NR_AnswerTo) { - // For oversized records which we are going to send back to the requester via unicast - // anyway, don't waste network bandwidth by also sending them via multicast. - // This means we lose passive conflict detection for these oversized records, but - // that is a reasonable tradeoff -- these large records usually have an associated - // SRV record with the same name which will catch conflicts for us anyway. - mDNSBool LargeRecordWithUnicastReply = (rr->rdestimate > 1024 && replyunicast); - - if (rr->NR_AnswerTo) - answers = mDNStrue; - - if (replymulticast && !LargeRecordWithUnicastReply) + mDNSBool SendMulticastResponse = mDNSfalse; + + // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc. + if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0) SendMulticastResponse = mDNStrue; + + // If the client insists on a multicast response, then we'd better send one + if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue; + else if (rr->NR_AnswerTo) HaveUnicastAnswer = mDNStrue; + + if (SendMulticastResponse) { - // If this query has additional duplicate suppression info - // coming in another packet, then remember the requesting IP address - if (query->h.flags.b[0] & kDNSFlag0_TC) - { - // We can only store one IP address at a time per record, so if we've already - // stored one address, set it to some special distinguished value instead - if (rr->Requester.NotAnInteger == zeroIPAddr.NotAnInteger) rr->Requester = srcaddr; - else rr->Requester = onesIPAddr; - } - if (rr->NR_AnswerTo) + // If we're already planning to send this on another interface, just send it on all interfaces + if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID) { - // This is a direct answer in response to one of the questions - rr->SendPriority = kDNSSendPriorityAnswer; + rr->ImmedAnswer = mDNSInterfaceMark; + m->NextScheduledResponse = m->timenow; + debugf("ProcessQuery: %##s (%s) : Will send on all interfaces", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } else { - // This is an additional record supporting one of our answers - if (rr->SendPriority < kDNSSendPriorityAdditional) - rr->SendPriority = kDNSSendPriorityAdditional; + rr->ImmedAnswer = InterfaceID; // Record interface to send it on + m->NextScheduledResponse = m->timenow; + if (srcaddr->type == mDNSAddrType_IPv4) + { + if (mDNSIPv4AddressIsZero(rr->v4Requester)) rr->v4Requester = srcaddr->ip.v4; + else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr; + } + else if (srcaddr->type == mDNSAddrType_IPv6) + { + if (mDNSIPv6AddressIsZero(rr->v6Requester)) rr->v6Requester = srcaddr->ip.v6; + else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr; + } } } } + else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0) + { + // Since additional records are an optimization anyway, we only ever send them on one interface at a time + // If two clients on different interfaces do queries that invoke the same optional additional answer, + // then the earlier client is out of luck + rr->ImmedAdditional = InterfaceID; + // No need to set m->NextScheduledResponse here + // We'll send these additional records when we send them, or not, as the case may be + } } // *** @@ -2836,19 +5210,19 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con if (delayresponse && !m->SuppressSending) { // Pick a random delay between 20ms and 120ms. - m->SuppressSending = timenow + (mDNSPlatformOneSecond*2 + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*10)) / 100; + m->SuppressSending = m->timenow + (mDNSPlatformOneSecond*2 + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*10)) / 100; if (m->SuppressSending == 0) m->SuppressSending = 1; } // *** - // *** 8. If query is from a legacy client, generate a unicast reply too + // *** 8. If query is from a legacy client, generate a unicast response too // *** - if (answers && replyunicast) - responseptr = GenerateUnicastResponse(query, end, InterfaceAddr, replyunicast, ResponseRecords); + if (HaveUnicastAnswer) + responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); exit: // *** - // *** 9. Finally, clear our NextResponse link chain ready for use next time + // *** 9. Finally, clear our link chains ready for use next time // *** while (ResponseRecords) { @@ -2859,41 +5233,109 @@ exit: rr->NR_AdditionalTo = mDNSNULL; } + while (ExpectedAnswers) + { + CacheRecord *rr; + rr = ExpectedAnswers; + ExpectedAnswers = rr->NextInKAList; + rr->NextInKAList = mDNSNULL; + + // For non-truncated queries, we can definitively say that we should expect + // to be seeing a response for any records still left in the ExpectedAnswers list + if (!(query->h.flags.b[0] & kDNSFlag0_TC)) + if (rr->UnansweredQueries == 0 || m->timenow - rr->LastUnansweredTime >= mDNSPlatformOneSecond) + { + rr->UnansweredQueries++; + rr->LastUnansweredTime = m->timenow; + if (rr->UnansweredQueries > 1) + debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, GetRRDisplayString(m, rr)); + SetNextCacheCheckTime(m, rr); + } + + // If we've seen multiple unanswered queries for this record, + // then mark it to expire in five seconds if we don't get a response by then. + if (rr->UnansweredQueries >= MaxUnansweredQueries) + { + // Only show debugging message if this record was not about to expire anyway + if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) + debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, GetRRDisplayString(m, rr)); + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); + } + // Make a guess, based on the multi-packet query / known answer counts, whether we think we + // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for + // possible packet loss of up to 20% of the additional KA packets.) + else if (rr->MPUnansweredQ * 4 > rr->MPUnansweredKA * 5 + 8) + { + // We want to do this conservatively. + // If there are so many machines on the network that they have to use multi-packet known-answer lists, + // then we don't want them to all hit the network simultaneously with their final expiration queries. + // By setting the record to expire in four minutes, we achieve two things: + // (a) the 90-95% final expiration queries will be less bunched together + // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own + mDNSu32 remain = (mDNSu32)(RRExpireTime(rr) - m->timenow) / 4; + if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond) + remain = 240 * (mDNSu32)mDNSPlatformOneSecond; + + // Only show debugging message if this record was not about to expire anyway + if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) + debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, GetRRDisplayString(m, rr)); + + if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond) + rr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query + rr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; + + if (remain < kDefaultReconfirmTimeForNoAnswer) + remain = kDefaultReconfirmTimeForNoAnswer; + mDNS_Reconfirm_internal(m, rr, remain); + } + } + + while (DupQuestions) + { + int i; + DNSQuestion *q = DupQuestions; + DupQuestions = q->NextInDQList; + q->NextInDQList = mDNSNULL; + i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); + debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID, + srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); + } + return(responseptr); } mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - const mDNSIPAddr srcaddr, const mDNSIPPort srcport, const mDNSIPAddr dstaddr, mDNSIPPort dstport, const mDNSIPAddr InterfaceAddr) + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) { - const mDNSs32 timenow = mDNSPlatformTimeNow(); DNSMessage response; const mDNSu8 *responseend = mDNSNULL; - DNSMessage *replyunicast = mDNSNULL; - mDNSBool replymulticast = mDNSfalse; - - verbosedebugf("Received Query from %.4a:%d to %.4a:%d on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s", - &srcaddr, (mDNSu16)srcport.b[0]<<8 | srcport.b[1], - &dstaddr, (mDNSu16)dstport.b[0]<<8 | dstport.b[1], - &InterfaceAddr, - msg->h.numQuestions, msg->h.numQuestions == 1 ? "" : "s", - msg->h.numAnswers, msg->h.numAnswers == 1 ? "" : "s", - msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y" : "ies", - msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); - - // If this was a unicast query, or it was from an old (non-port-5353) client, then send a unicast response - if (dstaddr.NotAnInteger != AllDNSLinkGroup.NotAnInteger || srcport.NotAnInteger != MulticastDNSPort.NotAnInteger) - replyunicast = &response; - // If this was a multicast query, then we need to send a multicast response - if (dstaddr.NotAnInteger == AllDNSLinkGroup.NotAnInteger) replymulticast = mDNStrue; + verbosedebugf("Received Query from %#-15a:%d to %#-15a:%d on 0x%.8X with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + srcaddr, (mDNSu16)srcport.b[0]<<8 | srcport.b[1], + dstaddr, (mDNSu16)dstport.b[0]<<8 | dstport.b[1], + InterfaceID, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); - responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceAddr, replyunicast, replymulticast, timenow); - if (replyunicast && responseend) + responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, + (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), &response); + + if (responseend) // If responseend is non-null, that means we built a unicast response packet { - mDNSSendDNSMessage(m, replyunicast, responseend, InterfaceAddr, dstport, srcaddr, srcport); - verbosedebugf("Unicast Response: %d Answer%s, %d Additional%s on %.4a", - replyunicast->h.numAnswers, replyunicast->h.numAnswers == 1 ? "" : "s", - replyunicast->h.numAdditionals, replyunicast->h.numAdditionals == 1 ? "" : "s", &InterfaceAddr); + debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld", + response.h.numQuestions, response.h.numQuestions == 1 ? "" : "s", + response.h.numAnswers, response.h.numAnswers == 1 ? "" : "s", + response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", + srcaddr, (mDNSu16)srcport.b[0]<<8 | srcport.b[1], InterfaceID, srcaddr->type); + mDNSSendDNSMessage(m, &response, responseend, InterfaceID, dstport, srcaddr, srcport); } } @@ -2901,101 +5343,126 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, - const DNSMessage *const response, const mDNSu8 *end, const mDNSIPAddr dstaddr, const mDNSIPAddr InterfaceAddr) + const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSAddr *dstaddr, + const mDNSInterfaceID InterfaceID, mDNSu8 ttl) { int i; - const mDNSs32 timenow = mDNSPlatformTimeNow(); - - // We ignore questions (if any) in a DNS response packet - const mDNSu8 *ptr = LocateAnswers(response, end); - + const mDNSu8 *ptr = LocateAnswers(response, end); // We ignore questions (if any) in a DNS response packet + CacheRecord *CacheFlushRecords = mDNSNULL; + CacheRecord **cfp = &CacheFlushRecords; + // All records in a DNS response packet are treated as equally valid statements of truth. If we want - // to guard against spoof replies, then the only credible protection against that is cryptographic + // to guard against spoof responses, then the only credible protection against that is cryptographic // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals; - verbosedebugf("Received Response addressed to %.4a on %.4a with %d Question%s, %d Answer%s, %d Authorit%s, %d Additional%s", - &dstaddr, &InterfaceAddr, - response->h.numQuestions, response->h.numQuestions == 1 ? "" : "s", - response->h.numAnswers, response->h.numAnswers == 1 ? "" : "s", - response->h.numAuthorities, response->h.numAuthorities == 1 ? "y" : "ies", + (void)srcaddr; // Currently used only for display in debugging message + + verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p TTL %d with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + srcaddr, dstaddr, InterfaceID, ttl, + response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,", + response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", + response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); - // Other mDNS devices may issue unicast queries (which we correctly answer), - // but we never *issue* unicast queries, so if we ever receive a unicast - // response then it is someone trying to spoof us, so ignore it! - if (dstaddr.NotAnInteger != AllDNSLinkGroup.NotAnInteger) - { debugf("** Ignored attempted spoof unicast mDNS response packet **"); return; } + // TTL should be 255 + // In the case of overlayed subnets that aren't using RFC 3442, some packets may incorrectly + // go to the router first and then come back with a TTL of 254, so we allow that too. + // Anything lower than 254 is a pretty good sign of an off-net spoofing attack. + // Also, if we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us + if (ttl < 254 || (!mDNSAddrIsDNSMulticast(dstaddr) && (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)mDNSPlatformOneSecond)) + { + debugf("** Ignored apparent spoof mDNS Response from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + srcaddr, dstaddr, ttl, InterfaceID, + response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,", + response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,", + response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,", + response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s"); + return; + } for (i = 0; i < totalrecords && ptr && ptr < end; i++) { - ResourceRecord pktrr; - mDNSu8 RecordType = (i < response->h.numAnswers) ? kDNSRecordTypePacketAnswer : kDNSRecordTypePacketAdditional; - ptr = getResourceRecord(response, ptr, end, InterfaceAddr, timenow, RecordType, &pktrr, mDNSNULL); - if (!ptr) return; + LargeCacheRecord pkt; + const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd); + ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &pkt); + if (!ptr) break; // Break out of the loop and clean up our CacheFlushRecords list before exiting // 1. Check that this packet resource record does not conflict with any of ours - if (m->CurrentRecord) debugf("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set"); + if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set"); m->CurrentRecord = m->ResourceRecords; while (m->CurrentRecord) { - ResourceRecord *rr = m->CurrentRecord; + AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (SameResourceRecordSignature(&pktrr, rr)) // If interface, name, type and class match... - { // ... check to see if rdata is identical - if (SameRData(pktrr.rrtype, pktrr.rdata, rr->rdata)) + if (PacketRRMatchesSignature(&pkt.r, rr)) // If interface, name, type (if verified) and class match... + { + // ... check to see if rdata is identical + if (SameRData(&pkt.r.resrec, &rr->resrec)) { // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us - if (pktrr.rroriginalttl >= rr->rroriginalttl || m->SleepState) - rr->SendPriority = kDNSSendPriorityNone; + if (pkt.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState) + { + // If we were planning to send on this -- and only this -- interface, then we don't need to any more + if (rr->ImmedAnswer == InterfaceID) rr->ImmedAnswer = mDNSNULL; + } else - rr->SendPriority = kDNSSendPriorityAnswer; + { + if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; } + else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } + } } else { // else, the packet RR has different rdata -- check to see if this is a conflict - if (pktrr.rroriginalttl > 0 && PacketRRConflict(m, rr, &pktrr)) + if (pkt.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &pkt.r)) { - if (rr->rrtype == kDNSType_SRV) - { - debugf("mDNSCoreReceiveResponse: Our Data %d %##s", rr->rdata->RDLength, rr->rdata->u.srv.target.c); - debugf("mDNSCoreReceiveResponse: Pkt Data %d %##s", pktrr.rdata->RDLength, pktrr.rdata->u.srv.target.c); - } - else if (rr->rrtype == kDNSType_TXT) - { - debugf("mDNSCoreReceiveResponse: Our Data %d %#s", rr->rdata->RDLength, rr->rdata->u.txt.c); - debugf("mDNSCoreReceiveResponse: Pkt Data %d %#s", pktrr.rdata->RDLength, pktrr.rdata->u.txt.c); - } - else if (rr->rrtype == kDNSType_A) - { - debugf("mDNSCoreReceiveResponse: Our Data %.4a", &rr->rdata->u.ip); - debugf("mDNSCoreReceiveResponse: Pkt Data %.4a", &pktrr.rdata->u.ip); - } + debugf("mDNSCoreReceiveResponse: Our Record: %08X %08X %s", rr-> resrec.rdatahash, rr-> resrec.rdnamehash, GetRRDisplayString(m, rr)); + debugf("mDNSCoreReceiveResponse: Pkt Record: %08X %08X %s", pkt.r.resrec.rdatahash, pkt.r.resrec.rdnamehash, GetRRDisplayString(m, &pkt.r)); + + // If this record is marked DependentOn another record for conflict detection purposes, + // then *that* record has to be bumped back to probing state to resolve the conflict + while (rr->DependentOn) rr = rr->DependentOn; + // If we've just whacked this record's ProbeCount, don't need to do it again if (rr->ProbeCount <= DefaultProbeCountForTypeUnique) { - if (rr->RecordType == kDNSRecordTypeVerified) + // If we'd previously verified this record, put it back to probing state and try again + if (rr->resrec.RecordType == kDNSRecordTypeVerified) { - debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); - // If we'd previously verified this record, put it back to probing state and try again - rr->RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; - rr->NextSendTime = timenow; - rr->NextSendInterval = DefaultSendIntervalForRecordType(kDNSRecordTypeUnique); - m->ProbeFailTime = timenow; - // If we've had ten probe failures, rate-limit to one every five seconds - // The result is ORed with 1 to make sure SuppressProbes is not accidentally set to zero - if (m->NumFailedProbes < 10) m->NumFailedProbes++; - else m->SuppressProbes = (timenow + mDNSPlatformOneSecond * 5) | 1; + debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique); + InitializeLastAPTime(m, rr); + RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate } - else + // If we're probing for this record, we just failed + else if (rr->resrec.RecordType == kDNSRecordTypeUnique) + { + debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + // We assumed this record must be unique, but we were wrong. + // (e.g. There are two mDNSResponders on the same machine giving + // different answers for the reverse mapping record.) + // This is simply a misconfiguration, and we don't try to recover from it. + else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) { - debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); - // If we're probing for this record (or we assumed it must be unique) we just failed - mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_conflict); + debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); } + else + debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)", + rr->resrec.RecordType, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } } + // Else, matching signature, different rdata, but not a considered a conflict. + // If the packet record has the cache-flush bit set, then we check to see if we have to re-assert our record(s) + // to rescue them (see note about "multi-homing and bridged networks" at the end of this function). + else if ((pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2) + { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } } } } @@ -3003,77 +5470,122 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // 2. See if we want to add this packet resource record to our cache if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in { - ResourceRecord *rr; + mDNSu32 slot = HashSlot(&pkt.r.resrec.name); + CacheRecord *rr; // 2a. Check if this packet resource record is already in our cache - for (rr = m->rrcache; rr; rr=rr->next) + for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) { // If we found this exact resource record, refresh its TTL - if (IdenticalResourceRecord(&pktrr, rr)) + if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec)) { - //debugf("Found RR %##s size %d already in cache", pktrr.name.c, pktrr.rdata->RDLength); - rr->TimeRcvd = timenow; - rr->UnansweredQueries = 0; - rr->NewData = mDNStrue; - // If we're deleting a record, push it out one second into the future - // to give other hosts on the network a chance to protest - if (pktrr.rroriginalttl == 0) rr->rroriginalttl = 1; - else rr->rroriginalttl = pktrr.rroriginalttl; + if (pkt.r.resrec.rdlength > InlineCacheRDSize) + verbosedebugf("Found record size %5d interface %p already in cache: %s", + pkt.r.resrec.rdlength, InterfaceID, GetRRDisplayString(m, &pkt.r)); + rr->TimeRcvd = m->timenow; + + if (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list + if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList) + { *cfp = rr; cfp = &rr->NextInCFList; } + + // If this packet record is marked unique, and our previous cached copy was not, then fix it + if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++; + rr->resrec.RecordType = pkt.r.resrec.RecordType; + } + } + + if (pkt.r.resrec.rroriginalttl > 0) + { + rr->resrec.rroriginalttl = pkt.r.resrec.rroriginalttl; + rr->UnansweredQueries = 0; + rr->MPUnansweredQ = 0; + rr->MPUnansweredKA = 0; + rr->MPExpectingKA = mDNSfalse; + } + else + { + // If the packet TTL is zero, that means we're deleting this record. + // To give other hosts on the network a chance to protest, we push the deletion + // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. + // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent + // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. + rr->resrec.rroriginalttl = 1; + rr->UnansweredQueries = MaxUnansweredQueries; + } + SetNextCacheCheckTime(m, rr); break; } } // If packet resource record not in our cache, add it now // (unless it is just a deletion of a record we never had, in which case we don't care) - if (!rr && pktrr.rroriginalttl > 0) + if (!rr && pkt.r.resrec.rroriginalttl > 0) { - rr = GetFreeCacheRR(m, timenow); - if (!rr) debugf("No cache space to add record for %#s", pktrr.name.c); + rr = GetFreeCacheRR(m, pkt.r.resrec.rdlength); + if (!rr) debugf("No cache space to add record for %#s", pkt.r.resrec.name.c); else { - *rr = pktrr; - rr->rdata = &rr->rdatastorage; // For now, all cache records use local storage - rr->next = m->rrcache; - m->rrcache = rr; - if ((rr->RecordType & kDNSRecordTypeUniqueMask) == 0) - TriggerImmediateQuestions(m, rr, timenow); - //debugf("Adding RR %##s to cache (%d)", pktrr.name.c, m->rrcache_used); - AnswerLocalQuestions(m, rr, timenow); + RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer + *rr = pkt.r; + rr->resrec.rdata = saveptr; // and then restore it after the structure assignment + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { *cfp = rr; cfp = &rr->NextInCFList; } + // If this is an oversized record with external storage allocated, copy rdata to external storage + if (pkt.r.resrec.rdlength > InlineCacheRDSize) + mDNSPlatformMemCopy(pkt.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + pkt.r.resrec.rdlength); + rr->next = m->rrcache_hash[slot]; + m->rrcache_hash[slot] = rr; + m->rrcache_used[slot]++; + //debugf("Adding RR %##s to cache (%d)", pkt.r.name.c, m->rrcache_used); + CacheRecordAdd(m, rr); + // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us + SetNextCacheCheckTime(m, rr); } } } } - // If we have a cache, then run through all the new records that we've just added, - // clear their 'NewData' flags, and if they were marked as unique in the packet, - // then search our cache for any records with the same name/type/class, - // and purge them if they are more than one second old. - if (m->rrcache_size) + // If we've just received one or more records with their cache flush bits set, + // then scan that cache slot to see if there are any old stale records we need to flush + while (CacheFlushRecords) { - ResourceRecord *rr; - for (rr = m->rrcache; rr; rr=rr->next) - { - if (rr->NewData) + CacheRecord *r1 = CacheFlushRecords, *r2; + CacheFlushRecords = CacheFlushRecords->NextInCFList; + r1->NextInCFList = mDNSNULL; + for (r2 = m->rrcache_hash[HashSlot(&r1->resrec.name)]; r2; r2=r2->next) + if (SameResourceRecordSignature(&r1->resrec, &r2->resrec) && m->timenow - r2->TimeRcvd > mDNSPlatformOneSecond) { - rr->NewData = mDNSfalse; - if (rr->RecordType & kDNSRecordTypeUniqueMask) - { - ResourceRecord *r; - for (r = m->rrcache; r; r=r->next) - if (SameResourceRecordSignature(rr, r) && timenow - r->TimeRcvd > mDNSPlatformOneSecond) - r->rroriginalttl = 0; - } + verbosedebugf("Cache flush %p X %p %##s (%s)", r1, r2, r2->resrec.name.c, DNSTypeName(r2->resrec.rrtype)); + // We set stale records to expire in one second. + // This gives the owner a chance to rescue it if necessary. + // This is important in the case of multi-homing and bridged networks: + // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be + // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit + // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet + // will promptly delete their cached copies of the (still valid) Ethernet IP address record. + // By delaying the deletion by one second, we give X a change to notice that this bridging has + // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches. + // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary + // final expiration queries for this record. + r2->resrec.rroriginalttl = 1; + r2->TimeRcvd = m->timenow; + r2->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTime(m, r2); } - } - TidyRRCache(m, timenow); } } mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - mDNSIPAddr srcaddr, mDNSIPPort srcport, mDNSIPAddr dstaddr, mDNSIPPort dstport, mDNSIPAddr InterfaceAddr) + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID, mDNSu8 ttl) { - const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; - const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; - mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; + const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); // Read the integer parts which are in IETF byte-order (MSB first, LSB second) mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; @@ -3082,13 +5594,15 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); - if (!m) { debugf("mDNSCoreReceive ERROR m is NULL"); return; } + if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; } + + // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address" + // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up + if (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; } mDNS_Lock(m); - if (m->mDNS_busy > 1) debugf("mDNSCoreReceive: Locking failure! mDNS already busy"); - - if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceAddr); - else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, dstaddr, InterfaceAddr); + if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); + else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, dstaddr, InterfaceID, ttl); else debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]); // Packet reception often causes a change to the task list: @@ -3100,7 +5614,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - #pragma mark - Searcher Functions @@ -3113,133 +5627,235 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest // This prevents circular references, where two questions are each marked as a duplicate of the other. // Accordingly, we break out of the loop when we get to 'question', because there's no point searching // further in the list. - for (q = m->ActiveQuestions; q && q != question; q=q->next) // Scan our list of questions - if (q->InterfaceAddr.NotAnInteger == question->InterfaceAddr.NotAnInteger && // for another question with the same InterfaceID, - q->rrtype == question->rrtype && // type, - q->rrclass == question->rrclass && // class, - SameDomainName(&q->name, &question->name)) // and name + for (q = m->Questions; q && q != question; q=q->next) // Scan our list of questions + if (q->InterfaceID == question->InterfaceID && // for another question with the same InterfaceID, + q->qtype == question->qtype && // type, + q->qclass == question->qclass && // class, + q->qnamehash == question->qnamehash && + SameDomainName(&q->qname, &question->qname)) // and name return(q); return(mDNSNULL); } // This is called after a question is deleted, in case other identical questions were being // suppressed as duplicates -mDNSlocal void UpdateQuestionDuplicates(const mDNS *const m, const DNSQuestion *const question) +mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const question) { DNSQuestion *q; - for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions + for (q = m->Questions; q; q=q->next) // Scan our list of questions if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate { - q->NextQTime = question->NextQTime; q->ThisQInterval = question->ThisQInterval; - q->NextQInterval = question->NextQInterval; + q->LastQTime = question->LastQTime; + q->RecentAnswers = 0; q->DuplicateOf = FindDuplicateQuestion(m, q); + q->LastQTxTime = question->LastQTxTime; + SetNextQueryTime(m,q); } } -mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question, const mDNSs32 timenow) +mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) { +#if TEST_LOCALONLY_FOR_EVERYTHING + question->InterfaceID = (mDNSInterfaceID)~0; +#endif if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated return(mStatus_NoCache); else { - DNSQuestion **q = &m->ActiveQuestions; + int i; + // Note: It important that new questions are appended at the *end* of the list, not prepended at the start + DNSQuestion **q = &m->Questions; + if (question->InterfaceID == ((mDNSInterfaceID)~0)) q = &m->LocalOnlyQuestions; while (*q && *q != question) q=&(*q)->next; if (*q) { - debugf("Error! Tried to add a question that's already in the active list"); + LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list", + question->qname.c, DNSTypeName(question->qtype)); return(mStatus_AlreadyRegistered); } - if (question->InterfaceAddr.NotAnInteger) + // If this question is referencing a specific interface, make sure it exists + if (question->InterfaceID && question->InterfaceID != ((mDNSInterfaceID)~0)) { - NetworkInterfaceInfo *p = m->HostInterfaces; - while (p && p->ip.NotAnInteger != question->InterfaceAddr.NotAnInteger) p=p->next; - if (!p) + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == question->InterfaceID) break; + if (!intf) { - LogErrorMessage("mDNS_StartQuery_internal: question->InterfaceAddr %.4a not found in interface list", &question->InterfaceAddr); - question->InterfaceAddr.NotAnInteger = 0; + debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID); + return(mStatus_BadReferenceErr); } } - question->next = mDNSNULL; - question->NextQTime = timenow; - question->ThisQInterval = mDNSPlatformOneSecond; // MUST NOT be zero for an active question - question->NextQInterval = mDNSPlatformOneSecond; - question->DuplicateOf = FindDuplicateQuestion(m, question); + // Note: In the case where we already have the answer to this question in our cache, that may be all the client + // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would + // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds + // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately. + if (!ValidateDomainName(&question->qname)) + { + LogMsg("Attempt to start query with invalid qname %##s %s", question->qname.c, DNSTypeName(question->qtype)); + return(mStatus_Invalid); + } + + if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); + + question->next = mDNSNULL; + question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() + question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question + question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization + question->RecentAnswers = 0; + question->CurrentAnswers = 0; + question->LargeAnswers = 0; + question->UniqueAnswers = 0; + question->DuplicateOf = FindDuplicateQuestion(m, question); + question->NextInDQList = mDNSNULL; + for (i=0; iDupSuppress[i].InterfaceID = mDNSNULL; + // question->InterfaceID must be already set by caller + question->SendQNow = mDNSNULL; + question->SendOnAll = mDNSfalse; + question->LastQTxTime = m->timenow; + + if (!question->DuplicateOf) + verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p (%p) started", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question); + else + verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p (%p) duplicate of (%p)", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question, question->DuplicateOf); + *q = question; - - if (!m->NewQuestions) m->NewQuestions = question; + if (question->InterfaceID == ((mDNSInterfaceID)~0)) + { + if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; + } + else + { + if (!m->NewQuestions) m->NewQuestions = question; + SetNextQueryTime(m,question); + } return(mStatus_NoError); } } -mDNSlocal void mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question) +mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question) { - DNSQuestion **q = &m->ActiveQuestions; + CacheRecord *rr; + DNSQuestion **q = &m->Questions; + if (question->InterfaceID == ((mDNSInterfaceID)~0)) q = &m->LocalOnlyQuestions; while (*q && *q != question) q=&(*q)->next; if (*q) *q = (*q)->next; - else debugf("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", - question->name.c, DNSTypeName(question->rrtype)); + else + { + if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active + LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", + question->qname.c, DNSTypeName(question->qtype)); + return(mStatus_BadReferenceErr); + } + // Take care to cut question from list *before* calling UpdateQuestionDuplicates UpdateQuestionDuplicates(m, question); - + // But don't trash ThisQInterval until afterwards. question->ThisQInterval = -1; - question->NextQInterval = -1; - - // If we just deleted the question that AnswerLocalQuestions() is about to look at, + + // If there are any cache records referencing this as their active question, then see if any other + // question that is also referencing them, else their CRActiveQuestion needs to get set to NULL. + for (rr = m->rrcache_hash[HashSlot(&question->qname)]; rr; rr=rr->next) + { + if (rr->CRActiveQuestion == question) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + break; + verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %X", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), q); + rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null + if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count + } + } + + // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at, // bump its pointer forward one question. if (m->CurrentQuestion == question) { - debugf("mDNS_StopQuery_internal: Just deleted the currently active question."); - m->CurrentQuestion = m->CurrentQuestion->next; + debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->CurrentQuestion = question->next; } - if (m->NewQuestions == question) + if (m->NewQuestions == question) { - debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet."); - m->NewQuestions = m->NewQuestions->next; + debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->NewQuestions = question->next; } - + + if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next; + // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions question->next = mDNSNULL; + return(mStatus_NoError); } mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question) { - const mDNSs32 timenow = mDNS_Lock(m); - mStatus status = mDNS_StartQuery_internal(m, question, timenow); + mStatus status; + mDNS_Lock(m); + status = mDNS_StartQuery_internal(m, question); + mDNS_Unlock(m); + return(status); + } + +mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) + { + mStatus status; + mDNS_Lock(m); + status = mDNS_StopQuery_internal(m, question); + mDNS_Unlock(m); + return(status); + } + +mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const rr) + { + mStatus status; + mDNS_Lock(m); + status = mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); mDNS_Unlock(m); return(status); } -mDNSexport void mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) +mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr) { + mStatus status = mStatus_BadReferenceErr; + CacheRecord *cr; mDNS_Lock(m); - mDNS_StopQuery_internal(m, question); + cr = FindIdenticalRecordInCache(m, rr); + if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); mDNS_Unlock(m); + return(status); } mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, const domainname *const srv, const domainname *const domain, - const mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context) - { - question->InterfaceAddr = InterfaceAddr; - question->name = *srv; - AppendDomainNameToName(&question->name, domain); - question->rrtype = kDNSType_PTR; - question->rrclass = kDNSClass_IN; - question->Callback = Callback; - question->Context = Context; + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) + { + question->ThisQInterval = -1; // Indicate that query is not yet active + question->InterfaceID = InterfaceID; + question->qtype = kDNSType_PTR; + question->qclass = kDNSClass_IN; + question->QuestionCallback = Callback; + question->QuestionContext = Context; + if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); return(mDNS_StartQuery(m, question)); } -mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer) +mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { - ServiceInfoQuery *query = (ServiceInfoQuery *)question->Context; - if (answer->rrremainingttl == 0) return; + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger); + if (!AddRecord) return; if (answer->rrtype != kDNSType_SRV) return; query->info->port = answer->rdata->u.srv.port; @@ -3248,116 +5864,176 @@ mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const R if (!query->GotSRV) { query->GotSRV = mDNStrue; - query->qADD.InterfaceAddr = answer->InterfaceAddr; - query->qADD.name = answer->rdata->u.srv.target; - mDNS_StartQuery_internal(m, &query->qADD, mDNSPlatformTimeNow()); + query->qAv4.InterfaceID = answer->InterfaceID; + AssignDomainName(query->qAv4.qname, answer->rdata->u.srv.target); + query->qAv6.InterfaceID = answer->InterfaceID; + AssignDomainName(query->qAv6.qname, answer->rdata->u.srv.target); + mDNS_StartQuery_internal(m, &query->qAv4); + mDNS_StartQuery_internal(m, &query->qAv6); } // If this is not our first answer, only re-issue the address query if the target host name has changed - else if (query->qADD.InterfaceAddr.NotAnInteger != answer->InterfaceAddr.NotAnInteger || - !SameDomainName(&query->qADD.name, &answer->rdata->u.srv.target)) + else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) || + !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target)) + { + mDNS_StopQuery_internal(m, &query->qAv4); + mDNS_StopQuery_internal(m, &query->qAv6); + if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged) + { + // If we get here, it means: + // 1. This is not our first SRV answer + // 2. The interface ID is different, but the target host and port are the same + // This implies that we're seeing the exact same SRV record on more than one interface, so we should + // make our address queries at least as broad as the original SRV query so that we catch all the answers. + query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface + query->qAv6.InterfaceID = query->qSRV.InterfaceID; + } + else + { + query->qAv4.InterfaceID = answer->InterfaceID; + AssignDomainName(query->qAv4.qname, answer->rdata->u.srv.target); + query->qAv6.InterfaceID = answer->InterfaceID; + AssignDomainName(query->qAv6.qname, answer->rdata->u.srv.target); + } + debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c); + mDNS_StartQuery_internal(m, &query->qAv4); + mDNS_StartQuery_internal(m, &query->qAv6); + } + else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged) { - mDNS_StopQuery_internal(m, &query->qADD); - query->qADD.InterfaceAddr = answer->InterfaceAddr; - query->qADD.name = answer->rdata->u.srv.target; - mDNS_StartQuery_internal(m, &query->qADD, mDNSPlatformTimeNow()); + if (++query->Answers >= 100) + debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u", + query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c, + ((mDNSu16)answer->rdata->u.srv.port.b[0] << 8) | answer->rdata->u.srv.port.b[1]); + query->ServiceInfoQueryCallback(m, query); } - - // Don't need to do ScheduleNextTask because this callback can only ever happen - // (a) as a result of an immediate result from the mDNS_StartQuery call, or - // (b) as a result of receiving a packet on the wire - // both of which will result in a subsequent ScheduleNextTask call of their own + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. } -mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer) +mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { - ServiceInfoQuery *query = (ServiceInfoQuery *)question->Context; - if (answer->rrremainingttl == 0) return; + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + if (!AddRecord) return; if (answer->rrtype != kDNSType_TXT) return; - if (answer->rdata->RDLength > sizeof(query->info->TXTinfo)) return; + if (answer->rdlength > sizeof(query->info->TXTinfo)) return; - query->GotTXT = 1 + (query->GotTXT || query->GotADD); - query->info->TXTlen = answer->rdata->RDLength; - mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdata->RDLength); + query->GotTXT = mDNStrue; + query->info->TXTlen = answer->rdlength; + mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength); - debugf("FoundServiceInfoTXT: %##s GotADD=%d", &query->info->name, query->GotADD); + verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); - if (query->Callback && query->GotADD) - query->Callback(m, query); + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. + if (query->ServiceInfoQueryCallback && query->GotADD) + { + if (++query->Answers >= 100) + debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...", + query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c); + query->ServiceInfoQueryCallback(m, query); + } } -mDNSlocal void FoundServiceInfoADD(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer) +mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { - ServiceInfoQuery *query = (ServiceInfoQuery *)question->Context; - if (answer->rrremainingttl == 0) return; - if (answer->rrtype != kDNSType_A) return; + ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + if (!AddRecord) return; + + if (answer->rrtype == kDNSType_A) + { + query->info->ip.type = mDNSAddrType_IPv4; + query->info->ip.ip.v4 = answer->rdata->u.ip; + } + else if (answer->rrtype == kDNSType_AAAA) + { + query->info->ip.type = mDNSAddrType_IPv6; + query->info->ip.ip.v6 = answer->rdata->u.ipv6; + } + else + { + debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name.c, answer->rrtype, DNSTypeName(answer->rrtype)); + return; + } + query->GotADD = mDNStrue; - query->info->InterfaceAddr = answer->InterfaceAddr; - query->info->ip = answer->rdata->u.ip; + query->info->InterfaceID = answer->InterfaceID; - debugf("FoundServiceInfoADD: %##s GotTXT=%d", &query->info->name, query->GotTXT); + verbosedebugf("FoundServiceInfo v%d: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); - // If query->GotTXT is 1 that means we already got a single TXT answer but didn't - // deliver it to the client at that time, so no further action is required. - // If query->GotTXT is 2 that means we either got more than one TXT answer, - // or we got a TXT answer and delivered it to the client at that time, so in either - // of these cases we may have lost information, so we should re-issue the TXT question. - if (query->GotTXT > 1) + // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's + // callback function is allowed to do anything, including deleting this query and freeing its memory. + if (query->ServiceInfoQueryCallback && query->GotTXT) { - mDNS_StopQuery_internal(m, &query->qTXT); - mDNS_StartQuery_internal(m, &query->qTXT, mDNSPlatformTimeNow()); + if (++query->Answers >= 100) + { + if (answer->rrtype == kDNSType_A) + debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ip); + else + debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv6); + } + query->ServiceInfoQueryCallback(m, query); } - - if (query->Callback && query->GotTXT) - query->Callback(m, query); } -// On entry, the client must have set the name and InterfaceAddr fields of the ServiceInfo structure -// If the query is not interface-specific, then InterfaceAddr may be zero +// On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure +// If the query is not interface-specific, then InterfaceID may be zero // Each time the Callback is invoked, the remainder of the fields will have been filled in -// In addition, InterfaceAddr will be updated to give the interface address corresponding to that reply +// In addition, InterfaceID will be updated to give the interface identifier corresponding to that response mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, - ServiceInfoQuery *query, ServiceInfo *info, ServiceInfoQueryCallback *Callback, void *Context) + ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context) { mStatus status; - const mDNSs32 timenow = mDNS_Lock(m); - - query->qSRV.InterfaceAddr = info->InterfaceAddr; - query->qSRV.name = info->name; - query->qSRV.rrtype = kDNSType_SRV; - query->qSRV.rrclass = kDNSClass_IN; - query->qSRV.Callback = FoundServiceInfoSRV; - query->qSRV.Context = query; - - query->qTXT.InterfaceAddr = info->InterfaceAddr; - query->qTXT.name = info->name; - query->qTXT.rrtype = kDNSType_TXT; - query->qTXT.rrclass = kDNSClass_IN; - query->qTXT.Callback = FoundServiceInfoTXT; - query->qTXT.Context = query; - - query->qADD.InterfaceAddr = info->InterfaceAddr; - query->qADD.name.c[0] = 0; - query->qADD.rrtype = kDNSType_A; - query->qADD.rrclass = kDNSClass_IN; - query->qADD.Callback = FoundServiceInfoADD; - query->qADD.Context = query; - - query->GotSRV = mDNSfalse; - query->GotTXT = mDNSfalse; - query->GotADD = mDNSfalse; - - query->info = info; - query->Callback = Callback; - query->Context = Context; + mDNS_Lock(m); + + query->qSRV.ThisQInterval = -1; // This question not yet in the question list + query->qSRV.InterfaceID = info->InterfaceID; + AssignDomainName(query->qSRV.qname, info->name); + query->qSRV.qtype = kDNSType_SRV; + query->qSRV.qclass = kDNSClass_IN; + query->qSRV.QuestionCallback = FoundServiceInfoSRV; + query->qSRV.QuestionContext = query; + + query->qTXT.ThisQInterval = -1; // This question not yet in the question list + query->qTXT.InterfaceID = info->InterfaceID; + AssignDomainName(query->qTXT.qname, info->name); + query->qTXT.qtype = kDNSType_TXT; + query->qTXT.qclass = kDNSClass_IN; + query->qTXT.QuestionCallback = FoundServiceInfoTXT; + query->qTXT.QuestionContext = query; + + query->qAv4.ThisQInterval = -1; // This question not yet in the question list + query->qAv4.InterfaceID = info->InterfaceID; + query->qAv4.qname.c[0] = 0; + query->qAv4.qtype = kDNSType_A; + query->qAv4.qclass = kDNSClass_IN; + query->qAv4.QuestionCallback = FoundServiceInfo; + query->qAv4.QuestionContext = query; + + query->qAv6.ThisQInterval = -1; // This question not yet in the question list + query->qAv6.InterfaceID = info->InterfaceID; + query->qAv6.qname.c[0] = 0; + query->qAv6.qtype = kDNSType_AAAA; + query->qAv6.qclass = kDNSClass_IN; + query->qAv6.QuestionCallback = FoundServiceInfo; + query->qAv6.QuestionContext = query; + + query->GotSRV = mDNSfalse; + query->GotTXT = mDNSfalse; + query->GotADD = mDNSfalse; + query->Answers = 0; + + query->info = info; + query->ServiceInfoQueryCallback = Callback; + query->ServiceInfoQueryContext = Context; // info->name = Must already be set up by client // info->interface = Must already be set up by client - info->ip = zeroIPAddr; + info->ip = zeroAddr; info->port = zeroIPPort; info->TXTlen = 0; - status = mDNS_StartQuery_internal(m, &query->qSRV, timenow); - if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT, timenow); + status = mDNS_StartQuery_internal(m, &query->qSRV); + if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT); if (status != mStatus_NoError) mDNS_StopResolveService(m, query); mDNS_Unlock(m); @@ -3369,103 +6045,123 @@ mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *que mDNS_Lock(m); if (query->qSRV.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qSRV); if (query->qTXT.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qTXT); - if (query->qADD.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qADD); + if (query->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qAv4); + if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qAv6); mDNS_Unlock(m); } -mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNSu8 DomainType, - const mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context) +mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) { - question->InterfaceAddr = InterfaceAddr; - ConvertCStringToDomainName(mDNS_DomainTypeNames[DomainType], &question->name); - question->rrtype = kDNSType_PTR; - question->rrclass = kDNSClass_IN; - question->Callback = Callback; - question->Context = Context; + MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType]); + question->InterfaceID = InterfaceID; + question->qtype = kDNSType_PTR; + question->qclass = kDNSClass_IN; + question->QuestionCallback = Callback; + question->QuestionContext = Context; return(mDNS_StartQuery(m, question)); } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Responder Functions #endif -// Set up a ResourceRecord with sensible default values. +// Set up a AuthRecord with sensible default values. // These defaults may be overwritten with new values before mDNS_Register is called -mDNSexport void mDNS_SetupResourceRecord(ResourceRecord *rr, RData *RDataStorage, mDNSIPAddr InterfaceAddr, +mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context) { // Don't try to store a TTL bigger than we can represent in platform time units if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond) ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; else if (ttl == 0) // And Zero TTL is illegal - ttl = 1; + ttl = kDefaultTTLforShared; // Field Group 1: Persistent metadata for Authoritative Records rr->Additional1 = mDNSNULL; rr->Additional2 = mDNSNULL; rr->DependentOn = mDNSNULL; rr->RRSet = mDNSNULL; - rr->Callback = Callback; - rr->Context = Context; + rr->RecordCallback = Callback; + rr->RecordContext = Context; - rr->RecordType = RecordType; + rr->resrec.RecordType = RecordType; rr->HostTarget = mDNSfalse; // Field Group 2: Transient state for Authoritative Records (set in mDNS_Register_internal) // Field Group 3: Transient state for Cache Records (set in mDNS_Register_internal) // Field Group 4: The actual information pertaining to this resource record - rr->InterfaceAddr = InterfaceAddr; - rr->name.c[0] = 0; // MUST be set by client - rr->rrtype = rrtype; - rr->rrclass = kDNSClass_IN; - rr->rroriginalttl = ttl; - rr->rrremainingttl = ttl; -// rr->rdlength = MUST set by client and/or in mDNS_Register_internal -// rr->rdestimate = set in mDNS_Register_internal -// rr->rdata = MUST be set by client + rr->resrec.InterfaceID = InterfaceID; + rr->resrec.name.c[0] = 0; // MUST be set by client + rr->resrec.rrtype = rrtype; + rr->resrec.rrclass = kDNSClass_IN; + rr->resrec.rroriginalttl = ttl; +// rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal +// rr->resrec.rdestimate = set in mDNS_Register_internal +// rr->resrec.rdata = MUST be set by client if (RDataStorage) - rr->rdata = RDataStorage; + rr->resrec.rdata = RDataStorage; else { - rr->rdata = &rr->rdatastorage; - rr->rdata->MaxRDLength = sizeof(RDataBody); + rr->resrec.rdata = &rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); } } -mDNSexport mStatus mDNS_Register(mDNS *const m, ResourceRecord *const rr) +mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) { - const mDNSs32 timenow = mDNS_Lock(m); - mStatus status = mDNS_Register_internal(m, rr, timenow); + mStatus status; + mDNS_Lock(m); + status = mDNS_Register_internal(m, rr); mDNS_Unlock(m); return(status); } -mDNSexport mStatus mDNS_Update(mDNS *const m, ResourceRecord *const rr, mDNSu32 newttl, +mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) { - const mDNSs32 timenow = mDNS_Lock(m); + mDNS_Lock(m); + + if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata)) + { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(m, &rr->resrec, &newrdata->u)); return(mStatus_Invalid); } + + // If TTL is unspecified, leave TTL unchanged + if (newttl == 0) newttl = rr->resrec.rroriginalttl; // If we already have an update queued up which has not gone through yet, // give the client a chance to free that memory if (rr->NewRData) { RData *n = rr->NewRData; - rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... - if (rr->UpdateCallback) rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary - } - - rr->AnnounceCount = DefaultAnnounceCountForRecordType(rr->RecordType); - rr->NextSendTime = timenow; - if (rr->RecordType == kDNSRecordTypeUnique && m->SuppressProbes) rr->NextSendTime = m->SuppressProbes; - rr->NextSendInterval = DefaultSendIntervalForRecordType(rr->RecordType); - rr->NewRData = newrdata; - rr->UpdateCallback = Callback; - rr->rroriginalttl = newttl; - rr->rrremainingttl = newttl; + rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->UpdateCallback) + rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary + } + + if (rr->AnnounceCount < ReannounceCount) + rr->AnnounceCount = ReannounceCount; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m, rr); + rr->NewRData = newrdata; + rr->newrdlength = newrdlength; + rr->UpdateCallback = Callback; + if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--; + if (!rr->NextUpdateCredit) rr->NextUpdateCredit = (m->timenow + mDNSPlatformOneSecond * 60) | 1; + if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1); + if (rr->UpdateCredits <= 5) + { + mDNSs32 delay = 1 << (5 - rr->UpdateCredits); + if (!rr->UpdateBlocked) rr->UpdateBlocked = (m->timenow + delay * mDNSPlatformOneSecond) | 1; + rr->LastAPTime = rr->UpdateBlocked; + rr->ThisAPInterval *= 4; + LogMsg("Excessive update rate for %##s; delaying announcement by %d seconds", rr->resrec.name.c, delay); + } + rr->resrec.rroriginalttl = newttl; mDNS_Unlock(m); return(mStatus_NoError); } @@ -3473,169 +6169,269 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, ResourceRecord *const rr, mDNSu32 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSexport void mDNS_Deregister(mDNS *const m, ResourceRecord *const rr) +mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) { - const mDNSs32 timenow = mDNS_Lock(m); - mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal); + mStatus status; + mDNS_Lock(m); + status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); mDNS_Unlock(m); + return(status); + } + +mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); + +mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) break; + return(intf); + } + +mDNSlocal void mDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) + { + char buffer[256]; + NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); + if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary + + mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kDefaultTTLforUnique, kDNSRecordTypeUnique, HostNameCallback, set); + mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kDefaultTTLforUnique, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kDefaultTTLforUnique, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); + + // 1. Set up Address record to map from host name ("foo.local.") to IP address + // 2. Set up reverse-lookup PTR record to map from our address back to our host name + AssignDomainName(set->RR_A.resrec.name, m->hostname); + if (set->ip.type == mDNSAddrType_IPv4) + { + set->RR_A.resrec.rrtype = kDNSType_A; + set->RR_A.resrec.rdata->u.ip = set->ip.ip.v4; + // Note: This is reverse order compared to a normal dotted-decimal IP address + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", + set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); + } + else if (set->ip.type == mDNSAddrType_IPv6) + { + int i; + set->RR_A.resrec.rrtype = kDNSType_AAAA; + set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6; + for (i = 0; i < 16; i++) + { + static const char hexValues[] = "0123456789ABCDEF"; + buffer[i * 4 ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F]; + buffer[i * 4 + 1] = '.'; + buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4]; + buffer[i * 4 + 3] = '.'; + } + mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); + } + + MakeDomainNameFromDNSNameString(&set->RR_PTR.resrec.name, buffer); + set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name + + set->RR_A.RRSet = &primary->RR_A; // May refer to self + + mDNS_Register_internal(m, &set->RR_A); + mDNS_Register_internal(m, &set->RR_PTR); + + if (m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254) + { + mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data; + AssignDomainName(set->RR_HINFO.resrec.name, m->hostname); + set->RR_HINFO.DependentOn = &set->RR_A; + mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]); + p += 1 + (int)p[0]; + mDNSPlatformMemCopy(&m->HISoftware, p, 1 + (mDNSu32)m->HISoftware.c[0]); + mDNS_Register_internal(m, &set->RR_HINFO); + } + else + { + debugf("Not creating HINFO record: platform support layer provided no information"); + set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered; + } + } + +mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) + { + NetworkInterfaceInfo *intf; + // If we still have address records referring to this one, update them + NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); + AuthRecord *A = primary ? &primary->RR_A : mDNSNULL; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->RR_A.RRSet == &set->RR_A) + intf->RR_A.RRSet = A; + + // Unregister these records. + // When doing the mDNS_Close processing, we first call mDNS_DeadvertiseInterface for each interface, so by the time the platform + // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. + // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. + // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). + if (set->RR_A. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); + if (set->RR_PTR. resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); + if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); } mDNSexport void mDNS_GenerateFQDN(mDNS *const m) { - // Set up the Primary mDNS FQDN - m->hostname1.c[0] = 0; - AppendDomainLabelToName(&m->hostname1, &m->hostlabel); - AppendStringLabelToName(&m->hostname1, "local"); + domainname newname; + mDNS_Lock(m); - // Set up the Secondary mDNS FQDN - m->hostname2.c[0] = 0; - AppendDomainLabelToName(&m->hostname2, &m->hostlabel); - AppendStringLabelToName(&m->hostname2, "local"); - AppendStringLabelToName(&m->hostname2, "arpa"); + newname.c[0] = 0; + if (!AppendDomainLabel(&newname, &m->hostlabel)) LogMsg("ERROR! Cannot create dot-local hostname"); + if (!AppendLiteralLabelString(&newname, "local")) LogMsg("ERROR! Cannot create dot-local hostname"); + if (!SameDomainName(&m->hostname, &newname)) + { + NetworkInterfaceInfo *intf; + AuthRecord *rr; + + m->hostname = newname; + + // 1. Stop advertising our address records on all interfaces + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) mDNS_DeadvertiseInterface(m, intf); + + // 2. Start advertising our address records using the new name + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) mDNS_AdvertiseInterface(m, intf); + + // 3. Make sure that any SRV records (and the like) that reference our + // host name in their rdata get updated to reference this new host name + for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); + for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr); + } - // Make sure that any SRV records (and the like) that reference our - // host name in their rdata get updated to reference this new host name - UpdateHostNameTargets(m); + mDNS_Unlock(m); } -mDNSlocal void HostNameCallback(mDNS *const m, ResourceRecord *const rr, mStatus result) +mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) { - #pragma unused(rr) - switch (result) + (void)rr; // Unused parameter + + #if MDNS_DEBUGMSGS { - case mStatus_NoError: - debugf("HostNameCallback: %##s (%s) Name registered", rr->name.c, DNSTypeName(rr->rrtype)); - break; - case mStatus_NameConflict: - debugf("HostNameCallback: %##s (%s) Name conflict", rr->name.c, DNSTypeName(rr->rrtype)); - break; - default: - debugf("HostNameCallback: %##s (%s) Unknown result %d", rr->name.c, DNSTypeName(rr->rrtype), result); - break; + char *msg = "Unknown result"; + if (result == mStatus_NoError) msg = "Name registered"; + else if (result == mStatus_NameConflict) msg = "Name conflict"; + debugf("HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), msg, result); } + #endif - if (result == mStatus_NameConflict) + if (result == mStatus_NoError) + { + // Notify the client that the host name is successfully registered + if (m->MainCallback) + m->MainCallback(m, result); + } + else if (result == mStatus_NameConflict) { - NetworkInterfaceInfo *hr = mDNSNULL; - NetworkInterfaceInfo **p = &hr; domainlabel oldlabel = m->hostlabel; - // 1. Deregister all our host sets - while (m->HostInterfaces) - { - NetworkInterfaceInfo *set = m->HostInterfaces; - mDNS_DeregisterInterface(m, set); - *p = set; - p = &set->next; - } - - // 2. Pick a new name - // First give the client callback a chance to pick a new name - if (m->Callback) m->Callback(m, mStatus_NameConflict); - // If the client callback didn't do it, add (or increment) an index ourselves + // 1. First give the client callback a chance to pick a new name + if (m->MainCallback) + m->MainCallback(m, mStatus_NameConflict); + + // 2. If the client callback didn't do it, add (or increment) an index ourselves if (SameDomainLabel(m->hostlabel.c, oldlabel.c)) IncrementLabelSuffix(&m->hostlabel, mDNSfalse); + + // 3. Generate the FQDNs from the hostlabel, + // and make sure all SRV records, etc., are updated to reference our new hostname mDNS_GenerateFQDN(m); - - // 3. Re-register all our host sets - while (hr) - { - NetworkInterfaceInfo *set = hr; - hr = hr->next; - mDNS_RegisterInterface(m, set); - } } } -mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) +mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) { - NetworkInterfaceInfo *i; - for (i=m->HostInterfaces; i; i=i->next) if (i->Advertise) break; - return(i); + NetworkInterfaceInfo *intf; + active->IPv4Available = mDNSfalse; + active->IPv6Available = mDNSfalse; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == active->InterfaceID) + { + if (intf->ip.type == mDNSAddrType_IPv4 && intf->TxAndRx) active->IPv4Available = mDNStrue; + if (intf->ip.type == mDNSAddrType_IPv6 && intf->TxAndRx) active->IPv6Available = mDNStrue; + } } mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set) { - const mDNSs32 timenow = mDNS_Lock(m); + mDNSBool FirstOfType = mDNStrue; NetworkInterfaceInfo **p = &m->HostInterfaces; + mDNS_Lock(m); - while (*p && *p != set) p=&(*p)->next; - if (*p) - { - debugf("Error! Tried to register a NetworkInterfaceInfo that's already in the list"); - mDNS_Unlock(m); - return(mStatus_AlreadyRegistered); - } + // Assume this interface will be active + set->InterfaceActive = mDNStrue; + set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->TxAndRx); + set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->TxAndRx); - if (set->Advertise) + while (*p) { - char buffer[256]; - NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); - if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary - - mDNS_SetupResourceRecord(&set->RR_A1, mDNSNULL, set->ip, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, set); - mDNS_SetupResourceRecord(&set->RR_A2, mDNSNULL, set->ip, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, set); - mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->ip, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); - - // 1. Set up primary Address record to map from primary host name ("foo.local.") to IP address - set->RR_A1.name = m->hostname1; - set->RR_A1.rdata->u.ip = set->ip; - - // 2. Set up secondary Address record to map from secondary host name ("foo.local.arpa.") to IP address - set->RR_A2.name = m->hostname2; - set->RR_A2.rdata->u.ip = set->ip; - - // 3. Set up reverse-lookup PTR record to map from our address back to our primary host name - // Setting HostTarget tells DNS that the target of this PTR is to be automatically kept in sync if our host name changes - // Note: This is reverse order compared to a normal dotted-decimal IP address - mDNS_sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", set->ip.b[3], set->ip.b[2], set->ip.b[1], set->ip.b[0]); - ConvertCStringToDomainName(buffer, &set->RR_PTR.name); - set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name - - set->RR_A1.RRSet = &primary->RR_A1; // May refer to self - set->RR_A2.RRSet = &primary->RR_A2; // May refer to self - - mDNS_Register_internal(m, &set->RR_A1, timenow); - mDNS_Register_internal(m, &set->RR_A2, timenow); - mDNS_Register_internal(m, &set->RR_PTR, timenow); - - // ... Add an HINFO record, etc.? - } + if (*p == set) + { + LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list"); + mDNS_Unlock(m); + return(mStatus_AlreadyRegistered); + } - { // Reactivate Interface Questions - DNSQuestion *q; - for (q = m->ActiveQuestions; q; q=q->next) // Scan our list of questions - if (!q->InterfaceAddr.NotAnInteger || q->InterfaceAddr.NotAnInteger == set->ip.NotAnInteger) + // This InterfaceID is already in the list, so mark this interface inactive for now + if ((*p)->InterfaceID == set->InterfaceID) { - q->NextQTime = timenow; - q->ThisQInterval = mDNSPlatformOneSecond; // MUST be > zero for an active question - q->NextQInterval = mDNSPlatformOneSecond; + set->InterfaceActive = mDNSfalse; + if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse; + if (set->ip.type == mDNSAddrType_IPv4 && set->TxAndRx) (*p)->IPv4Available = mDNStrue; + if (set->ip.type == mDNSAddrType_IPv6 && set->TxAndRx) (*p)->IPv6Available = mDNStrue; } - } + + p=&(*p)->next; + } set->next = mDNSNULL; *p = set; - mDNS_Unlock(m); - return(mStatus_NoError); - } - -mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set, const mDNSs32 timenow) - { - NetworkInterfaceInfo *i; - // If we still have address records referring to this one, update them - NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); - ResourceRecord *A1 = primary ? &primary->RR_A1 : mDNSNULL; - ResourceRecord *A2 = primary ? &primary->RR_A2 : mDNSNULL; - for (i=m->HostInterfaces; i; i=i->next) + + debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set->InterfaceID, &set->ip, + set->InterfaceActive ? + "not represented in list; marking active and retriggering queries" : + "already represented in list; marking inactive for now"); + + // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, + // giving the false impression that there's an active representative of this interface when there really isn't. + // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records, + // even if we believe that we previously had an active representative of this interface. + if ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive) { - if (i->RR_A1.RRSet == &set->RR_A1) i->RR_A1.RRSet = A1; - if (i->RR_A2.RRSet == &set->RR_A2) i->RR_A2.RRSet = A2; + // Use a small amount of randomness: + // In the case of a network administrator turning on an Ethernet hub so that all the connected machines establish link at + // exactly the same time, we don't want them to all go and hit the network with identical queries at exactly the same moment. + if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); + DNSQuestion *q; + AuthRecord *rr; + for (q = m->Questions; q; q=q->next) // Scan our list of questions + if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, + { // then reactivate this question + q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + q->LastQTime = m->timenow - q->ThisQInterval; + q->RecentAnswers = 0; + if (ActiveQuestion(q)) m->NextScheduledQuery = m->timenow; + } + + // For all our non-specific authoritative resource records (and any dormant records specific to this interface) + // we now need them to re-probe if necessary, and then re-announce. + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID) + { + if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + if (rr->AnnounceCount < ReannounceCount) + rr->AnnounceCount = ReannounceCount; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m, rr); + } } - // Unregister these records - mDNS_Deregister_internal(m, &set->RR_A1, timenow, mDNS_Dereg_normal); - mDNS_Deregister_internal(m, &set->RR_A2, timenow, mDNS_Dereg_normal); - mDNS_Deregister_internal(m, &set->RR_PTR, timenow, mDNS_Dereg_normal); + if (set->Advertise) + mDNS_AdvertiseInterface(m, set); + + mDNS_Unlock(m); + return(mStatus_NoError); } // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change @@ -3644,74 +6440,136 @@ mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *se mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set) { NetworkInterfaceInfo **p = &m->HostInterfaces; - const mDNSs32 timenow = mDNS_Lock(m); + + mDNSBool revalidate = mDNSfalse; + // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every + // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it + // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion. + if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue; + + mDNS_Lock(m); // Find this record in our list while (*p && *p != set) p=&(*p)->next; - if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); return; } + if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; } // Unlink this record from our list *p = (*p)->next; set->next = mDNSNULL; - // Flush any cache entries we received on this interface - FlushCacheRecords(m, set->ip, timenow); + if (!set->InterfaceActive) + { + // If this interface not the active member of its set, update the v4/v6Available flags for the active member + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID) + UpdateInterfaceProtocols(m, intf); + } + else + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == set->InterfaceID) + break; + if (intf) + { + debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active", + set->InterfaceID); + intf->InterfaceActive = mDNStrue; + UpdateInterfaceProtocols(m, intf); + + // See if another representative *of the same type* exists. If not, we mave have gone from + // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid. + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type) + break; + if (!intf) revalidate = mDNStrue; + } + else + { + CacheRecord *rr; + DNSQuestion *q; + mDNSu32 slot; + debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant", + set->InterfaceID); + + // 1. Deactivate any questions specific to this interface + for (q = m->Questions; q; q=q->next) + if (q->InterfaceID == set->InterfaceID) + q->ThisQInterval = 0; + + // 2. Flush any cache records received on this interface + revalidate = mDNSfalse; // Don't revalidate if we're flushing the records + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) + if (rr->resrec.InterfaceID == set->InterfaceID) + PurgeCacheResourceRecord(m, rr); + } + } - { // Deactivate Interface Questions - DNSQuestion *q; - for (q = m->ActiveQuestions; q; q=q->next) - if (q->InterfaceAddr.NotAnInteger == set->ip.NotAnInteger) - q->ThisQInterval = 0; - } + // If we were advertising on this interface, deregister those address and reverse-lookup records now + if (set->Advertise) + mDNS_DeadvertiseInterface(m, set); - // If we were advertising on this interface, deregister now - // When doing the mDNS_Close processing, we first call mDNS_DeadvertiseInterface for each interface - // so by the time the platform support layer gets to call mDNS_DeregisterInterface, - // the address and PTR records have already been deregistered for it - if (set->Advertise && set->RR_A1.RecordType) mDNS_DeadvertiseInterface(m, set, timenow); + // If we have any cache records received on this interface that went away, then re-verify them. + // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off, + // giving the false impression that there's an active representative of this interface when there really isn't. + // Don't need to do this when shutting down, because *all* interfaces are about to go away + if (revalidate && !m->mDNS_shutdown) + { + mDNSu32 slot; + CacheRecord *rr; + m->NextCacheCheck = m->timenow; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) + if (rr->resrec.InterfaceID == set->InterfaceID) + mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForCableDisconnect); + } mDNS_Unlock(m); } -mDNSlocal void ServiceCallback(mDNS *const m, ResourceRecord *const rr, mStatus result) +mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) { - #pragma unused(m) - ServiceRecordSet *sr = (ServiceRecordSet *)rr->Context; - switch (result) - { - case mStatus_NoError: - if (rr == &sr->RR_SRV) - debugf("ServiceCallback: Service RR_SRV %##s Registered", rr->name.c); - else - debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_NoError callback for RR_SRV", - rr->name.c, DNSTypeName(rr->rrtype)); - break; - - case mStatus_NameConflict: - debugf("ServiceCallback: %##s (%s) Name Conflict", rr->name.c, DNSTypeName(rr->rrtype)); - break; - - case mStatus_MemFree: - if (rr == &sr->RR_PTR) - debugf("ServiceCallback: Service RR_PTR %##s Memory Free", rr->name.c); - else - debugf("ServiceCallback: %##s (%s) ERROR Should only get mStatus_MemFree callback for RR_PTR", - rr->name.c, DNSTypeName(rr->rrtype)); - break; + ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; + (void)m; // Unused parameter - default: - debugf("ServiceCallback: %##s (%s) Unknown Result %d", rr->name.c, DNSTypeName(rr->rrtype), result); - break; + #if MDNS_DEBUGMSGS + { + char *msg = "Unknown result"; + if (result == mStatus_NoError) msg = "Name Registered"; + else if (result == mStatus_NameConflict) msg = "Name Conflict"; + else if (result == mStatus_MemFree) msg = "Memory Free"; + debugf("ServiceCallback: %##s (%s) %s (%ld)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), msg, result); } + #endif // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that - if (result == mStatus_NameConflict) { sr->Conflict = mDNStrue; mDNS_DeregisterService(m, sr); return; } + if (result == mStatus_NameConflict) + { + sr->Conflict = mDNStrue; // Record that this service set had a conflict + sr->RR_PTR.AnnounceCount = InitialAnnounceCount; // Make sure we don't send a goodbye for the PTR record + mDNS_DeregisterService(m, sr); // Unlink the records from our list + return; + } - // If this ServiceRecordSet was forcibly deregistered, and now it's memory is ready for reuse, - // then we can now report the NameConflict to the client - if (result == mStatus_MemFree && sr->Conflict) result = mStatus_NameConflict; + if (result == mStatus_MemFree) + { + // If the PTR record or any of the subtype PTR record are still in the process of deregistering, + // don't pass on the NameConflict/MemFree message until every record is finished cleaning up. + mDNSu32 i; + if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return; + for (i=0; iNumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return; + + // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, + // then we can now report the NameConflict to the client + if (sr->Conflict) result = mStatus_NameConflict; + } - if (sr->Callback) sr->Callback(m, sr, result); + // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback + // function is allowed to do anything, including deregistering this service and freeing its memory. + if (sr->ServiceCallback) + sr->ServiceCallback(m, sr, result); } // Note: @@ -3720,92 +6578,125 @@ mDNSlocal void ServiceCallback(mDNS *const m, ResourceRecord *const rr, mStatus // Domain is fully qualified domain name (i.e. ending with a null label) // We always register a TXT, even if it is empty (so that clients are not // left waiting forever looking for a nonexistent record.) +// If the host parameter is mDNSNULL or the root domain (ASCII NUL), +// then the default host name (m->hostname1) is automatically used mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, const domainlabel *const name, const domainname *const type, const domainname *const domain, const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, - mDNSServiceCallback Callback, void *Context) + AuthRecord *SubTypes, mDNSu32 NumSubTypes, + const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context) { - mDNSs32 timenow; + mStatus err; + mDNSu32 i; - sr->Callback = Callback; - sr->Context = Context; - sr->Conflict = mDNSfalse; + sr->ServiceCallback = Callback; + sr->ServiceContext = Context; + sr->Extras = mDNSNULL; + sr->NumSubTypes = NumSubTypes; + sr->SubTypes = SubTypes; + sr->Conflict = mDNSfalse; if (host && host->c[0]) sr->Host = *host; else sr->Host.c[0] = 0; - mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, zeroIPAddr, kDNSType_PTR, 2*3600, kDNSRecordTypeShared, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, zeroIPAddr, kDNSType_SRV, 60, kDNSRecordTypeUnique, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, zeroIPAddr, kDNSType_TXT, 60, kDNSRecordTypeUnique, ServiceCallback, sr); + // Initialize the AuthRecord objects to sane values + mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeAdvisory, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kDefaultTTLforUnique, kDNSRecordTypeUnique, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kDefaultTTLforUnique, kDNSRecordTypeUnique, ServiceCallback, sr); // If the client is registering an oversized TXT record, // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it - if (sr->RR_TXT.rdata->MaxRDLength < txtlen) - sr->RR_TXT.rdata->MaxRDLength = txtlen; - - if (ConstructServiceName(&sr->RR_PTR.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr); - if (ConstructServiceName(&sr->RR_SRV.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); - sr->RR_TXT.name = sr->RR_SRV.name; + if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen) + sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen; + + // Set up the record names + // For now we only create an advisory record for the main type, not for subtypes + // We need to gain some operational experience before we decide if there's a need to create them for subtypes too + if (ConstructServiceName(&sr->RR_ADV.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x05_mdns\x04_udp", domain) == mDNSNULL) + return(mStatus_BadParamErr); + if (ConstructServiceName(&sr->RR_PTR.resrec.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + if (ConstructServiceName(&sr->RR_SRV.resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + AssignDomainName(sr->RR_TXT.resrec.name, sr->RR_SRV.resrec.name); - // 1. Set up the PTR record rdata to point to our service name + // 1. Set up the ADV record rdata to advertise our service type + AssignDomainName(sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name); + + // 2. Set up the PTR record rdata to point to our service name // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too - sr->RR_PTR.rdata->u.name = sr->RR_SRV.name; + AssignDomainName(sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name); sr->RR_PTR.Additional1 = &sr->RR_SRV; sr->RR_PTR.Additional2 = &sr->RR_TXT; - // 2. Set up the SRV record rdata. - sr->RR_SRV.rdata->u.srv.priority = 0; - sr->RR_SRV.rdata->u.srv.weight = 0; - sr->RR_SRV.rdata->u.srv.port = port; + // 2a. Set up any subtype PTRs to point to our service name + // If the client is using subtypes, it is the client's responsibility to have + // already set the first label of the record name to the subtype being registered + for (i=0; iSubTypes[i].resrec.name; + mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, ServiceCallback, sr); + if (ConstructServiceName(&sr->SubTypes[i].resrec.name, &s, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + AssignDomainName(sr->SubTypes[i].resrec.rdata->u.name, sr->RR_SRV.resrec.name); + sr->SubTypes[i].Additional1 = &sr->RR_SRV; + sr->SubTypes[i].Additional2 = &sr->RR_TXT; + } + + // 3. Set up the SRV record rdata. + sr->RR_SRV.resrec.rdata->u.srv.priority = 0; + sr->RR_SRV.resrec.rdata->u.srv.weight = 0; + sr->RR_SRV.resrec.rdata->u.srv.port = port; // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name - if (sr->Host.c[0]) sr->RR_SRV.rdata->u.srv.target = sr->Host; + if (sr->Host.c[0]) AssignDomainName(sr->RR_SRV.resrec.rdata->u.srv.target, sr->Host); else sr->RR_SRV.HostTarget = mDNStrue; - // 3. Set up the TXT record rdata, + // 4. Set up the TXT record rdata, // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us - if (txtinfo == mDNSNULL) sr->RR_TXT.rdata->RDLength = 0; - else if (txtinfo != sr->RR_TXT.rdata->u.txt.c) + if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0; + else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c) { - sr->RR_TXT.rdata->RDLength = txtlen; - if (sr->RR_TXT.rdata->RDLength > sr->RR_TXT.rdata->MaxRDLength) return(mStatus_BadParamErr); - mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.rdata->u.txt.c, txtlen); + sr->RR_TXT.resrec.rdlength = txtlen; + if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr); + mDNSPlatformMemCopy(txtinfo, sr->RR_TXT.resrec.rdata->u.txt.c, txtlen); } sr->RR_TXT.DependentOn = &sr->RR_SRV; - // 4. We have no Extras yet - sr->Extras = mDNSNULL; - - timenow = mDNS_Lock(m); - mDNS_Register_internal(m, &sr->RR_SRV, timenow); - mDNS_Register_internal(m, &sr->RR_TXT, timenow); + mDNS_Lock(m); + err = mDNS_Register_internal(m, &sr->RR_SRV); + if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); // We register the RR_PTR last, because we want to be sure that in the event of a forced call to // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to // make sure we've deregistered all our records and done any other necessary cleanup before that happens. - mDNS_Register_internal(m, &sr->RR_PTR, timenow); + if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV); + for (i=0; iSubTypes[i]); + if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR); mDNS_Unlock(m); - return(mStatus_NoError); + if (err) mDNS_DeregisterService(m, sr); + return(err); } -mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl) +mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, + ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl) { + mStatus result = mStatus_UnknownErr; ExtraResourceRecord **e = &sr->Extras; while (*e) e = &(*e)->next; - // If TTL is unspecified, make it 60 seconds, the same as the service's TXT and SRV default - if (ttl == 0) ttl = 60; + // If TTL is unspecified, make it the same as the service's TXT and SRV default + if (ttl == 0) ttl = kDefaultTTLforUnique; extra->next = mDNSNULL; - mDNS_SetupResourceRecord(&extra->r, rdata, zeroIPAddr, extra->r.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr); - extra->r.name = sr->RR_SRV.name; + mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr); + AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name); extra->r.DependentOn = &sr->RR_SRV; - debugf("mDNS_AddRecordToService adding record to %##s", extra->r.name.c); + debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name.c); - *e = extra; - return(mDNS_Register(m, &extra->r)); + result = mDNS_Register(m, &extra->r); + if (!result) *e = extra; + return result; } mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra) @@ -3814,15 +6705,14 @@ mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet while (*e && *e != extra) e = &(*e)->next; if (!*e) { - debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.name.c); + debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name.c); return(mStatus_BadReferenceErr); } - debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.name.c); + debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name.c); *e = (*e)->next; - mDNS_Deregister(m, &extra->r); - return(mStatus_NoError); + return(mDNS_Deregister(m, &extra->r)); } mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname) @@ -3833,7 +6723,7 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS ExtraResourceRecord *extras = sr->Extras; mStatus err; - DeconstructServiceName(&sr->RR_SRV.name, &name, &type, &domain); + DeconstructServiceName(&sr->RR_SRV.resrec.name, &name, &type, &domain); if (!newname) { IncrementLabelSuffix(&name, mDNStrue); @@ -3843,14 +6733,18 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host; err = mDNS_RegisterService(m, sr, newname, &type, &domain, - host, sr->RR_SRV.rdata->u.srv.port, sr->RR_TXT.rdata->u.txt.c, sr->RR_TXT.rdata->RDLength, - sr->Callback, sr->Context); + host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength, + sr->SubTypes, sr->NumSubTypes, + sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext); + // mDNS_RegisterService() just reset sr->Extras to NULL. + // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run + // through the old list of extra records, and re-add them to our freshly created service registration while (!err && extras) { ExtraResourceRecord *e = extras; extras = extras->next; - err = mDNS_AddRecordToService(m, sr, e, e->r.rdata, e->r.rroriginalttl); + err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl); } return(err); @@ -3859,134 +6753,236 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback, // which may change the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -mDNSexport void mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) +mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) { - const mDNSs32 timenow = mDNS_Lock(m); - ExtraResourceRecord *e = sr->Extras; - - // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of - // these records could have already been automatically deregistered, and that's okay - mDNS_Deregister_internal(m, &sr->RR_SRV, timenow, mDNS_Dereg_repeat); - mDNS_Deregister_internal(m, &sr->RR_TXT, timenow, mDNS_Dereg_repeat); - while (e) + if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) + { + debugf("Service set for %##s already deregistered", sr->RR_PTR.resrec.name.c); + return(mStatus_BadReferenceErr); + } + else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) { - mDNS_Deregister_internal(m, &e->r, timenow, mDNS_Dereg_repeat); - e=e->next; + debugf("Service set for %##s already in the process of deregistering", sr->RR_PTR.resrec.name.c); + return(mStatus_NoError); } + else + { + mDNSu32 i; + mStatus status; + ExtraResourceRecord *e; + mDNS_Lock(m); + e = sr->Extras; + + // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the + // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay + mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat); + mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat); + + mDNS_Deregister_internal(m, &sr->RR_ADV, mDNS_Dereg_normal); + + // We deregister all of the extra records, but we leave the sr->Extras list intact + // in case the client wants to do a RenameAndReregister and reinstate the registration + while (e) + { + mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat); + e = e->next; + } - // Be sure to deregister the PTR last! - // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback, - // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback, - // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure - // we've deregistered all our records and done any other necessary cleanup before that happens. - mDNS_Deregister_internal(m, &sr->RR_PTR, timenow, mDNS_Dereg_normal); + for (i=0; iNumSubTypes; i++) + mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal); - mDNS_Unlock(m); + // Be sure to deregister the PTR last! + // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback, + // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback, + // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure + // we've deregistered all our records and done any other necessary cleanup before that happens. + status = mDNS_Deregister_internal(m, &sr->RR_PTR, mDNS_Dereg_normal); + mDNS_Unlock(m); + return(status); + } + } + +// Create a registration that asserts that no such service exists with this name. +// This can be useful where there is a given function is available through several protocols. +// For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP" +// protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an +// "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing +// could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users. +mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, + const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context) + { + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kDefaultTTLforUnique, kDNSRecordTypeUnique, Callback, Context); + if (ConstructServiceName(&rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + rr->resrec.rdata->u.srv.priority = 0; + rr->resrec.rdata->u.srv.weight = 0; + rr->resrec.rdata->u.srv.port = zeroIPPort; + if (host && host->c[0]) AssignDomainName(rr->resrec.rdata->u.srv.target, *host); + else rr->HostTarget = mDNStrue; + return(mDNS_Register(m, rr)); } -mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, ResourceRecord *rr, - mDNSu8 DomainType, const mDNSIPAddr InterfaceAddr, char *domname) +mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, + mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) { - mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceAddr, kDNSType_PTR, 2*3600, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); - ConvertCStringToDomainName(mDNS_DomainTypeNames[DomainType], &rr->name); - ConvertCStringToDomainName(domname, &rr->rdata->u.name); + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); + if (!MakeDomainNameFromDNSNameString(&rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); + if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr); return(mDNS_Register(m, rr)); } // *************************************************************************** -#if 0 +#if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - #pragma mark - Startup and Shutdown #endif +mDNSexport void mDNS_GrowCache(mDNS *const m, CacheRecord *storage, mDNSu32 numrecords) + { + if (storage && numrecords) + { + mDNSu32 i; + for (i=0; irrcache_free; + m->rrcache_free = storage; + m->rrcache_size += numrecords; + } + } + mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, - ResourceRecord *rrcachestorage, mDNSu32 rrcachesize, mDNSCallback *Callback, void *Context) + CacheRecord *rrcachestorage, mDNSu32 rrcachesize, + mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) { - mStatus result; mDNSu32 i; + mDNSs32 timenow; + mStatus result = mDNSPlatformTimeInit(&timenow); + if (result != mStatus_NoError) return(result); if (!rrcachestorage) rrcachesize = 0; - m->p = p; - m->mDNSPlatformStatus = mStatus_Waiting; - m->Callback = Callback; - m->Context = Context; - - m->mDNS_busy = 0; - - m->lock_rrcache = 0; - m->lock_Questions = 0; - m->lock_Records = 0; - - m->ActiveQuestions = mDNSNULL; - m->NewQuestions = mDNSNULL; - m->CurrentQuestion = mDNSNULL; - m->rrcache_size = rrcachesize; - m->rrcache_used = 0; - m->rrcache_report = 10; - m->rrcache_free = rrcachestorage; - if (rrcachesize) - { - for (i=0; irrcache = mDNSNULL; - - m->hostlabel.c[0] = 0; - m->nicelabel.c[0] = 0; - m->ResourceRecords = mDNSNULL; - m->CurrentRecord = mDNSNULL; - m->HostInterfaces = mDNSNULL; - m->SuppressSending = 0; - m->ProbeFailTime = 0; - m->NumFailedProbes = 0; - m->SuppressProbes = 0; - m->SleepState = mDNSfalse; - m->NetChanged = mDNSfalse; + m->p = p; + m->KnownBugs = 0; + m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; + m->mDNSPlatformStatus = mStatus_Waiting; + m->MainCallback = Callback; + m->MainContext = Context; + + // For debugging: To catch and report locking failures + m->mDNS_busy = 0; + m->mDNS_reentrancy = 0; + m->mDNS_shutdown = mDNSfalse; + m->lock_rrcache = 0; + m->lock_Questions = 0; + m->lock_Records = 0; + + // Task Scheduling variables + m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section + m->timenow_last = timenow; + m->timenow_adjust = 0; + m->NextScheduledEvent = timenow; + m->SuppressSending = timenow; + m->NextCacheCheck = timenow + 0x78000000; + m->NextScheduledQuery = timenow + 0x78000000; + m->NextScheduledProbe = timenow + 0x78000000; + m->NextScheduledResponse = timenow + 0x78000000; + m->ExpectUnicastResponse = timenow + 0x78000000; + m->RandomQueryDelay = 0; + m->SendDeregistrations = mDNSfalse; + m->SendImmediateAnswers = mDNSfalse; + m->SleepState = mDNSfalse; + + // These fields only required for mDNS Searcher... + m->Questions = mDNSNULL; + m->NewQuestions = mDNSNULL; + m->CurrentQuestion = mDNSNULL; + m->LocalOnlyQuestions = mDNSNULL; + m->NewLocalOnlyQuestions = mDNSNULL; + m->rrcache_size = 0; + m->rrcache_totalused = 0; + m->rrcache_active = 0; + m->rrcache_report = 10; + m->rrcache_free = mDNSNULL; + + for (i = 0; i < CACHE_HASH_SLOTS; i++) + { + m->rrcache_hash[i] = mDNSNULL; + m->rrcache_used[i] = 0; + } + + mDNS_GrowCache(m, rrcachestorage, rrcachesize); + + // Fields below only required for mDNS Responder... + m->hostlabel.c[0] = 0; + m->nicelabel.c[0] = 0; + m->hostname.c[0] = 0; + m->HIHardware.c[0] = 0; + m->HISoftware.c[0] = 0; + m->ResourceRecords = mDNSNULL; + m->DuplicateRecords = mDNSNULL; + m->LocalOnlyRecords = mDNSNULL; + m->NewLocalOnlyRecords = mDNSNULL; + m->DiscardLocalOnlyRecords = mDNSfalse; + m->CurrentRecord = mDNSNULL; + m->HostInterfaces = mDNSNULL; + m->ProbeFailTime = 0; + m->NumFailedProbes = 0; + m->SuppressProbes = 0; result = mDNSPlatformInit(m); - + return(result); } -extern void mDNSCoreInitComplete(mDNS *const m, mStatus result) +mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result) { m->mDNSPlatformStatus = result; - if (m->Callback) m->Callback(m, mStatus_NoError); - mDNS_Lock(m); // This lock/unlock causes a ScheduleNextTask(m) to get things started - mDNS_Unlock(m); + if (m->MainCallback) + m->MainCallback(m, mStatus_NoError); } -extern void mDNS_Close(mDNS *const m) +mDNSexport void mDNS_Close(mDNS *const m) { - NetworkInterfaceInfo *i; - const mDNSs32 timenow = mDNS_Lock(m); + mDNSu32 rrcache_active = 0; + mDNSu32 rrcache_totalused = 0; + mDNSu32 slot; + NetworkInterfaceInfo *intf; + mDNS_Lock(m); -#if DEBUGBREAKS - ResourceRecord *rr; - int rrcache_active = 0; - for (rr = m->rrcache; rr; rr=rr->next) if (CacheRRActive(m, rr)) rrcache_active++; - debugf("mDNS_Close: RR Cache now using %d records, %d active", m->rrcache_used, rrcache_active); -#endif + m->mDNS_shutdown = mDNStrue; - m->ActiveQuestions = mDNSNULL; // We won't be answering any more questions! + rrcache_totalused = m->rrcache_totalused; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + while (m->rrcache_hash[slot]) + { + CacheRecord *rr = m->rrcache_hash[slot]; + m->rrcache_hash[slot] = rr->next; + if (rr->CRActiveQuestion) rrcache_active++; + m->rrcache_used[slot]--; + ReleaseCacheRR(m, rr); + } + debugf("mDNS_Close: RR Cache was using %ld records, %d active", rrcache_totalused, rrcache_active); + if (rrcache_active != m->rrcache_active) + LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active); + + m->Questions = mDNSNULL; // We won't be answering any more questions! - for (i=m->HostInterfaces; i; i=i->next) - if (i->Advertise) - mDNS_DeadvertiseInterface(m, i, timenow); + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) + mDNS_DeadvertiseInterface(m, intf); // Make sure there are nothing but deregistering records remaining in the list - if (m->CurrentRecord) debugf("DiscardDeregistrations ERROR m->CurrentRecord already set"); + if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set"); m->CurrentRecord = m->ResourceRecords; while (m->CurrentRecord) { - ResourceRecord *rr = m->CurrentRecord; + AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (rr->RecordType != kDNSRecordTypeDeregistering) + if (rr->resrec.RecordType != kDNSRecordTypeDeregistering) { - debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->RecordType, rr->name.c); - mDNS_Deregister_internal(m, rr, timenow, mDNS_Dereg_normal); + debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name.c); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); } } @@ -3995,10 +6991,10 @@ extern void mDNS_Close(mDNS *const m) // If any deregistering records remain, send their deregistration announcements before we exit if (m->mDNSPlatformStatus != mStatus_NoError) - DiscardDeregistrations(m, timenow); + DiscardDeregistrations(m); else while (m->ResourceRecords) - SendResponses(m, timenow); + SendResponses(m); mDNS_Unlock(m); debugf("mDNS_Close: mDNSPlatformClose"); diff --git a/mDNSCore/mDNSClientAPI.h b/mDNSCore/mDNSClientAPI.h index c6ba060..975d559 100755 --- a/mDNSCore/mDNSClientAPI.h +++ b/mDNSCore/mDNSClientAPI.h @@ -1,5 +1,401 @@ -#pragma once +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSClientAPI.h,v $ +Revision 1.114 2003/08/29 19:44:15 cheshire + Traffic reduction: Eliminate synchronized QUs when a new service appears +1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries + that already have at least one unique answer in the cache +2. For these queries, go straight to QM, skipping QU + +Revision 1.113 2003/08/21 19:31:58 cheshire +Cosmetic: Swap order of fields + +Revision 1.112 2003/08/21 19:27:36 cheshire + Traffic reduction: No need to announce record for longer than TTL + +Revision 1.111 2003/08/21 02:21:50 cheshire + Efficiency: Reduce repeated queries + +Revision 1.110 2003/08/20 23:39:31 cheshire + Review syslog messages, and remove as appropriate + +Revision 1.109 2003/08/19 22:24:10 cheshire +Comment change + +Revision 1.108 2003/08/19 22:20:00 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.107 2003/08/19 06:48:25 cheshire + Guard against excessive record updates +Each record starts with 10 UpdateCredits. +Every update consumes one UpdateCredit. +UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. +As the number of UpdateCredits declines, the number of announcements is similarly scaled back. +When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. + +Revision 1.106 2003/08/19 04:49:28 cheshire + Interaction between v4, v6 and dual-stack hosts not working quite right +1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6. +2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. +3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. + +Revision 1.105 2003/08/19 02:33:37 cheshire +Update comments + +Revision 1.104 2003/08/19 02:31:11 cheshire + mDNSResponder overenthusiastic with final expiration queries +Final expiration queries now only mark the question for sending on the particular interface +pertaining to the record that's expiring. + +Revision 1.103 2003/08/18 19:05:44 cheshire + UpdateRecord not working right +Added "newrdlength" field to hold new length of updated rdata + +Revision 1.102 2003/08/16 03:39:00 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.101 2003/08/15 20:16:02 cheshire + mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.100 2003/08/14 19:29:04 cheshire + Include cache records in SIGINFO output +Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them + +Revision 1.99 2003/08/14 02:17:05 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.98 2003/08/12 19:56:23 cheshire +Update to APSL 2.0 + +Revision 1.97 2003/08/12 14:59:27 cheshire + Rate-limiting blocks some legitimate responses +When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine +whether to suppress the response, also check LastMCInterface to see if it matches. + +Revision 1.96 2003/08/12 13:57:04 cheshire + Improve cache performance +Changed the number of hash table slots from 37 to 499 +Revision 1.95 2003/08/09 00:55:02 cheshire + mDNSResponder is taking 20-30% of the CPU +Don't scan the whole cache after every packet. + +Revision 1.94 2003/08/09 00:35:29 cheshire + +Revision 1.93 2003/08/08 18:55:48 cheshire + Guard against time going backwards + +Revision 1.92 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.91 2003/08/06 21:33:39 cheshire +Fix compiler warnings on PocketPC 2003 (Windows CE) + +Revision 1.90 2003/08/06 20:30:17 cheshire +Add structure definition for rdataMX (not currently used, but good to have it for completeness) + +Revision 1.89 2003/08/06 18:58:19 cheshire +Update comments + +Revision 1.88 2003/07/24 23:45:44 cheshire +To eliminate compiler warnings, changed definition of mDNSBool from +"unsigned char" to "int", since "int" is in fact truly the type that C uses +for the result of comparison operators (a: Feature: New Rendezvous APIs (#7875) (mDNSResponder component) +Added error type for incompatibility between daemon and client versions + +Revision 1.85 2003/07/19 03:23:13 cheshire + mDNSResponder needs to receive and cache larger records + +Revision 1.84 2003/07/18 23:52:12 cheshire +To improve consistency of field naming, global search-and-replace: +NextProbeTime -> NextScheduledProbe +NextResponseTime -> NextScheduledResponse + +Revision 1.83 2003/07/18 00:29:59 cheshire + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.82 2003/07/17 17:35:04 cheshire + Rate-limit responses, to guard against packet flooding + +Revision 1.81 2003/07/16 05:01:36 cheshire +Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for + Need to implement "unicast response" request, using top bit of qclass + +Revision 1.80 2003/07/15 01:55:12 cheshire + Need to implement service registration with subtypes + +Revision 1.79 2003/07/13 02:28:00 cheshire + SendResponses didn't all its responses +Delete all references to RRInterfaceActive -- it's now superfluous + +Revision 1.78 2003/07/13 01:47:53 cheshire +Fix one error and one warning in the Windows build + +Revision 1.77 2003/07/11 01:32:38 cheshire +Syntactic cleanup (no change to funcationality): Now that we only have one host name, +rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". + +Revision 1.76 2003/07/11 01:28:00 cheshire + No more local.arpa + +Revision 1.75 2003/07/02 21:19:45 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.74 2003/07/02 02:41:23 cheshire + mDNSResponder needs to start with a smaller cache and then grow it as needed + +Revision 1.73 2003/06/10 04:24:39 cheshire + React when we observe other people query unsuccessfully for a record that's in our cache +Some additional refinements: +Don't try to do this for unicast-response queries +better tracking of Qs and KAs in multi-packet KA lists + +Revision 1.72 2003/06/10 01:46:27 cheshire +Add better comments explaining how these data structures are intended to be used from the client layer + +Revision 1.71 2003/06/07 06:45:05 cheshire + No need for multiple machines to all be sending the same queries + +Revision 1.70 2003/06/07 04:50:53 cheshire + React when we observe other people query unsuccessfully for a record that's in our cache + +Revision 1.69 2003/06/07 04:22:17 cheshire +Add MsgBuffer for error log and debug messages + +Revision 1.68 2003/06/07 01:46:38 cheshire + When query produces zero results, call mDNS_Reconfirm() on any antecedent records + +Revision 1.67 2003/06/07 01:22:14 cheshire + mDNSResponder needs an mDNS_Reconfirm() function + +Revision 1.66 2003/06/07 00:59:43 cheshire + Need some randomness to spread queries on the network + +Revision 1.65 2003/06/06 21:41:11 cheshire +For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines + +Revision 1.64 2003/06/06 21:38:55 cheshire +Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we +already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) + +Revision 1.63 2003/06/06 17:20:14 cheshire +For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass +(Global search-and-replace; no functional change to code execution.) + +Revision 1.62 2003/06/04 01:25:33 cheshire + Cannot perform multi-packet known-answer suppression messages +Display time interval between first and subsequent queries + +Revision 1.61 2003/06/03 05:02:16 cheshire + Duplicate registrations not handled as efficiently as they should be + +Revision 1.60 2003/05/31 00:09:49 cheshire + Add ability to discover what services are on a network + +Revision 1.59 2003/05/29 06:11:35 cheshire +: Report if there appear to be too many "Resolve" callbacks + +Revision 1.58 2003/05/29 05:48:06 cheshire +Minor fix for when generating printf warnings: mDNS_snprintf arguments are now 3,4 + +Revision 1.57 2003/05/26 03:21:27 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.56 2003/05/26 03:01:27 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.55 2003/05/26 00:47:30 cheshire +Comment clarification + +Revision 1.54 2003/05/24 16:39:48 cheshire + SendResponses also needs to handle multihoming better + +Revision 1.53 2003/05/23 02:15:37 cheshire +Fixed misleading use of the term "duplicate suppression" where it should have +said "known answer suppression". (Duplicate answer suppression is something +different, and duplicate question suppression is yet another thing, so the use +of the completely vague term "duplicate suppression" was particularly bad.) + +Revision 1.52 2003/05/22 02:29:22 cheshire + SendQueries needs to handle multihoming better +Complete rewrite of SendQueries. Works much better now :-) + +Revision 1.51 2003/05/21 20:14:55 cheshire +Fix comments and warnings + +Revision 1.50 2003/05/14 07:08:36 cheshire + mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.49 2003/05/07 01:49:36 cheshire +Remove "const" in ConstructServiceName prototype + +Revision 1.48 2003/05/07 00:18:44 cheshire +Fix typo: "kDNSQClass_Mask" should be "kDNSClass_Mask" + +Revision 1.47 2003/05/06 00:00:46 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.46 2003/04/30 20:39:09 cheshire +Add comment + +Revision 1.45 2003/04/29 00:40:50 cheshire +Fix compiler warnings + +Revision 1.44 2003/04/26 02:41:56 cheshire + Change timenow from a local variable to a structure member + +Revision 1.43 2003/04/25 01:45:56 cheshire + mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.42 2003/04/15 20:58:31 jgraessl + +Bug #: 3229014 +Added a hash to lookup records in the cache. + +Revision 1.41 2003/04/15 18:09:13 jgraessl + +Bug #: 3228892 +Reviewed by: Stuart Cheshire +Added code to keep track of when the next cache item will expire so we can +call TidyRRCache only when necessary. + +Revision 1.40 2003/03/29 01:55:19 cheshire + mDNSResponder sometimes suffers false self-conflicts when it sees its own packets +Solution: Major cleanup of packet timing and conflict handling rules + +Revision 1.39 2003/03/27 03:30:55 cheshire + Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.38 2003/03/15 04:40:36 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.37 2003/03/14 21:34:11 cheshire + Can't setup and print to Lexmark PS printers via Airport Extreme +Increase size of cache rdata from 512 to 768 + +Revision 1.36 2003/03/05 03:38:35 cheshire +Bug #: 3185731 Bogus error message in console: died or deallocated, but no record of client can be found! +Fixed by leaving client in list after conflict, until client explicitly deallocates + +Revision 1.35 2003/02/21 02:47:54 cheshire +Bug #: 3099194 mDNSResponder needs performance improvements +Several places in the code were calling CacheRRActive(), which searched the entire +question list every time, to see if this cache resource record answers any question. +Instead, we now have a field "CRActiveQuestion" in the resource record structure + +Revision 1.34 2003/02/21 01:54:08 cheshire +Bug #: 3099194 mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.33 2003/02/20 06:48:32 cheshire +Bug #: 3169535 Xserve RAID needs to do interface-specific registrations +Reviewed by: Josh Graessley, Bob Bradley + +Revision 1.32 2003/01/31 03:35:59 cheshire +Bug #: 3147097 mDNSResponder sometimes fails to find the correct results +When there were *two* active questions in the list, they were incorrectly +finding *each other* and *both* being marked as duplicates of another question + +Revision 1.31 2003/01/29 02:46:37 cheshire +Fix for IPv6: +A physical interface is identified solely by its InterfaceID (not by IP and type). +On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts. +In cases where the requested outbound protocol (v4 or v6) is not supported on +that InterfaceID, the platform support layer should simply discard that packet. + +Revision 1.30 2003/01/29 01:47:08 cheshire +Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity + +Revision 1.29 2003/01/28 05:23:43 cheshire +Bug #: 3147097 mDNSResponder sometimes fails to find the correct results +Add 'Active' flag for interfaces + +Revision 1.28 2003/01/28 01:35:56 cheshire +Revise comment about ThisQInterval to reflect new semantics + +Revision 1.27 2003/01/13 23:49:42 jgraessl +Merged changes for the following fixes in to top of tree: +3086540 computer name changes not handled properly +3124348 service name changes are not properly handled +3124352 announcements sent in pairs, failing chattiness test + +Revision 1.26 2002/12/23 22:13:28 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.25 2002/09/21 20:44:49 zarzycki +Added APSL info + +Revision 1.24 2002/09/19 23:47:35 cheshire +Added mDNS_RegisterNoSuchService() function for assertion of non-existance +of a particular named service + +Revision 1.23 2002/09/19 21:25:34 cheshire +mDNS_snprintf() doesn't need to be in a separate file + +Revision 1.22 2002/09/19 04:20:43 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.21 2002/09/17 01:06:35 cheshire +Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() + +Revision 1.20 2002/09/16 18:41:41 cheshire +Merge in license terms from Quinn's copy, in preparation for Darwin release + +*/ + +#ifndef __mDNSClientAPI_h +#define __mDNSClientAPI_h + +#include // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration #include "mDNSDebug.h" #ifdef __cplusplus @@ -10,25 +406,32 @@ // Function scope indicators // If you see "mDNSlocal" before a function name, it means the function is not callable outside this file +#ifndef mDNSlocal #define mDNSlocal static +#endif // If you see "mDNSexport" before a symbol, it means the symbol is exported for use by clients +#ifndef mDNSexport #define mDNSexport +#endif // *************************************************************************** #if 0 #pragma mark - DNS Resource Record class and type constants #endif -typedef enum // From RFC 1035 +typedef enum // From RFC 1035 { - kDNSClass_IN = 1, // Internet - kDNSClass_CS = 2, // CSNET - kDNSClass_CH = 3, // CHAOS - kDNSClass_HS = 4, // Hesiod - kDNSClass_NONE = 254, // Used in DNS UPDATE [RFC 2136] - kDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes" - kDNSQClass_Mask = 0x7FFF, // Multicast DNS uses the bottom 15 bits to identify the record class... - kDNSClass_UniqueRRSet = 0x8000 // ... and the top bit indicates that all other cached records are now invalid + kDNSClass_IN = 1, // Internet + kDNSClass_CS = 2, // CSNET + kDNSClass_CH = 3, // CHAOS + kDNSClass_HS = 4, // Hesiod + kDNSClass_NONE = 254, // Used in DNS UPDATE [RFC 2136] + + kDNSClass_Mask = 0x7FFF,// Multicast DNS uses the bottom 15 bits to identify the record class... + kDNSClass_UniqueRRSet = 0x8000,// ... and the top bit indicates that all other cached records are now invalid + + kDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes" + kDNSQClass_UnicastResponse = 0x8000 // Top bit set in a question means "unicast response acceptable" } DNS_ClassValues; typedef enum // From RFC 1035 @@ -49,7 +452,7 @@ typedef enum // From RFC 1035 kDNSType_MINFO, // 14 Mailbox information kDNSType_MX, // 15 Mail Exchanger kDNSType_TXT, // 16 Arbitrary text string - + kDNSType_AAAA = 28, // 28 IPv6 address kDNSType_SRV = 33, // 33 Service record @@ -63,7 +466,7 @@ typedef enum // From RFC 1035 // mDNS defines its own names for these common types to simplify portability across // multiple platforms that may each have their own (different) names for these types. -typedef unsigned char mDNSBool; +typedef int mDNSBool; typedef signed char mDNSs8; typedef unsigned char mDNSu8; typedef signed short mDNSs16; @@ -71,15 +474,39 @@ typedef unsigned short mDNSu16; typedef signed long mDNSs32; typedef unsigned long mDNSu32; +// To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct +// This way, mDNSInterfaceIDs can be assigned, and compared with each other, but not with other types +// Declaring the type to be the typical generic "void *" would lack this type checking +typedef struct { void *dummy; } *mDNSInterfaceID; + // These types are for opaque two- and four-byte identifiers. -// The "NotAnInteger" fields of the unions allow the value to be conveniently passed around in a register -// for the sake of efficiency, but don't forget -- just because it is in a register doesn't mean it is an -// integer. Operations like add, multiply, increment, decrement, etc., are undefined for opaque identifiers. +// The "NotAnInteger" fields of the unions allow the value to be conveniently passed around in a +// register for the sake of efficiency, and compared for equality or inequality, but don't forget -- +// just because it is in a register doesn't mean it is an integer. Operations like greater than, +// less than, add, multiply, increment, decrement, etc., are undefined for opaque identifiers, +// and if you make the mistake of trying to do those using the NotAnInteger field, then you'll +// find you get code that doesn't work consistently on big-endian and little-endian machines. typedef union { mDNSu8 b[2]; mDNSu16 NotAnInteger; } mDNSOpaque16; typedef union { mDNSu8 b[4]; mDNSu32 NotAnInteger; } mDNSOpaque32; +typedef union { mDNSu8 b[16]; mDNSu16 w[8]; mDNSu32 l[4]; } mDNSOpaque128; -typedef mDNSOpaque16 mDNSIPPort; // An IP port is a two-byte opaque identifier (not an integer) -typedef mDNSOpaque32 mDNSIPAddr; // An IP address is a four-byte opaque identifier (not an integer) +typedef mDNSOpaque16 mDNSIPPort; // An IP port is a two-byte opaque identifier (not an integer) +typedef mDNSOpaque32 mDNSv4Addr; // An IP address is a four-byte opaque identifier (not an integer) +typedef mDNSOpaque128 mDNSv6Addr; // An IPv6 address is a 16-byte opaque identifier (not an integer) + +enum + { + mDNSAddrType_None = 0, + mDNSAddrType_IPv4 = 4, + mDNSAddrType_IPv6 = 6, + mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording + }; + +typedef struct + { + mDNSs32 type; + union { mDNSv6Addr v6; mDNSv4Addr v4; } ip; + } mDNSAddr; enum { mDNSfalse = 0, mDNStrue = 1 }; @@ -104,16 +531,22 @@ enum mStatus_AlreadyRegistered = -65547, mStatus_NameConflict = -65548, mStatus_Invalid = -65549, - + mStatus_GrowCache = -65550, + mStatus_Incompatible = -65551, + mStatus_ConfigChanged = -65791, mStatus_MemFree = -65792 // 0xFFFE FF00 }; typedef mDNSs32 mStatus; +// RFC 1034/1035 specify that a domain label consists of a length byte plus up to 63 characters #define MAX_DOMAIN_LABEL 63 typedef struct { mDNSu8 c[ 64]; } domainlabel; // One label: length byte and up to 63 characters + +// RFC 1034/1035 specify that a domain name, including length bytes, data bytes, and terminating zero, may be up to 255 bytes long #define MAX_DOMAIN_NAME 255 typedef struct { mDNSu8 c[256]; } domainname; // Up to 255 bytes of length-prefixed domainlabels + typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string // *************************************************************************** @@ -121,176 +554,292 @@ typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string #pragma mark - Resource Record structures #endif -// Shared Resource Records do not have to be unique -// -- Shared Resource Records are used for NIAS service PTRs +// Authoritative Resource Records: +// There are four basic types: Shared, Advisory, Unique, Known Unique + +// * Shared Resource Records do not have to be unique +// -- Shared Resource Records are used for DNS-SD service PTRs // -- It is okay for several hosts to have RRs with the same name but different RDATA -// -- We use a random delay on replies to reduce collisions when all the hosts reply to the same query +// -- We use a random delay on responses to reduce collisions when all the hosts respond to the same query // -- These RRs typically have moderately high TTLs (e.g. one hour) // -- These records are announced on startup and topology changes for the benefit of passive listeners - -// Unique Resource Records should be unique among hosts within any given mDNS scope +// -- These records send a goodbye packet when deregistering +// +// * Advisory Resource Records are like Shared Resource Records, except they don't send a goodbye packet +// +// * Unique Resource Records should be unique among hosts within any given mDNS scope // -- The majority of Resource Records are of this type // -- If two entities on the network have RRs with the same name but different RDATA, this is a conflict -// -- Replies may be sent immediately, because only one host should be replying to any particular query +// -- Responses may be sent immediately, because only one host should be responding to any particular query // -- These RRs typically have low TTLs (e.g. ten seconds) // -- On startup and after topology changes, a host issues queries to verify uniqueness -// Known Unique Resource Records are treated like Unique Resource Records, except that mDNS does +// * Known Unique Resource Records are treated like Unique Resource Records, except that mDNS does // not have to verify their uniqueness because this is already known by other means (e.g. the RR name // is derived from the host's IP or Ethernet address, which is already known to be a unique identifier). +// Summary of properties of different record types: +// Probe? Does this record type send probes before announcing? +// Conflict? Does this record type react if we observe an apparent conflict? +// Goodbye? Does this record type send a goodbye packet on departure? +// +// Probe? Conflict? Goodbye? Notes +// Unregistered Should not appear in any list (sanity check value) +// Shared No No Yes e.g. Service PTR record +// Deregistering No No Yes Shared record about to announce its departure and leave the list +// Advisory No No No +// Unique Yes Yes No Record intended to be unique -- will probe to verify +// Verified Yes Yes No Record has completed probing, and is verified unique +// KnownUnique No Yes No Record is assumed by other means to be unique + +// Valid lifecycle of a record: +// Unregistered -> Shared -> Deregistering -(goodbye)-> Unregistered +// Unregistered -> Advisory -> Unregistered +// Unregistered -> Unique -(probe)-> Verified -> Unregistered +// Unregistered -> KnownUnique -> Unregistered + +// Each Authoritative kDNSRecordType has only one bit set. This makes it easy to quickly see if a record +// is one of a particular set of types simply by performing the appropriate bitwise masking operation. + +// Cache Resource Records (received from the network): +// There are four basic types: Answer, Unique Answer, Additional, Unique Additional +// Bit 7 (the top bit) of kDNSRecordType is always set for Cache Resource Records; always clear for Authoritative Resource Records +// Bit 6 (value 0x40) is set for answer records; clear for additional records +// Bit 5 (value 0x20) is set for records received with the kDNSClass_UniqueRRSet + enum { kDNSRecordTypeUnregistered = 0x00, // Not currently in any list kDNSRecordTypeDeregistering = 0x01, // Shared record about to announce its departure and leave the list - kDNSRecordTypeUnique = 0x08, // Will become a kDNSRecordTypeVerified when probing is complete + kDNSRecordTypeUnique = 0x02, // Will become a kDNSRecordTypeVerified when probing is complete - kDNSRecordTypePacketAnswer = 0x10, // Received in the Answer Section of a DNS Response - kDNSRecordTypePacketAdditional = 0x11, // Received in the Additional Section of a DNS Response - kDNSRecordTypePacketUniqueAns = 0x18, // Received in the Answer Section of a DNS Response with kDNSQClass_CacheFlushBit set - kDNSRecordTypePacketUniqueAdd = 0x19, // Received in the Additional Section of a DNS Response with kDNSQClass_CacheFlushBit set + kDNSRecordTypeAdvisory = 0x04, // Like Shared, but no goodbye packet + kDNSRecordTypeShared = 0x08, // Shared means record name does not have to be unique -- use random delay on responses + kDNSRecordTypeVerified = 0x10, // Unique means mDNS should check that name is unique (and then send immediate responses) + kDNSRecordTypeKnownUnique = 0x20, // Known Unique means mDNS can assume name is unique without checking - kDNSRecordTypeShared = 0x20, // Shared means record name does not have to be unique -- so use random delay on replies - kDNSRecordTypeVerified = 0x28, // Unique means mDNS should check that name is unique (and then send immediate replies) - kDNSRecordTypeKnownUnique = 0x29, // Known Unique means mDNS can assume name is unique without checking - - kDNSRecordTypeUniqueMask = 0x08, // Test for records that are supposed to not be shared with other hosts - kDNSRecordTypeRegisteredMask = 0xF8, // Test for records that have not had mDNS_Deregister called on them yet - kDNSRecordTypeActiveMask = 0xF0 // Test for all records that have finished their probing and are now active - }; + kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), + kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), -enum - { - kDNSSendPriorityNone = 0, // Don't need to send this record right now - kDNSSendPriorityAdditional = 1, // Send this record as an additional, if we have space in the packet - kDNSSendPriorityAnswer = 2 // Need to send this record as an answer + kDNSRecordTypePacketAdd = 0x80, // Received in the Additional Section of a DNS Response + kDNSRecordTypePacketAddUnique = 0xA0, // Received in the Additional Section of a DNS Response with kDNSClass_UniqueRRSet set + kDNSRecordTypePacketAns = 0xC0, // Received in the Answer Section of a DNS Response + kDNSRecordTypePacketAnsUnique = 0xE0, // Received in the Answer Section of a DNS Response with kDNSClass_UniqueRRSet set + + kDNSRecordTypePacketAnsMask = 0x40, // True for PacketAns and PacketAnsUnique + kDNSRecordTypePacketUniqueMask = 0x20 // True for PacketAddUnique and PacketAnsUnique }; typedef struct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; +typedef struct { mDNSu16 preference; domainname exchange; } rdataMX; + +// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record +// MaximumRDSize is 8K the absolute maximum we support (at least for now) +#define StandardAuthRDSize 264 +#define MaximumRDSize 8192 + +// InlineCacheRDSize is 64 +// Records received from the network with rdata this size or less have their rdata stored right in the CacheRecord object +// Records received from the network with rdata larger than this have additional storage allocated for the rdata +// A quick unscientific sample from a busy network at Apple with lots of machines revealed this: +// 1461 records in cache +// 292 were one-byte TXT records +// 136 were four-byte A records +// 184 were sixteen-byte AAAA records +// 780 were various PTR, TXT and SRV records from 12-64 bytes +// Only 69 records had rdata bigger than 64 bytes +#define InlineCacheRDSize 64 typedef union { - mDNSu8 data[768]; // Generic untyped data (temporarily set 768 for the benefit of Airport Extreme printing) - mDNSIPAddr ip; // For 'A' record - domainname name; // For PTR and CNAME records - UTF8str255 txt; // For TXT record - rdataSRV srv; // For SRV record + mDNSu8 data[StandardAuthRDSize]; + mDNSv4Addr ip; // For 'A' record + mDNSv6Addr ipv6; // For 'AAAA' record + domainname name; // For PTR and CNAME records + UTF8str255 txt; // For TXT record + rdataSRV srv; // For SRV record + rdataMX mx; // For MX record } RDataBody; typedef struct { mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody)) - mDNSu16 RDLength; // Size of the rdata currently stored here RDataBody u; } RData; +#define sizeofRDataHeader (sizeof(RData) - sizeof(RDataBody)) +typedef struct AuthRecord_struct AuthRecord; +typedef struct CacheRecord_struct CacheRecord; typedef struct ResourceRecord_struct ResourceRecord; -typedef struct ResourceRecord_struct *ResourceRecordPtr; - +typedef struct DNSQuestion_struct DNSQuestion; typedef struct mDNS_struct mDNS; typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; -typedef void mDNSRecordCallback(mDNS *const m, ResourceRecord *const rr, mStatus result); -typedef void mDNSRecordUpdateCallback(mDNS *const m, ResourceRecord *const rr, RData *OldRData); +// Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef void mDNSRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result); + +// Note: +// Restrictions: An mDNSRecordUpdateCallback may not make any mDNS API calls. +// The intent of this callback is to allow the client to free memory, if necessary. +// The internal data structures of the mDNS code may not be in a state where mDNS API calls may be made safely. +typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData); -// Fields labelled "AR:" apply to our authoritative records -// Fields labelled "CR:" apply to cache records -// Fields labelled "--:" apply to both -// (May want to make this a union later, but not now, because using the -// same storage for two different purposes always makes debugging harder.) struct ResourceRecord_struct { - ResourceRecord *next; // --: Next in list - - // Field Group 1: Persistent metadata for Authoritative Records - ResourceRecord *Additional1; // AR: Recommended additional record to include in response - ResourceRecord *Additional2; // AR: Another additional - ResourceRecord *DependentOn; // AR: This record depends on another for its uniqueness checking - ResourceRecord *RRSet; // AR: This unique record is part of an RRSet - mDNSRecordCallback *Callback; // AR: Callback function to call for state changes - void *Context; // AR: Context parameter for the callback function - mDNSu8 RecordType; // --: See enum above - mDNSu8 HostTarget; // AR: Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name - - // Field Group 2: Transient state for Authoritative Records - mDNSu8 Acknowledged; // AR: Set if we've given the success callback to the client - mDNSu8 ProbeCount; // AR: Number of probes remaining before this record is valid (kDNSRecordTypeUnique) - mDNSu8 AnnounceCount; // AR: Number of announcements remaining (kDNSRecordTypeShared) - mDNSu8 IncludeInProbe; // AR: Set if this RR is being put into a probe right now - mDNSu8 SendPriority; // AR: See enum above - mDNSIPAddr Requester; // AR: Used for inter-packet duplicate suppression - // If set, give the IP address of the last host that sent a truncated query for this record - // If set to all-ones, more than one host sent such a request in the last few milliseconds - ResourceRecord *NextResponse; // AR: Link to the next element in the chain of responses to generate - const mDNSu8 *NR_AnswerTo; // AR: Set if this record was selected by virtue of being a direct answer to a question - ResourceRecord *NR_AdditionalTo; // AR: Set if this record was selected by virtue of being additional to another - mDNSs32 LastSendTime; // AR: In platform time units - mDNSs32 NextSendTime; // AR: In platform time units - mDNSs32 NextSendInterval; // AR: In platform time units - RData *NewRData; // AR: Set if we are updating this record with new rdata - mDNSRecordUpdateCallback *UpdateCallback; - - // Field Group 3: Transient state for Cache Records - ResourceRecord *NextDupSuppress; // CR: Link to the next element in the chain of duplicate suppression answers to send - mDNSs32 TimeRcvd; // CR: In platform time units - mDNSs32 LastUsed; // CR: In platform time units - mDNSu32 UseCount; // CR: Number of times this RR has been used to answer a question - mDNSu32 UnansweredQueries; // CR: Number of times we've issued a query for this record without getting an answer - mDNSBool Active; // CR: Set if there is currently a question referencing this answer - mDNSBool NewData; // CR: Set if this is a record we just received - - // Field Group 4: The actual information pertaining to this resource record - mDNSIPAddr InterfaceAddr; // --: Set if this RR is specific to one interface (e.g. a linklocal address) - // For records received off the wire, InterfaceAddr is *always* set to the receiving interface - // For our authoritative records, InterfaceAddr is usually zero, - // except those few records that are interface-specific (e.g. linklocal address records) - domainname name; // --: All the rest are used both in our authoritative records and in cache records + mDNSu8 RecordType; // See enum above + mDNSInterfaceID InterfaceID; // Set if this RR is specific to one interface + // For records received off the wire, InterfaceID is *always* set to the receiving interface + // For our authoritative records, InterfaceID is usually zero, except for those few records + // that are interface-specific (e.g. address records, especially linklocal addresses) + domainname name; mDNSu16 rrtype; mDNSu16 rrclass; - mDNSu32 rroriginalttl; // In seconds. - mDNSu32 rrremainingttl; // In seconds. Always set to correct value before calling question callback. + mDNSu32 rroriginalttl; // In seconds + mDNSu16 rdlength; // Size of the raw rdata, in bytes mDNSu16 rdestimate; // Upper bound on size of rdata after name compression + mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name + mDNSu32 rdatahash; // 32-bit hash of the raw rdata + mDNSu32 rdnamehash; // Set if this rdata contains a domain name (e.g. PTR, SRV, CNAME etc.) RData *rdata; // Pointer to storage for this rdata + }; + +struct AuthRecord_struct + { + // For examples of how to set up this structure for use in mDNS_Register(), + // see mDNS_AdvertiseInterface() or mDNS_RegisterService(). + // Basically, resrec and persistent metadata need to be set up before calling mDNS_Register(). + // mDNS_SetupResourceRecord() is avaliable as a helper routine to set up most fields to sensible default values for you + + AuthRecord *next; // Next in list; first element of structure for efficiency reasons + ResourceRecord resrec; + + // Persistent metadata for Authoritative Records + AuthRecord *Additional1; // Recommended additional record to include in response + AuthRecord *Additional2; // Another additional + AuthRecord *DependentOn; // This record depends on another for its uniqueness checking + AuthRecord *RRSet; // This unique record is part of an RRSet + mDNSRecordCallback *RecordCallback; // Callback function to call for state changes + void *RecordContext; // Context parameter for the callback function + mDNSu8 HostTarget; // Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name + + // Transient state for Authoritative Records + mDNSu8 Acknowledged; // Set if we've given the success callback to the client + mDNSu8 ProbeCount; // Number of probes remaining before this record is valid (kDNSRecordTypeUnique) + mDNSu8 AnnounceCount; // Number of announcements remaining (kDNSRecordTypeShared) + mDNSu8 IncludeInProbe; // Set if this RR is being put into a probe right now + mDNSInterfaceID ImmedAnswer; // Someone on this interface issued a query we need to answer (all-ones for all interfaces) + mDNSInterfaceID ImmedAdditional; // Hint that we might want to also send this record, just to be helpful + mDNSInterfaceID SendRNow; // The interface this query is being sent on right now + mDNSv4Addr v4Requester; // Recent v4 query for this record, or all-ones if more than one recent query + mDNSv6Addr v6Requester; // Recent v6 query for this record, or all-ones if more than one recent query + AuthRecord *NextResponse; // Link to the next element in the chain of responses to generate + const mDNSu8 *NR_AnswerTo; // Set if this record was selected by virtue of being a direct answer to a question + AuthRecord *NR_AdditionalTo; // Set if this record was selected by virtue of being additional to another + mDNSs32 ThisAPInterval; // In platform time units: Current interval for announce/probe + mDNSs32 AnnounceUntil; // In platform time units: Creation time + TTL + mDNSs32 LastAPTime; // In platform time units: Last time we sent announcement/probe + mDNSs32 LastMCTime; // Last time we multicast this record (used to guard against packet-storm attacks) + mDNSInterfaceID LastMCInterface; // Interface this record was multicast on at the time LastMCTime was recorded + RData *NewRData; // Set if we are updating this record with new rdata + mDNSu16 newrdlength; // ... and the length of the new RData + mDNSRecordUpdateCallback *UpdateCallback; + mDNSu32 UpdateCredits; // Token-bucket rate limiting of excessive updates + mDNSs32 NextUpdateCredit; // Time next token is added to bucket + mDNSs32 UpdateBlocked; // Set if update delaying is in effect + RData rdatastorage; // Normally the storage is right here, except for oversized records + // rdatastorage MUST be the last thing in the structure -- when using oversized AuthRecords, extra bytes + // are appended after the end of the AuthRecord, logically augmenting the size of the rdatastorage + // DO NOT ADD ANY MORE FIELDS HERE }; +struct CacheRecord_struct + { + CacheRecord *next; // Next in list; first element of structure for efficiency reasons + ResourceRecord resrec; + + // Transient state for Cache Records + CacheRecord *NextInKAList; // Link to the next element in the chain of known answers to send + mDNSs32 TimeRcvd; // In platform time units + mDNSs32 NextRequiredQuery; // In platform time units + mDNSs32 LastUsed; // In platform time units + mDNSu32 UseCount; // Number of times this RR has been used to answer a question + DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer + mDNSu32 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer + mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries + mDNSu32 MPUnansweredQ; // Multi-packet query handling: Number of times we've seen a query for this record + mDNSs32 MPLastUnansweredQT; // Multi-packet query handling: Last time we incremented MPUnansweredQ + mDNSu32 MPUnansweredKA; // Multi-packet query handling: Number of times we've seen this record in a KA list + mDNSBool MPExpectingKA; // Multi-packet query handling: Set when we increment MPUnansweredQ; allows one KA + CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set + + struct { mDNSu16 MaxRDLength; mDNSu8 data[InlineCacheRDSize]; } rdatastorage; // Storage for small records is right here + }; + +typedef struct + { + CacheRecord r; + mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes + } LargeCacheRecord; + typedef struct NetworkInterfaceInfo_struct NetworkInterfaceInfo; struct NetworkInterfaceInfo_struct { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. NetworkInterfaceInfo *next; - mDNSIPAddr ip; - mDNSBool Advertise; // Set Advertise to false if you are only searching on this interface - // Standard ResourceRecords that every Responder host should have (one per active IP address) - ResourceRecord RR_A1; // 'A' (address) record for our ".local" name - ResourceRecord RR_A2; // 'A' record for our ".local.arpa" name - ResourceRecord RR_PTR; // PTR (reverse lookup) record + + mDNSBool InterfaceActive; // InterfaceActive is set if interface is sending & receiving packets + // InterfaceActive is clear if interface is here to represent an address with A + // and/or AAAA records, but there is already an earlier representative for this + // physical interface which will be used for the actual sending & receiving + // packets (this status may change as interfaces are added and removed) + mDNSBool IPv4Available; // If InterfaceActive, set if v4 available on this InterfaceID + mDNSBool IPv6Available; // If InterfaceActive, set if v6 available on this InterfaceID + + // Standard AuthRecords that every Responder host should have (one per active IP address) + AuthRecord RR_A; // 'A' or 'AAAA' (address) record for our ".local" name + AuthRecord RR_PTR; // PTR (reverse lookup) record + AuthRecord RR_HINFO; + + // Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface() + mDNSInterfaceID InterfaceID; + mDNSAddr ip; + mDNSBool Advertise; // Set Advertise to false if you are only searching on this interface + mDNSBool TxAndRx; // Set to false if not sending and receiving packets on this interface }; typedef struct ExtraResourceRecord_struct ExtraResourceRecord; struct ExtraResourceRecord_struct { ExtraResourceRecord *next; - ResourceRecord r; - // Note: Add any additional fields *before* the ResourceRecord in this structure, not at the end. + AuthRecord r; + // Note: Add any additional fields *before* the AuthRecord in this structure, not at the end. // In some cases clients can allocate larger chunks of memory and set r->rdata->MaxRDLength to indicate - // that this extra memory is available, which would result in any fields after the ResourceRecord getting smashed + // that this extra memory is available, which would result in any fields after the AuthRecord getting smashed }; +// Note: Within an mDNSServiceCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() typedef struct ServiceRecordSet_struct ServiceRecordSet; typedef void mDNSServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result); struct ServiceRecordSet_struct { - mDNSServiceCallback *Callback; - void *Context; - ExtraResourceRecord *Extras; // Optional list of extra ResourceRecords attached to this service registration + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_RegisterService(); + // all required data is passed as parameters to that function. + mDNSServiceCallback *ServiceCallback; + void *ServiceContext; + ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration + mDNSu32 NumSubTypes; + AuthRecord *SubTypes; mDNSBool Conflict; // Set if this record set was forcibly deregistered because of a conflict domainname Host; // Set if this service record does not use the standard target host name - ResourceRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. - ResourceRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target - ResourceRecord RR_TXT; // e.g. Name._printer._tcp.local. TXT PrintQueueName - // Don't add any fields after ResourceRecord RR_TXT. + AuthRecord RR_ADV; // e.g. _services._mdns._udp.local. PTR _printer._tcp.local. + AuthRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. + AuthRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target + AuthRecord RR_TXT; // e.g. Name._printer._tcp.local. TXT PrintQueueName + // Don't add any fields after AuthRecord RR_TXT. // This is where the implicit extra space goes if we allocate a ServiceRecordSet containing an oversized RR_TXT record }; @@ -299,48 +848,85 @@ struct ServiceRecordSet_struct #pragma mark - Question structures #endif -typedef struct DNSQuestion_struct DNSQuestion; -typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer); +// We record the last eight instances of each duplicate query +// This gives us v4/v6 on each of Ethernet/AirPort and Firewire, and two free slots "for future expansion" +// If the host has more active interfaces that this it is not fatal -- duplicate question suppression will degrade gracefully. +// Since we will still remember the last eight, the busiest interfaces will still get the effective duplicate question suppression. +#define DupSuppressInfoSize 8 + +typedef struct + { + mDNSs32 Time; + mDNSInterfaceID InterfaceID; + mDNSs32 Type; // v4 or v6? + } DupSuppressInfo; + +// Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); struct DNSQuestion_struct { + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. DNSQuestion *next; - mDNSs32 NextQTime; // In platform time units - mDNSs32 ThisQInterval; // In platform time units (zero for questions not in list) - // ThisQInterval will be non-zero for an active question; - // Zero for a cancelled or inactive question - mDNSs32 NextQInterval; + mDNSu32 qnamehash; + mDNSs32 LastQTime; // Last scheduled tranmission of this Q on *all* applicable interfaces + mDNSs32 ThisQInterval; // LastQTime + ThisQInterval is the next scheduled tranmission of this Q + // ThisQInterval > 0 for an active question; + // ThisQInterval = 0 for a suspended question that's still in the list + // ThisQInterval = -1 for a cancelled question that's been removed from the list + mDNSu32 RecentAnswers; // Number of answers since the last time we sent this query + mDNSu32 CurrentAnswers; // Number of records currently in the cache that answer this question + mDNSu32 LargeAnswers; // Number of answers with rdata > 1024 bytes + mDNSu32 UniqueAnswers; // Number of answers received with kDNSClass_UniqueRRSet bit set DNSQuestion *DuplicateOf; - mDNSIPAddr InterfaceAddr; // Non-zero if you want to issue link-local queries only on a single specific IP interface - domainname name; - mDNSu16 rrtype; - mDNSu16 rrclass; - mDNSQuestionCallback *Callback; - void *Context; + DNSQuestion *NextInDQList; + DupSuppressInfo DupSuppress[DupSuppressInfoSize]; + mDNSInterfaceID SendQNow; // The interface this query is being sent on right now + mDNSBool SendOnAll; // Set if we're sending this question on all active interfaces + mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces + + // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() + mDNSInterfaceID InterfaceID; // Non-zero if you want to issue link-local queries only on a single specific IP interface + domainname qname; + mDNSu16 qtype; + mDNSu16 qclass; + mDNSQuestionCallback *QuestionCallback; + void *QuestionContext; }; typedef struct { - domainname name; - mDNSIPAddr InterfaceAddr; // Local (source) IP Interface (needed for scoped addresses such as link-local) - mDNSIPAddr ip; // Remote (destination) IP address where this service can be accessed - mDNSIPPort port; // Port where this service can be accessed - mDNSu16 TXTlen; - mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) + // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() + // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. + domainname name; + mDNSInterfaceID InterfaceID; // ID of the interface the response was received on + mDNSAddr ip; // Remote (destination) IP address where this service can be accessed + mDNSIPPort port; // Port where this service can be accessed + mDNSu16 TXTlen; + mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) } ServiceInfo; +// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() typedef struct ServiceInfoQuery_struct ServiceInfoQuery; -typedef void ServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query); +typedef void mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query); struct ServiceInfoQuery_struct { - DNSQuestion qSRV; - DNSQuestion qTXT; - DNSQuestion qADD; - mDNSu8 GotSRV; - mDNSu8 GotTXT; - mDNSu8 GotADD; - ServiceInfo *info; - ServiceInfoQueryCallback *Callback; - void *Context; + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); + // all required data is passed as parameters to that function. + // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information + // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may + // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. + DNSQuestion qSRV; + DNSQuestion qTXT; + DNSQuestion qAv4; + DNSQuestion qAv6; + mDNSu8 GotSRV; + mDNSu8 GotTXT; + mDNSu8 GotADD; + mDNSu32 Answers; + ServiceInfo *info; + mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; + void *ServiceInfoQueryContext; }; // *************************************************************************** @@ -350,44 +936,82 @@ struct ServiceInfoQuery_struct typedef void mDNSCallback(mDNS *const m, mStatus result); +#define CACHE_HASH_SLOTS 499 + +enum + { + mDNS_KnownBug_PhantomInterfaces = 1 + }; + struct mDNS_struct { - mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size + // Internal state fields. These hold the main internal state of mDNSCore; + // the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_Init(); + // all required data is passed as parameters to that function. + + mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size + mDNSu32 KnownBugs; + mDNSBool AdvertiseLocalAddresses; mStatus mDNSPlatformStatus; - mDNSCallback *Callback; - void *Context; - - mDNSu32 mDNS_busy; // For debugging: To catch and report locking failures - - mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified - mDNSu8 lock_Questions; - mDNSu8 lock_Records; - mDNSu8 padding; + mDNSCallback *MainCallback; + void *MainContext; + + // For debugging: To catch and report locking failures + mDNSu32 mDNS_busy; // Incremented between mDNS_Lock/mDNS_Unlock section + mDNSu32 mDNS_reentrancy; // Incremented when calling a client callback + mDNSu8 mDNS_shutdown; // Set when we're shutting down, allows us to skip some unnecessary steps + mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified + mDNSu8 lock_Questions; + mDNSu8 lock_Records; + char MsgBuffer[80]; // Temp storage used while building error log messages + + // Task Scheduling variables + mDNSs32 timenow; // The time that this particular activation of the mDNS code started + mDNSs32 timenow_last; // The time the last time we ran + mDNSs32 timenow_adjust; // Correction applied if we ever discover time went backwards + mDNSs32 NextScheduledEvent; // Derived from values below + mDNSs32 SuppressSending; // Don't send *any* packets during this time + mDNSs32 NextCacheCheck; // Next time to refresh cache record before it expires + mDNSs32 NextScheduledQuery; // Next time to send query in its exponential backoff sequence + mDNSs32 NextScheduledProbe; // Next time to probe for new authoritative record + mDNSs32 NextScheduledResponse; // Next time to send authoritative record(s) in responses + mDNSs32 ExpectUnicastResponse; // Set when we send a query with the kDNSQClass_UnicastResponse bit set + mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire + mDNSBool SendDeregistrations; // Set if we need to send deregistrations (immediately) + mDNSBool SendImmediateAnswers; // Set if we need to send answers (immediately -- or as soon as SuppressSending clears) + mDNSBool SleepState; // Set if we're sleeping (send no more packets) // These fields only required for mDNS Searcher... - DNSQuestion *ActiveQuestions; // List of all active questions - DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache - DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions() - mDNSu32 rrcache_size; - mDNSu32 rrcache_used; + DNSQuestion *Questions; // List of all registered questions, active and inactive + DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache + DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions() + DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to ~0 ("local only") + DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only questions not yet answered + mDNSu32 rrcache_size; // Total number of available cache entries + mDNSu32 rrcache_totalused; // Number of cache entries currently occupied + mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions mDNSu32 rrcache_report; - ResourceRecord *rrcache_free; - ResourceRecord *rrcache; + CacheRecord *rrcache_free; + CacheRecord *rrcache_hash[CACHE_HASH_SLOTS]; + mDNSu32 rrcache_used[CACHE_HASH_SLOTS]; // Fields below only required for mDNS Responder... - domainlabel nicelabel; // Rich text label encoded using canonically precomposed UTF-8 - domainlabel hostlabel; // Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules - domainname hostname1; // Primary Host Name "Foo.local." - domainname hostname2; // Secondary Host Name "Foo.local.arpa." - ResourceRecord *ResourceRecords; - ResourceRecord *CurrentRecord; // Next ResourceRecord about to be examined + domainlabel nicelabel; // Rich text label encoded using canonically precomposed UTF-8 + domainlabel hostlabel; // Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules + domainname hostname; // Host Name, e.g. "Foo.local." + UTF8str255 HIHardware; + UTF8str255 HISoftware; + AuthRecord *ResourceRecords; + AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records + AuthRecord *LocalOnlyRecords; // Local records registered with InterfaceID set to ~0 ("local only") + AuthRecord *NewLocalOnlyRecords; // Fresh local-only records not yet delivered to local-only questions + mDNSBool DiscardLocalOnlyRecords;// Set when we have "remove" events we need to deliver to local-only questions + AuthRecord *CurrentRecord; // Next AuthRecord about to be examined NetworkInterfaceInfo *HostInterfaces; - mDNSs32 SuppressSending; mDNSs32 ProbeFailTime; mDNSs32 NumFailedProbes; mDNSs32 SuppressProbes; - mDNSBool SleepState; - mDNSBool NetChanged; }; // *************************************************************************** @@ -395,15 +1019,20 @@ struct mDNS_struct #pragma mark - Useful Static Constants #endif -extern const ResourceRecord zeroRR; -extern const mDNSIPPort zeroIPPort; -extern const mDNSIPAddr zeroIPAddr; -extern const mDNSIPAddr onesIPAddr; - -extern const mDNSIPPort UnicastDNSPort; -extern const mDNSIPPort MulticastDNSPort; -extern const mDNSIPAddr AllDNSLinkGroup; -extern const mDNSIPAddr AllDNSAdminGroup; +extern const mDNSIPPort zeroIPPort; +extern const mDNSv4Addr zeroIPAddr; +extern const mDNSv6Addr zerov6Addr; +extern const mDNSv4Addr onesIPv4Addr; +extern const mDNSv6Addr onesIPv6Addr; +extern const mDNSInterfaceID mDNSInterface_Any; + +extern const mDNSIPPort UnicastDNSPort; +extern const mDNSIPPort MulticastDNSPort; +extern const mDNSv4Addr AllDNSAdminGroup; +extern const mDNSv4Addr AllDNSLinkGroup; +extern const mDNSv6Addr AllDNSLinkGroupv6; +extern const mDNSAddr AllDNSLinkGroup_v4; +extern const mDNSAddr AllDNSLinkGroup_v6; // *************************************************************************** #if 0 @@ -412,17 +1041,17 @@ extern const mDNSIPAddr AllDNSAdminGroup; // Every client should call mDNS_Init, passing in storage for the mDNS object, mDNS_PlatformSupport object, and rrcache. // The rrcachesize parameter is the size of (i.e. number of entries in) the rrcache array passed in. -// When mDNS has finished setting up the initComplete callback is called +// When mDNS has finished setting up the client's callback is called // A client can also spin and poll the mDNSPlatformStatus field to see when it changes from mStatus_Waiting to mStatus_NoError // // Call mDNS_Close to tidy up before exiting // -// Call mDNS_Register with a completed ResourceRecord object to register a resource record +// Call mDNS_Register with a completed AuthRecord object to register a resource record // If the resource record type is kDNSRecordTypeUnique (or kDNSknownunique) then if a conflicting resource record is discovered, // the resource record's mDNSRecordCallback will be called with error code mStatus_NameConflict. The callback should deregister // the record, and may then try registering the record again after picking a new name (e.g. by automatically appending a number). // -// Call mDNS_StartQuery to initiate a query. mDNS will proceed to issue Multicast DNS query packets, and any time a reply +// Call mDNS_StartQuery to initiate a query. mDNS will proceed to issue Multicast DNS query packets, and any time a response // is received containing a record which matches the question, the DNSQuestion's mDNSAnswerCallback function will be called // Call mDNS_StopQuery when no more answers are required // @@ -435,21 +1064,43 @@ extern const mDNSIPAddr AllDNSAdminGroup; // interrupt-time timer callback while in the middle of processing a client call. extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p, - ResourceRecord *rrcachestorage, mDNSu32 rrcachesize, mDNSCallback *Callback, void *Context); + CacheRecord *rrcachestorage, mDNSu32 rrcachesize, + mDNSBool AdvertiseLocalAddresses, + mDNSCallback *Callback, void *Context); +#define mDNS_Init_NoCache mDNSNULL +#define mDNS_Init_ZeroCacheSize 0 +#define mDNS_Init_AdvertiseLocalAddresses mDNStrue +#define mDNS_Init_DontAdvertiseLocalAddresses mDNSfalse +#define mDNS_Init_NoInitCallback mDNSNULL +#define mDNS_Init_NoInitCallbackContext mDNSNULL +extern void mDNS_GrowCache (mDNS *const m, CacheRecord *storage, mDNSu32 numrecords); extern void mDNS_Close (mDNS *const m); -extern mStatus mDNS_Register (mDNS *const m, ResourceRecord *const rr); -extern mStatus mDNS_Update (mDNS *const m, ResourceRecord *const rr, mDNSu32 newttl, +extern mDNSs32 mDNS_Execute (mDNS *const m); + +extern mStatus mDNS_Register (mDNS *const m, AuthRecord *const rr); +extern mStatus mDNS_Update (mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback); -extern void mDNS_Deregister(mDNS *const m, ResourceRecord *const rr); +extern mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr); + extern mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question); -extern void mDNS_StopQuery (mDNS *const m, DNSQuestion *const question); +extern mStatus mDNS_StopQuery (mDNS *const m, DNSQuestion *const question); +extern mStatus mDNS_Reconfirm (mDNS *const m, CacheRecord *const cacherr); +extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr); + +// *************************************************************************** +#if 0 +#pragma mark - Platform support functions that are accessible to the client layer too +#endif + +extern mDNSs32 mDNSPlatformOneSecond; +extern mDNSs32 mDNSPlatformTimeNow(void); // *************************************************************************** #if 0 #pragma mark - General utility and helper functions #endif -// mDNS_RegisterHostSet is a single call to register the standard resource records associated with every host. // mDNS_RegisterService is a single call to register the set of resource records associated with a given named service. // // mDNS_StartResolveService is single call which is equivalent to multiple calls to mDNS_StartQuery, @@ -467,28 +1118,31 @@ extern void mDNS_StopQuery (mDNS *const m, DNSQuestion *const question); // of one or more domains that should be offered to the user as choices for where they may register their service, // and the default domain in which to register in the case where the user has made no selection. -extern void mDNS_SetupResourceRecord(ResourceRecord *rr, RData *RDataStorage, mDNSIPAddr InterfaceAddr, +extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context); -extern void mDNS_GenerateFQDN(mDNS *const m); -extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set); -extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set); - extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, const domainlabel *const name, const domainname *const type, const domainname *const domain, const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, - mDNSServiceCallback Callback, void *Context); + AuthRecord *SubTypes, mDNSu32 NumSubTypes, + const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context); extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl); extern mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra); extern mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname); -extern void mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr); +extern mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr); + +extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, + const domainlabel *const name, const domainname *const type, const domainname *const domain, + const domainname *const host, + const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context); +#define mDNS_DeregisterNoSuchService mDNS_Deregister extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, - const domainname *const srv, const domainname *const domain, - const mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context); + const domainname *const srv, const domainname *const domain, + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); #define mDNS_StopBrowse mDNS_StopQuery -extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, ServiceInfoQueryCallback *Callback, void *Context); +extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context); extern void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query); typedef enum @@ -499,9 +1153,9 @@ typedef enum mDNS_DomainTypeRegistrationDefault = 3 } mDNS_DomainType; -extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNSu8 DomainType, const mDNSIPAddr InterfaceAddr, mDNSQuestionCallback *Callback, void *Context); +extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); #define mDNS_StopGetDomains mDNS_StopQuery -extern mStatus mDNS_AdvertiseDomains(mDNS *const m, ResourceRecord *rr, mDNSu8 DomainType, const mDNSIPAddr InterfaceAddr, char *domname); +extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname); #define mDNS_StopAdvertiseDomains mDNS_Deregister // *************************************************************************** @@ -514,30 +1168,71 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, ResourceRecord *rr, mDNSu8 D // work with DNS's native length-prefixed strings. For convenience in C, the following utility functions // are provided for converting between C's null-terminated strings and DNS's length-prefixed strings. +// Comparison functions extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b); extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2); -extern mDNSu32 DomainNameLength(const domainname *const name); -extern void AppendDomainLabelToName(domainname *const name, const domainlabel *const label); -extern void AppendStringLabelToName(domainname *const name, const char *cstr); -extern void AppendDomainNameToName(domainname *const name, const domainname *const append); -extern void AppendStringNameToName(domainname *const name, const char *cstr); - -extern void ConvertCStringToDomainLabel(const char *src, domainlabel *label); -extern mDNSu8 *ConvertCStringToDomainName(const char *const cstr, domainname *name); +// Get total length of domain name, in native DNS format, including terminal root label +// (e.g. length of "com." is 5 (length byte, three data bytes, final zero) +extern mDNSu16 DomainNameLength(const domainname *const name); + +// Append functions to append one or more labels to an existing native format domain name: +// AppendLiteralLabelString adds a single label from a literal C string, with no escape character interpretation. +// AppendDNSNameString adds zero or more labels from a C string using conventional DNS dots-and-escaping interpretation +// AppendDomainLabel adds a single label from a native format domainlabel +// AppendDomainName adds zero or more labels from a native format domainname +extern mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr); +extern mDNSu8 *AppendDNSNameString (domainname *const name, const char *cstr); +extern mDNSu8 *AppendDomainLabel (domainname *const name, const domainlabel *const label); +extern mDNSu8 *AppendDomainName (domainname *const name, const domainname *const append); + +// Convert from null-terminated string to native DNS format: +// The DomainLabel form makes a single label from a literal C string, with no escape character interpretation. +// The DomainName form makes native format domain name from a C string using conventional DNS interpretation: +// dots separate labels, and within each label, '\.' represents a literal dot, '\\' represents a literal +// backslash and backslash with three decimal digits (e.g. \000) represents an arbitrary byte value. +extern mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr); +extern mDNSu8 *MakeDomainNameFromDNSNameString (domainname *const name, const char *cstr); + +// Convert native format domainlabel or domainname back to C string format +extern char *ConvertDomainLabelToCString_withescape(const domainlabel *const name, char *cstr, char esc); +#define ConvertDomainLabelToCString_unescaped(D,C) ConvertDomainLabelToCString_withescape((D), (C), 0) +#define ConvertDomainLabelToCString(D,C) ConvertDomainLabelToCString_withescape((D), (C), '\\') +extern char *ConvertDomainNameToCString_withescape(const domainname *const name, char *cstr, char esc); +#define ConvertDomainNameToCString_unescaped(D,C) ConvertDomainNameToCString_withescape((D), (C), 0) +#define ConvertDomainNameToCString(D,C) ConvertDomainNameToCString_withescape((D), (C), '\\') + +extern void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel); + +extern mDNSu8 *ConstructServiceName(domainname *const fqdn, const domainlabel *name, const domainname *type, const domainname *const domain); +extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel *const name, domainname *const type, domainname *const domain); -extern char *ConvertDomainLabelToCString_withescape(const domainlabel *const name, char *cstr, char esc); -#define ConvertDomainLabelToCString_unescaped(D,C) ConvertDomainLabelToCString_withescape((D), (C), 0) -#define ConvertDomainLabelToCString(D,C) ConvertDomainLabelToCString_withescape((D), (C), '\\') +// Note: Some old functions have been replaced by more sensibly-named versions. +// You can uncomment the hash-defines below if you don't want to have to change your source code right away. +// When updating your code, note that (unlike the old versions) *all* the new routines take the target object +// as their first parameter. +//#define ConvertCStringToDomainName(SRC,DST) MakeDomainNameFromDNSNameString((DST),(SRC)) +//#define ConvertCStringToDomainLabel(SRC,DST) MakeDomainLabelFromLiteralString((DST),(SRC)) +//#define AppendStringLabelToName(DST,SRC) AppendLiteralLabelString((DST),(SRC)) +//#define AppendStringNameToName(DST,SRC) AppendDNSNameString((DST),(SRC)) +//#define AppendDomainLabelToName(DST,SRC) AppendDomainLabel((DST),(SRC)) +//#define AppendDomainNameToName(DST,SRC) AppendDomainName((DST),(SRC)) -extern char *ConvertDomainNameToCString_withescape(const domainname *const name, char *cstr, char esc); -#define ConvertDomainNameToCString_unescaped(D,C) ConvertDomainNameToCString_withescape((D), (C), 0) -#define ConvertDomainNameToCString(D,C) ConvertDomainNameToCString_withescape((D), (C), '\\') -extern void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel); +// *************************************************************************** +#if 0 +#pragma mark - Other utility functions +#endif -extern mDNSu8 *ConstructServiceName(domainname *const fqdn, const domainlabel *const name, const domainname *const type, const domainname *const domain); -extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel *const name, domainname *const type, domainname *const domain); +extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg); +extern mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4); +extern char *DNSTypeName(mDNSu16 rrtype); +extern char *GetRRDisplayString_rdb(mDNS *const m, const ResourceRecord *rr, RDataBody *rd); +#define GetRRDisplayString(m, rr) GetRRDisplayString_rdb((m), &(rr)->resrec, &(rr)->resrec.rdata->u) +extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); +extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); #ifdef __cplusplus } #endif + +#endif diff --git a/mDNSCore/mDNSDebug.h b/mDNSCore/mDNSDebug.h index f6e6e1e..65b6daf 100755 --- a/mDNSCore/mDNSDebug.h +++ b/mDNSCore/mDNSDebug.h @@ -1,39 +1,112 @@ -// Set DEBUGBREAKS to 0 to optimize debugf() calls out of the compiled code -// Set DEBUGBREAKS to 1 to generate normal debugging messages -// Set DEBUGBREAKS to 2 to generate verbose debugging messages -// DEBUGBREAKS is normally set in the project options (or makefile) but can also be set here if desired +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ -//#define DEBUGBREAKS 2 + Change History (most recent first): + +$Log: mDNSDebug.h,v $ +Revision 1.14 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + +Revision 1.13 2003/07/02 21:19:46 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.12 2003/05/26 03:01:27 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.11 2003/05/21 17:48:10 cheshire +Add macro to enable GCC's printf format string checking + +Revision 1.10 2003/04/26 02:32:57 cheshire +Add extern void LogMsg(const char *format, ...); + +Revision 1.9 2002/09/21 20:44:49 zarzycki +Added APSL info + +Revision 1.8 2002/09/19 04:20:43 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.7 2002/09/16 18:41:42 cheshire +Merge in license terms from Quinn's copy, in preparation for Darwin release + +*/ + +#ifndef __mDNSDebug_h +#define __mDNSDebug_h + +// Set MDNS_DEBUGMSGS to 0 to optimize debugf() calls out of the compiled code +// Set MDNS_DEBUGMSGS to 1 to generate normal debugging messages +// Set MDNS_DEBUGMSGS to 2 to generate verbose debugging messages +// MDNS_DEBUGMSGS is normally set in the project options (or makefile) but can also be set here if desired + +//#define MDNS_DEBUGMSGS 2 + +// Set MDNS_CHECK_PRINTF_STYLE_FUNCTIONS to 1 to enable extra GCC compiler warnings +// Note: You don't normally want to do this, because it generates a bunch of +// spurious warnings for the following custom extensions implemented by mDNS_vsnprintf: +// warning: `#' flag used with `%s' printf format (for %#s -- pascal string format) +// warning: repeated `#' flag in format (for %##s -- DNS name string format) +// warning: double format, pointer arg (arg 2) (for %.4a, %.16a, %#a -- IP address formats) +#define MDNS_CHECK_PRINTF_STYLE_FUNCTIONS 0 +#if MDNS_CHECK_PRINTF_STYLE_FUNCTIONS +#define IS_A_PRINTF_STYLE_FUNCTION(F,A) __attribute__ ((format(printf,F,A))) +#else +#define IS_A_PRINTF_STYLE_FUNCTION(F,A) +#endif #ifdef __cplusplus extern "C" { #endif -#if DEBUGBREAKS +#if MDNS_DEBUGMSGS #define debugf debugf_ -extern void debugf_(const char *format, ...); +extern void debugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); #else // If debug breaks are off, use a preprocessor trick to optimize those calls out of the code #if( defined( __GNUC__ ) ) - #define debugf( ARGS... ) + #define debugf( ARGS... ) ((void)0) #elif( defined( __MWERKS__ ) ) #define debugf( ... ) #else - #define debugf 1 ? ((void) 0) : (void) + #define debugf 1 ? ((void)0) : (void) #endif #endif -#if DEBUGBREAKS > 1 -#define verbosedebugf debugf_ +#if MDNS_DEBUGMSGS > 1 +#define verbosedebugf verbosedebugf_ +extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); #else #if( defined( __GNUC__ ) ) - #define verbosedebugf( ARGS... ) + #define verbosedebugf( ARGS... ) ((void)0) #elif( defined( __MWERKS__ ) ) #define verbosedebugf( ... ) #else - #define verbosedebugf 1 ? ((void) 0) : (void) + #define verbosedebugf 1 ? ((void)0) : (void) #endif #endif +// LogMsg is used even in shipping code, to write truly serious error messages to syslog (or equivalent) +extern void LogMsg(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); + #ifdef __cplusplus } #endif + +#endif diff --git a/mDNSCore/mDNSPlatformEnvironment.h b/mDNSCore/mDNSPlatformEnvironment.h deleted file mode 100755 index 1a349c7..0000000 --- a/mDNSCore/mDNSPlatformEnvironment.h +++ /dev/null @@ -1,103 +0,0 @@ -// mDNS-PlatformEnvironment.h needs to ensure that the necessary mDNS types are defined, -// plus whatever additional types are needed to support a particular platform - -// To add support for a new target platform with its own networking APIs and types, -// duplicate the "#elif __SOME_OTHER_OS__" section (including its two-line comment -// at the start) and add support for the new target platform in the new section. - -#pragma once - -#ifdef __cplusplus - extern "C" { -#endif - -// *************************************************************************** -// Classic Mac (Open Transport) structures - -#if (TARGET_API_MAC_OS8 || __MACOS__) - -// Headers needed for code on this platform -#include -#include - -typedef enum - { - mOT_Reset = 0, - mOT_Start, - mOT_ReusePort, - mOT_RcvDestAddr, - mOT_LLScope, - mOT_AdminScope, - mOT_Bind, - mOT_Ready - } mOT_State; - -typedef struct { TOptionHeader h; mDNSIPAddr multicastGroupAddress; mDNSIPAddr InterfaceAddress; } TIPAddMulticastOption; -typedef struct { TOptionHeader h; UInt32 flag; } TSetBooleanOption; - -// TOptionBlock is a union of various types. -// What they all have in common is that they all start with a TOptionHeader. -typedef union { TOptionHeader h; TIPAddMulticastOption m; TSetBooleanOption b; } TOptionBlock; - -struct mDNS_PlatformSupport_struct - { - EndpointRef ep; - UInt32 mOTstate; // mOT_State enum - TOptionBlock optBlock; - TOptMgmt optReq; - long OTTimerTask; - UInt32 nesting; - - // Platforms that support multi-homing will want a list of HostRecordSets instead of just one - HostRecordSet hostset; - }; - -// *************************************************************************** -// Mac OS X structures - -#elif (TARGET_API_MAC_OSX || __MACOSX__) - -// Headers needed for code on this platform -#include -#include -#include -#include - -struct mDNS_PlatformSupport_struct - { - CFRunLoopTimerRef CFTimer; - SCDynamicStoreRef Store; - CFRunLoopSourceRef StoreRLS; - io_connect_t PowerConnection; - io_object_t PowerNotifier; - CFRunLoopSourceRef PowerRLS; - }; - -// Set this symbol to 1 to do extra debug checks on malloc() and free() -#define MACOSX_MDNS_MALLOC_DEBUGGING 0 - -#if MACOSX_MDNS_MALLOC_DEBUGGING -extern void *mallocL(char *msg, unsigned int size); -extern void freeL(char *msg, void *x); -#else -#define mallocL(X,Y) malloc(Y) -#define freeL(X,Y) free(Y) -#endif - -// *************************************************************************** -// Placeholder for future platforms - -#elif __SOME_OTHER_OS__ - -// *************************************************************************** -// Generic code for Unix-style platforms - -#else - -#error Other platforms need to make sure that types like UInt16 are defined - -#endif - -#ifdef __cplusplus - } -#endif diff --git a/mDNSCore/mDNSPlatformFunctions.h b/mDNSCore/mDNSPlatformFunctions.h index e058894..beef207 100755 --- a/mDNSCore/mDNSPlatformFunctions.h +++ b/mDNSCore/mDNSPlatformFunctions.h @@ -1,3 +1,85 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSPlatformFunctions.h,v $ +Revision 1.22 2003/08/18 22:53:37 cheshire + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.21 2003/08/15 20:16:57 cheshire +Update comment for mDNSResponder takes too much RPRVT + +Revision 1.20 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + +Revision 1.19 2003/08/05 22:20:15 cheshire + Need to check IP TTL on responses + +Revision 1.18 2003/07/22 23:57:20 cheshire +Move platform-layer function prototypes from mDNSClientAPI.h to mDNSPlatformFunctions.h where they belong + +Revision 1.17 2003/07/19 03:15:15 cheshire +Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, +and add the obvious trivial implementations to each platform support layer + +Revision 1.16 2003/07/02 21:19:46 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.15 2003/05/23 22:39:45 cheshire + Need to adjust maximum packet size for IPv6 + +Revision 1.14 2003/04/28 21:54:57 cheshire +Fix compiler warning + +Revision 1.13 2003/03/15 04:40:36 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.12 2003/02/21 01:54:08 cheshire +Bug #: 3099194 mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.11 2002/12/23 22:13:29 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.10 2002/09/21 20:44:49 zarzycki +Added APSL info + +Revision 1.9 2002/09/19 04:20:43 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.8 2002/09/16 23:12:14 cheshire +Minor code tidying + +Revision 1.7 2002/09/16 18:41:42 cheshire +Merge in license terms from Quinn's copy, in preparation for Darwin release + +*/ + +#ifndef __mDNSPlatformFunctions_h +#define __mDNSPlatformFunctions_h + // *************************************************************************** // Support functions which must be provided by each set of specific PlatformSupport files @@ -5,8 +87,6 @@ // When Setup is complete, the callback is called. // mDNSPlatformSendUDP() sends one UDP packet // When a packet is received, the PlatformSupport code calls mDNSCoreReceive() -// mDNSPlatformScheduleTask() indicates that a timer should be set, -// and mDNSCoreTask() should be called when the timer expires // mDNSPlatformClose() tidies up on exit #ifdef __cplusplus @@ -28,42 +108,78 @@ typedef struct // We can send and receive packets up to 9000 bytes (Ethernet Jumbo Frame size, if that ever becomes widely used) // However, in the normal case we try to limit packets to 1500 bytes so that we don't get IP fragmentation on standard Ethernet -#define AbsoluteMaxDNSMessageData 8960 -#define NormalMaxDNSMessageData 1460 +// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total +#define AbsoluteMaxDNSMessageData 8940 +#define NormalMaxDNSMessageData 1440 typedef struct { DNSMessageHeader h; // Note: Size 12 bytes - mDNSu8 data[AbsoluteMaxDNSMessageData]; // 20 (IP) + 8 (UDP) + 12 (header) + 8960 (data) = 9000 + mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 } DNSMessage; // *************************************************************************** // Functions -// Every platform support module must provide the following functions -extern mStatus mDNSPlatformInit (mDNS *const m); -extern void mDNSPlatformClose (mDNS *const m); +// Every platform support module must provide the following functions. +// Note: mDNSPlatformMemAllocate/mDNSPlatformMemFree are only required for handling oversized resource records. +// If your target platform has a well-defined specialized application, and you know that all the records it uses +// are InlineCacheRDSize or less, then you can just make a simple mDNSPlatformMemAllocate() stub that always returns +// NULL. InlineCacheRDSize is a compile-time constant, which is set by default to 64. If you need to handle records +// a little larger than this and you don't want to have to implement run-time allocation and freeing, then you +// can raise the value of this constant to a suitable value (at the expense of increased memory usage). +extern mStatus mDNSPlatformInit (mDNS *const m); +extern void mDNSPlatformClose (mDNS *const m); extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - mDNSIPAddr src, mDNSIPPort srcport, mDNSIPAddr dst, mDNSIPPort dstport); + mDNSInterfaceID InterfaceID, mDNSIPPort srcport, const mDNSAddr *dst, mDNSIPPort dstport); -extern void mDNSPlatformScheduleTask(const mDNS *const m, mDNSs32 NextTaskTime); extern void mDNSPlatformLock (const mDNS *const m); extern void mDNSPlatformUnlock (const mDNS *const m); -extern void mDNSPlatformStrCopy(const void *src, void *dst); -extern mDNSu32 mDNSPlatformStrLen (const void *src); -extern void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len); -extern mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len); -extern void mDNSPlatformMemZero( void *dst, mDNSu32 len); -extern mDNSs32 mDNSPlatformTimeNow(); -extern mDNSs32 mDNSPlatformOneSecond; +extern void mDNSPlatformStrCopy (const void *src, void *dst); +extern mDNSu32 mDNSPlatformStrLen (const void *src); +extern void mDNSPlatformMemCopy (const void *src, void *dst, mDNSu32 len); +extern mDNSBool mDNSPlatformMemSame (const void *src, const void *dst, mDNSu32 len); +extern void mDNSPlatformMemZero ( void *dst, mDNSu32 len); +extern void * mDNSPlatformMemAllocate (mDNSu32 len); +extern void mDNSPlatformMemFree (void *mem); +extern mStatus mDNSPlatformTimeInit (mDNSs32 *timenow); // The core mDNS code provides these functions, for the platform support code to call at appropriate times +// +// mDNS_GenerateFQDN() is called once on startup (typically from mDNSPlatformInit()) +// and then again on each subsequent dot-local host name change. +// +// mDNS_RegisterInterface() is used by the platform support layer to inform mDNSCore of what +// physical and/or logical interfaces are available for sending and receiving packets. +// Typically it is called on startup for each available interface, but register/deregister may be +// called again later, on multiple occasions, to inform the core of interface configuration changes. +// If set->Advertise is set non-zero, then mDNS_RegisterInterface() also registers the standard +// resource records that should be associated with every publicised IP address/interface: +// -- Name-to-address records (A/AAAA) +// -- Address-to-name records (PTR) +// -- Host information (HINFO) +// +// mDNSCoreInitComplete() is called when the platform support layer is finished. +// Typically this is at the end of mDNSPlatformInit(), but may be later +// (on platforms like OT that allow asynchronous initialization of the networking stack). +// +// mDNSCoreReceive() is called when a UDP packet is received +// +// mDNSCoreMachineSleep() is called when the machine sleeps or wakes +// (This refers to heavyweight laptop-style sleep/wake that disables network access, +// not lightweight second-by-second CPU power management modes.) + +extern void mDNS_GenerateFQDN(mDNS *const m); +extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set); +extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set); extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); extern void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - mDNSIPAddr srcaddr, mDNSIPPort srcport, mDNSIPAddr dstaddr, mDNSIPPort dstport, mDNSIPAddr InterfaceAddr); -extern void mDNSCoreTask (mDNS *const m); -extern void mDNSCoreSleep (mDNS *const m, mDNSBool wake); + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, + const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl); +extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); #ifdef __cplusplus } #endif + +#endif diff --git a/mDNSCore/mDNSsprintf.c b/mDNSCore/mDNSsprintf.c deleted file mode 100755 index beaab51..0000000 --- a/mDNSCore/mDNSsprintf.c +++ /dev/null @@ -1,227 +0,0 @@ -#include -#include // For va_list support - -#include "mDNSsprintf.h" -#include "mDNSvsprintf.h" - -static const struct mDNSsprintf_format - { - unsigned leftJustify : 1; - unsigned forceSign : 1; - unsigned zeroPad : 1; - unsigned havePrecision : 1; - unsigned hSize : 1; - unsigned lSize : 1; - char altForm; - char sign; // +, - or space - int fieldWidth; - int precision; - } default_format = { 0 }; - -#define BUFLEN 512 - -int mDNS_vsprintf(char *sbuffer, const char *fmt, va_list arg) - { - int c, nwritten = 0; - - for (c = *fmt; c; c = *++fmt) - { - int i=0, j; - char buf[BUFLEN], *digits; - char *s = &buf[BUFLEN]; - struct mDNSsprintf_format F; - if (c != '%') goto copy1; - F = default_format; - - for (;;) // decode flags - { - c = *++fmt; - if (c == '-') F.leftJustify = 1; - else if (c == '+') F.forceSign = 1; - else if (c == ' ') F.sign = ' '; - else if (c == '#') F.altForm++; - else if (c == '0') F.zeroPad = 1; - else break; - } - - if (c == '*') // decode field width - { - if ((F.fieldWidth = va_arg(arg, int)) < 0) - { - F.leftJustify = 1; - F.fieldWidth = -F.fieldWidth; - } - c = *++fmt; - } - else - { - for (; c >= '0' && c <= '9'; c = *++fmt) - F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); - } - - if (c == '.') // decode precision - { - if ((c = *++fmt) == '*') - { F.precision = va_arg(arg, int); c = *++fmt; } - else for (; c >= '0' && c <= '9'; c = *++fmt) - F.precision = (10 * F.precision) + (c - '0'); - if (F.precision >= 0) F.havePrecision = 1; - } - - if (F.leftJustify) F.zeroPad = 0; - -conv: switch (c) // perform appropriate conversion - { - unsigned long n; - case 'h' : F.hSize = 1; c = *++fmt; goto conv; - case 'l' : // fall through - case 'L' : F.lSize = 1; c = *++fmt; goto conv; - case 'd' : - case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long); - else n = (unsigned long)va_arg(arg, int); - if (F.hSize) n = (short) n; - if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } - else if (F.forceSign) F.sign = '+'; - goto decimal; - case 'u' : if (F.lSize) n = va_arg(arg, unsigned long); - else n = va_arg(arg, unsigned int); - if (F.hSize) n = (unsigned short) n; - F.sign = 0; - goto decimal; - decimal: if (!F.havePrecision) - { - if (F.zeroPad) - { - F.precision = F.fieldWidth; - if (F.sign) --F.precision; - } - if (F.precision < 1) F.precision = 1; - } - for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0'); - for (; i < F.precision; i++) *--s = '0'; - if (F.sign) { *--s = F.sign; i++; } - break; - - case 'o' : if (F.lSize) n = va_arg(arg, unsigned long); - else n = va_arg(arg, unsigned int); - if (F.hSize) n = (unsigned short) n; - if (!F.havePrecision) - { - if (F.zeroPad) F.precision = F.fieldWidth; - if (F.precision < 1) F.precision = 1; - } - for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0'); - if (F.altForm && i && *s != '0') { *--s = '0'; i++; } - for (; i < F.precision; i++) *--s = '0'; - break; - - case 'a' : { - unsigned char *a = va_arg(arg, unsigned char *); - unsigned short *w = (unsigned short *)a; - s = buf; - switch (F.precision) - { - case 4: i = mDNS_sprintf(s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]); break; - case 6: i = mDNS_sprintf(s, "%02X:%02X:%02X:%02X:%02X:%02X", a[0], a[1], a[2], a[3], a[4], a[5]); break; - case 16: i = mDNS_sprintf(s, "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X", - w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]); break; - default: i = mDNS_sprintf(s, "%s", "ERROR: Must specify address size " - "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; - } - } - break; - - case 'p' : F.havePrecision = F.lSize = 1; - F.precision = 8; - case 'X' : digits = "0123456789ABCDEF"; - goto hexadecimal; - case 'x' : digits = "0123456789abcdef"; - hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long); - else n = va_arg(arg, unsigned int); - if (F.hSize) n = (unsigned short) n; - if (!F.havePrecision) - { - if (F.zeroPad) - { - F.precision = F.fieldWidth; - if (F.altForm) F.precision -= 2; - } - if (F.precision < 1) F.precision = 1; - } - for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; - for (; i < F.precision; i++) *--s = '0'; - if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } - break; - - case 'c' : *--s = (char)va_arg(arg, int); i = 1; break; - - case 's' : s = va_arg(arg, char *); - switch (F.altForm) - { - case 0: { char *a=s; i=0; while(*a++) i++; break; } // C string - case 1: i = (unsigned char) *s++; break; // Pascal string - case 2: { // DNS label-sequence name - unsigned char *a = (unsigned char *)s; - s = buf; - if (*a == 0) *s++ = '.'; // Special case for root DNS name - while (*a && s + *a + 1 < &buf[BUFLEN]) - { - s += mDNS_sprintf(s, "%#s.", a); - a += 1 + *a; - } - i = (int)(s - buf); - s = buf; - break; - } - } - if (F.havePrecision && i > F.precision) i = F.precision; - break; - - case 'n' : s = va_arg(arg, char *); - if (F.hSize) * (short *) s = (short)nwritten; - else if (F.lSize) * (long *) s = (long)nwritten; - else * (int *) s = (int)nwritten; - continue; - - // oops - unknown conversion, abort - - case 'M': case 'N': case 'O': case 'P': case 'Q': - case 'R': case 'S': case 'T': case 'U': case 'V': - // (extra cases force this to be an indexed switch) - default: goto done; - - case '%' : - copy1 : *sbuffer++ = (char)c; ++nwritten; continue; - } - - // pad on the left - - if (i < F.fieldWidth && !F.leftJustify) - do { *sbuffer++ = ' '; ++nwritten; } while (i < --F.fieldWidth); - - // write the converted result - - for (j=0; j Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.16 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + + */ + +#include // For printf() +#include // For strlen() etc. + +#include // For WaitNextEvent() +#include // For SIOUXHandleOneEvent() + +#include "mDNSClientAPI.h" // Defines the interface to the client layer above + +#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform + +// These don't have to be globals, but their memory does need to remain valid for as +// long as the search is going on. They are declared as globals here for simplicity. +static mDNS m; +static mDNS_PlatformSupport p; +static ServiceRecordSet p1, p2, afp, http, njp; +static AuthRecord browsedomain; + +// This sample code just calls mDNS_RenameAndReregisterService to automatically pick a new +// unique name for the service. For a device such as a printer, this may be appropriate. +// For a device with a user interface, and a screen, and a keyboard, the appropriate +// response may be to prompt the user and ask them to choose a new name for the service. +mDNSlocal void Callback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) + { + switch (result) + { + case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name.c); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name.c); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name.c); break; + default: debugf("Callback: %##s Unknown Result %d", sr->RR_SRV.resrec.name.c, result); break; + } + + if (result == mStatus_NameConflict) mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + } + +// RegisterService() is a simple wrapper function which takes C string +// parameters, converts them to domainname parameters, and calls mDNS_RegisterService() +mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, + UInt16 PortAsNumber, const char txtinfo[], + const domainlabel *const n, const char type[], const char domain[]) + { + mDNSIPPort port; + domainname t; + domainname d; + char buffer[512]; + UInt8 txtbuffer[512]; + + port.b[0] = (UInt8)(PortAsNumber >> 8); + port.b[1] = (UInt8)(PortAsNumber ); + MakeDomainNameFromDNSNameString(&t, type); + MakeDomainNameFromDNSNameString(&d, domain); + + if (txtinfo) + { + strncpy((char*)txtbuffer+1, txtinfo, sizeof(txtbuffer)-1); + txtbuffer[0] = (UInt8)strlen(txtinfo); + } + else + txtbuffer[0] = 0; + + mDNS_RegisterService(m, recordset, + n, &t, &d, // Name, type, domain + mDNSNULL, port, // Host and port + txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length + mDNSNULL, 0, // Subtypes (none) + mDNSInterface_Any, // Interace ID + Callback, mDNSNULL); // Callback and context + + ConvertDomainNameToCString(&recordset->RR_SRV.resrec.name, buffer); + printf("Made Service Records for %s\n", buffer); + } + +// RegisterFakeServiceForTesting() simulates the effect of services being registered on +// dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. +mDNSlocal void RegisterFakeServiceForTesting(mDNS *m, ServiceRecordSet *recordset, const char txtinfo[], + const char name[], const char type[], const char domain[]) + { + static UInt16 NextPort = 0xF000; + domainlabel n; + MakeDomainLabelFromLiteralString(&n, name); + RegisterService(m, recordset, NextPort++, txtinfo, &n, type, domain); + } + +// CreateProxyRegistrationForRealService() checks to see if the given port is currently +// in use, and if so, advertises the specified service as present on that port. +// This is useful for advertising existing real services (Personal Web Sharing, Personal +// File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves. +mDNSlocal OSStatus CreateProxyRegistrationForRealService(mDNS *m, UInt16 PortAsNumber, const char txtinfo[], + const char *servicetype, ServiceRecordSet *recordset) + { + mDNSIPPort port; + InetAddress ia; + TBind bindReq; + OSStatus err; + TEndpointInfo endpointinfo; + EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err); + if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); } + + port.b[0] = (UInt8)(PortAsNumber >> 8); + port.b[1] = (UInt8)(PortAsNumber ); + ia.fAddressType = AF_INET; + ia.fPort = port.NotAnInteger; + ia.fHost = 0; + bindReq.addr.maxlen = sizeof(ia); + bindReq.addr.len = sizeof(ia); + bindReq.addr.buf = (UInt8*)&ia; + bindReq.qlen = 0; + err = OTBind(ep, &bindReq, NULL); + + if (err == kOTBadAddressErr) + RegisterService(m, recordset, PortAsNumber, txtinfo, &m->nicelabel, servicetype, "local."); + else if (err) + debugf("OTBind failed %d", err); + + OTCloseProvider(ep); + return(noErr); + } + +// Done once on startup, and then again every time our address changes +mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) + { + char buffer[256]; + mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; + + ConvertDomainNameToCString(&m->hostname, buffer); + printf("Name %s\n", buffer); + printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); + + printf("\n"); + printf("Registering Service Records\n"); + // Create example printer discovery records + //static ServiceRecordSet p1, p2; + +#define SRSET 0 +#if SRSET==0 + RegisterFakeServiceForTesting(m, &p1, "path=/index.html", "Web Server One", "_http._tcp.", "local."); + RegisterFakeServiceForTesting(m, &p2, "path=/path.html", "Web Server Two", "_http._tcp.", "local."); +#elif SRSET==1 + RegisterFakeServiceForTesting(m, &p1, "rn=lpq1", "Epson Stylus 900N", "_printer._tcp.", "local."); + RegisterFakeServiceForTesting(m, &p2, "rn=lpq2", "HP LaserJet", "_printer._tcp.", "local."); +#else + RegisterFakeServiceForTesting(m, &p1, "rn=lpq3", "My Printer", "_printer._tcp.", "local."); + RegisterFakeServiceForTesting(m, &p2, "lrn=pq4", "My Other Printer", "_printer._tcp.", "local."); +#endif + + // If AFP Server is running, register a record for it + CreateProxyRegistrationForRealService(m, 548, "", "_afpovertcp._tcp.", &afp); + + // If Web Server is running, register a record for it + CreateProxyRegistrationForRealService(m, 80, "", "_http._tcp.", &http); + + // And pretend we always have an NJP server running on port 80 too + //RegisterService(m, &njp, 80, "NJP/", &m->nicelabel, "_njp._tcp.", "local."); + + // Advertise that apple.com. is available for browsing + mDNS_AdvertiseDomains(m, &browsedomain, mDNS_DomainTypeBrowse, mDNSInterface_Any, "IL 2\\4th Floor.apple.com."); + + return(kOTNoError); + } + +// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS +mDNSlocal Boolean YieldSomeTime(UInt32 milliseconds) + { + extern Boolean SIOUXQuitting; + EventRecord e; + WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); + SIOUXHandleOneEvent(&e); + return(SIOUXQuitting); + } + +int main() + { + extern void mDNSPlatformIdle(mDNS *const m); // Only needed for debugging version + mStatus err; + Boolean DoneSetup = false; + + SIOUXSettings.asktosaveonclose = false; + SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; + + printf("Prototype Multicast DNS Responder\n\n"); + printf("WARNING! This is experimental software.\n\n"); + printf("Multicast DNS is currently an experimental protocol.\n\n"); + printf("This software reports errors using MacsBug breaks,\n"); + printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); + printf("******************************************************************************\n"); + + err = InitOpenTransport(); + if (err) { debugf("InitOpenTransport failed %d", err); return(err); } + + err = mDNS_Init(&m, &p, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_AdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (err) return(err); + + while (!YieldSomeTime(35)) + { + // For debugging, use "#define __ONLYSYSTEMTASK__ 1" and call mDNSPlatformIdle() periodically. + // For shipping code, don't define __ONLYSYSTEMTASK__, and you don't need to call mDNSPlatformIdle() + mDNSPlatformIdle(&m); // Only needed for debugging version + if (m.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) + { + DoneSetup = true; + printf("\nListening for mDNS queries...\n"); + mDNSResponderTestSetup(&m); + } + } + + if (p1.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p1); + if (p2.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p2); + if (afp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &afp); + if (http.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &http); + if (njp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &njp); + + mDNS_Close(&m); + + return(0); + } diff --git a/mDNSMacOS9/Mac OS Test Searcher.c b/mDNSMacOS9/Mac OS Test Searcher.c new file mode 100644 index 0000000..d55dcd5 --- /dev/null +++ b/mDNSMacOS9/Mac OS Test Searcher.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Mac\040OS\040Test\040Searcher.c,v $ +Revision 1.13 2003/08/14 02:19:54 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.12 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + + */ + +#include // For printf() +#include // For WaitNextEvent() +#include // For SIOUXHandleOneEvent() + +#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform + +typedef struct + { + OTLIFO serviceinfolist; + Boolean headerPrinted; + Boolean lostRecords; + } SearcherServices; + +typedef struct { ServiceInfo i; mDNSBool add; mDNSBool dom; OTLink link; } linkedServiceInfo; + +// These don't have to be globals, but their memory does need to remain valid for as +// long as the search is going on. They are declared as globals here for simplicity. +#define RR_CACHE_SIZE 1000 +static CacheRecord rrcachestorage[RR_CACHE_SIZE]; +static mDNS mDNSStorage; +static mDNS_PlatformSupport PlatformSupportStorage; +static SearcherServices services; +static DNSQuestion browsequestion, domainquestion; + +// PrintServiceInfo prints the service information to standard out +// A real application might want to do something else with the information +static void PrintServiceInfo(SearcherServices *services) + { + OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist)); + + while (link) + { + linkedServiceInfo *ls = OTGetLinkObject(link, linkedServiceInfo, link); + ServiceInfo *s = &ls->i; + + if (!services->headerPrinted) + { + printf("%-55s Type Domain IP Address Port Info\n", "Name"); + services->headerPrinted = true; + } + + if (ls->dom) + { + char c_dom[256]; + ConvertDomainNameToCString(&s->name, c_dom); + if (ls->add) printf("%-55s available for browsing\n", c_dom); + else printf("%-55s no longer available for browsing\n", c_dom); + } + else + { + domainlabel name; + domainname type, domain; + UInt16 port = (UInt16)((UInt16)s->port.b[0] << 8 | s->port.b[1]); + char c_name[64], c_type[256], c_dom[256], c_ip[20]; + + DeconstructServiceName(&s->name, &name, &type, &domain); + ConvertDomainLabelToCString_unescaped(&name, c_name); + ConvertDomainNameToCString(&type, c_type); + ConvertDomainNameToCString(&domain, c_dom); + sprintf(c_ip, "%d.%d.%d.%d", s->ip.ip.v4.b[0], s->ip.ip.v4.b[1], s->ip.ip.v4.b[2], s->ip.ip.v4.b[3]); + + printf("%-55s %-16s %-14s ", c_name, c_type, c_dom); + if (ls->add) printf("%-15s %5d %#s\n", c_ip, port, s->TXTinfo); + else printf("Removed\n"); + } + + link = link->fNext; + OTFreeMem(ls); + } + } + +// When the name, address, port, and txtinfo for a service is found, FoundInstanceInfo() +// enqueues a record for PrintServiceInfo() to print. +// Note, a browsing application would *not* normally need to get all this information -- +// all it needs is the name, to display to the user. +// Finding out the address, port, and txtinfo should be deferred to the time that the user +// actually needs to contact the service to use it. +static void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) + { + SearcherServices *services = (SearcherServices *)query->ServiceInfoQueryContext; + linkedServiceInfo *info = (linkedServiceInfo *)(query->info); + if (query->info->ip.type == mDNSAddrType_IPv4) + { + mDNS_StopResolveService(m, query); // For this test code, one answer is sufficient + OTLIFOEnqueue(&services->serviceinfolist, &info->link); + OTFreeMem(query); + } + } + +// When a new named instance of a service is found, FoundInstance() is called. +// In this sample code we turn around and immediately issue a query to resolve that service name to +// find its address, port, and txtinfo, but a normal browing application would just display the name. +static void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + #pragma unused (question) + SearcherServices *services = (SearcherServices *)question->QuestionContext; + linkedServiceInfo *info; + + debugf("FoundInstance %##s PTR %##s", answer->name.c, answer->rdata->u.name.c); + + if (answer->rrtype != kDNSType_PTR) return; + if (!services) { debugf("FoundInstance: services is NULL"); return; } + + info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); + if (!info) { services->lostRecords = true; return; } + + info->i.name = answer->rdata->u.name; + info->i.InterfaceID = answer->InterfaceID; + info->i.ip.type = mDNSAddrType_IPv4; + info->i.ip.ip.v4 = zeroIPAddr; + info->i.port = zeroIPPort; + info->add = AddRecord; + info->dom = mDNSfalse; + + if (!AddRecord) // If TTL == 0 we're deleting a service, + OTLIFOEnqueue(&services->serviceinfolist, &info->link); + else // else we're adding a new service + { + ServiceInfoQuery *q = (ServiceInfoQuery *)OTAllocMem(sizeof(ServiceInfoQuery)); + if (!q) { OTFreeMem(info); services->lostRecords = true; return; } + mDNS_StartResolveService(m, q, &info->i, FoundInstanceInfo, services); + } + } + +static void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + #pragma unused (m) + #pragma unused (question) + SearcherServices *services = (SearcherServices *)question->QuestionContext; + linkedServiceInfo *info; + + debugf("FoundDomain %##s PTR %##s", answer->name.c, answer->rdata->u.name.c); + + if (answer->rrtype != kDNSType_PTR) return; + if (!services) { debugf("FoundDomain: services is NULL"); return; } + + info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); + if (!info) { services->lostRecords = true; return; } + + info->i.name = answer->rdata->u.name; + info->i.InterfaceID = answer->InterfaceID; + info->i.ip.type = mDNSAddrType_IPv4; + info->i.ip.ip.v4 = zeroIPAddr; + info->i.port = zeroIPPort; + info->add = AddRecord; + info->dom = mDNStrue; + + OTLIFOEnqueue(&services->serviceinfolist, &info->link); + } + +// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS +static Boolean YieldSomeTime(UInt32 milliseconds) + { + extern Boolean SIOUXQuitting; + EventRecord e; + WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); + SIOUXHandleOneEvent(&e); + return(SIOUXQuitting); + } + +int main() + { + extern void mDNSPlatformIdle(mDNS *const m); // Only needed for debugging version + mStatus err; + Boolean DoneSetup = false; + + SIOUXSettings.asktosaveonclose = false; + SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher"; + SIOUXSettings.rows = 40; + SIOUXSettings.columns = 132; + + printf("Prototype Multicast DNS Searcher\n\n"); + printf("WARNING! This is experimental software.\n\n"); + printf("Multicast DNS is currently an experimental protocol.\n\n"); + printf("This software reports errors using MacsBug breaks,\n"); + printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); + printf("******************************************************************************\n"); + + err = InitOpenTransport(); + if (err) { debugf("InitOpenTransport failed %d", err); return(err); } + + err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE, + mDNS_Init_DontAdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (err) return(err); + + services.serviceinfolist.fHead = NULL; + services.headerPrinted = false; + services.lostRecords = false; + + while (!YieldSomeTime(35)) + { + // For debugging, use "#define __ONLYSYSTEMTASK__ 1" and call mDNSPlatformIdle() periodically. + // For shipping code, don't define __ONLYSYSTEMTASK__, and you don't need to call mDNSPlatformIdle() + mDNSPlatformIdle(&mDNSStorage); // Only needed for debugging version + if (mDNSStorage.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) + { + domainname srvtype, srvdom; + DoneSetup = true; + printf("\nSending mDNS service lookup queries and waiting for responses...\n\n"); + MakeDomainNameFromDNSNameString(&srvtype, "_http._tcp."); + MakeDomainNameFromDNSNameString(&srvdom, "local."); + err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, FoundInstance, &services); + if (err) break; + err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, mDNSInterface_Any, FoundDomain, &services); + if (err) break; + } + + if (services.serviceinfolist.fHead) + PrintServiceInfo(&services); + + if (services.lostRecords) + { + services.lostRecords = false; + printf("**** Warning: Out of memory: Records have been missed.\n"); + } + } + + mDNS_StopBrowse(&mDNSStorage, &browsequestion); + mDNS_Close(&mDNSStorage); + return(0); + } diff --git a/mDNSMacOS9/README.txt b/mDNSMacOS9/README.txt new file mode 100644 index 0000000..4d13761 --- /dev/null +++ b/mDNSMacOS9/README.txt @@ -0,0 +1,10 @@ +This directory contains support files for running mDNS on Mac OS 9 +(and Carbon). + +mDNS.mcp is a CodeWarrior 8 project file. + +mDNSMacOS9.c and mDNSMacOS9.h are the Platform Support files that go below +mDNS Core. + +"Mac OS Test Responder.c" and "Mac OS Test Searcher.c" build an example +mDNS Responder and Searcher, respectively. \ No newline at end of file diff --git a/mDNSMacOS9/mDNS.mcp b/mDNSMacOS9/mDNS.mcp new file mode 100644 index 0000000000000000000000000000000000000000..dc4932a6665e1c78f31d5268727305f885a66db4 GIT binary patch literal 413450 zcmeF434B!5^~c|v$;P6hBDfRx1r4YOVNnu75QIPk6vPFFWPm`(#3U#nN^sxBy4DqU zt$WpKT`F2@wbuS^Yip}rXx-Y{#oAhJtL6Va_nmn&Z|1!>14L1n`}zFd{ms4S-R0bK z?tS|-BofV*WkoH^-q$MGf0R|yf4*hyv5Mcz?1)X46KWET@oBNPwx&c|d3{@Ae!QV0 znYEe3MYAodsME5po?%(n&}aGW8`?=d3_J=R1`mP9!7boUa5K0E+yqvE2-pYo2m65v za4;AM%E2CBTQCaLfFra)CF9b5}82Hya8 zgX_V!!8p(hoC;0{%fNE50(62iz%Jl$uqD_VoCcPHMc`y`8@LOI|11QpU;(%e+zJ}N znc!0JB)AN$1b2WUFcNG9b_7+RC)f#W0d@sjgMQ#%a04g-+ksNh2gE=Jh=Vzx5j27M zU<#NDx`7?QEO0b92Alwa2!a03&45cd~hQ88mI&# zz%VcYd>vc^t_ItKFFHI=g=-i##!`mZlHaq8Wrz)Rmn3Y~X4`%8q)DPBH$;z^e0073 zDl46mSk%@KZ|8T>m~j(o8M8qWGY&)a_ehNoPutJ#QPUi2Z*OWSuZuOXZ<)xTLnBLE zrN65+9;I=O#-lZ^)p$%=+1#M)*jTD0h>h|sKLb4=0vuI`MM3_4b9&MFzyyqdn+ zB}1MVSTf|Hfs%%aOYbE_fpB&*^N{$p*qPXvjC(Q`I}-a8+Z0)WC|7LMimbY3mc(Hp zvg(F4nqMASb+b^Zr#!+2)Pf;kI2Z+Lz-UmLl+#M9`7QoE!FW#gxXv`aknxqC$Q&ZQ zkhw`}lex}`k8(_dIm3}#p!RW$>9P2SqoETup04S~YI!r%j*ip)p4WPhH!%%&cBj>Y zzz}da7zwICwaIpBHq9~{qYv4DTG=dLE9qHV+oo{9wk%>wB1L{Xb;>p+Ve*shA&VVE z)+C!P7T}kbDnW~trdVaB7L($ZVKTHlhfDp^3MYsOk!{+WN~K9|D3&G>oTjC<1|19m z!@(#}14aYaD5PjmEy~*J$k@p-<9YG+iuo~Fm^@*|xcc(CX)_$}cG@A^^mZ6|5{p38 zh-~TfE3G%4GBbXdU7U`JPR*i}A>#a`&4a)Ya5xwVsz7yiYqEAPQ-jOOFbTbwkrTF5I+GU@7Ld57MAIY3D#y3(@X^r3Daa;chI=vN^wWjR8 z;t#ZmuXa1Qc4e%+A=X@8lUUf=)LeF7WX;4WTAdN?v!Rf{?l2g=UPSL5Lk~_ zJ0!Ui33E}_l&vXduhg`kRfg(!vx(9akU4`dZEDR_gUsMZRv%MMdLGhgdrLLzwnzF3 zkXBGOZSRP+$uao)1~Zu@cMhNTN~KY1cqzrXW=A2ZR4Fml2dDgvw*qA9KEKsDx2L7| z-RWcIHhi@drwv)9DNJ-Gr)U!Qe6sQzHEY-2BByhn;2dFkk_taIHw|Fp>YJ(geRI=6 zp})QwkmJ>bu_n2!viXKm=N!trM6HK9X3pHuC3ih4_3V;yKGWH$eG2Xx=T_TJ>1Zsk z>EGWyL>^o@-cxFhA0r9X_4Qm{i&)cF=Mc^#Tgr3Aj<#3Y-6E&DzYWQ~xO(Mt{*uc@ z`2}MtqRupxrqTQg+2o`g2XeZ_Y7=mdk$H9;t_C@_ks!xGoE29&%$6&;g;3Cd7{yuw zkywa5Y*@iE3RdCF%W4-zVi^UiC|HGPAUR)D35`0}eUl4!-IQ^1-M$buV-3vf=x7}_ zV8DFt)Ks)DYN=>QEF93ijubDlYb7GD=}1{8DkA6evvBX?g)&U3lk-3hr-$UT*kbF)M-?hoW@ z_F<|aP98YA zKGk2hizIvW+|_b}DgWA3z5aH9%)#$-zsa^s_S9o!l1j#~83YI=v)LR9s^F($?t>k< z5_xD!e1V6*O1?!b?I)DH#%8Fvg_bvLfi3r*OXc3K_yqE$2NI8vhis7BHB~RE89^St zp?yWn%)LY|vg*bvr#zec`*+T2htXbva9U>RwwrG54V6n&zf>OUJm;J$e4LgXd3cTRPg7^tA2vxX8&5 zX`iGv#oJ4d8eQAgmS~e5r!}?IBy78_o5(F%%uiEXhiMS{s7D|2%tN_V5_`?I%N`WD zc*n%J^gpud7Ri@qBP9Pteeg%7siQ=W{8mSQ)@sq8eY(AzYcn%>gmRFSjod7e<8j*4 zNRL^A>{FG`78!GIDPxQZKQ}uKcqXyUuJ?rwyBa9F@3Mi;rE=Sn=WN^zNZGyr8(4PV zmGvh7%o_)q{Iq3A^3vtMlDu#`-GA6}KL5cX!613yyKt|`tf>*st@8_|Yp+8va{X;) zkBa)oyh1}O)S8y=+np!>v06RYcLD{809 zxuj31k~DK7$VTJr+q@B;i%&U$JI>TXEW-* z#q;Nduz71hZqaN#uB9X1))Mb1uU*{AX+4iuuTvNocXq3YFOIL%js@XOL<2$3N721| zK;AtvVSL(I!FXf$$;#fsu zQP>;xRnir%o0yHp*>^Sy)2e7_vkrx6b+aD+F6w+SAN4mcN_2hIl? z3EF%H(!K&*iL$8Ee3FqmE$T9#UZgGN(~Q(Zv(|oFsI^E9`Qm6S0Bz2lk zKvJjq6eI04pH#e$_9tHA*?qlF_@NMt} zcoIAXo(6Y=&%plx7l@4^Nx!ZFR|9$4m%f`%V1AwWT5ugeC+2gQ*Aw3WZUi@hRp4eI z7oKkgw}IQi9RS^#Pd7eK{5|jjcm_NNz7JjmFM;oXXTcA^pTS?iU%|`Zhu{_PBk+IV zRq$i*6Yx{;8dwef20j3P2OrX}5>N_wZp-HBBYB|TE(YB}56~0Pd7HJ`-U4h1wgSDt z)?gd3E$9ul1ARbWAkVeE0p0|^0>1`72fqZr0lx)nz|X)h!0*66z(2u9;P>Dy@HTh{ zybIm~e*k|3e**7=*TKKQ$KVt2Z?GrW3+xT{0p(y1urJsT><{{Z-9dkF05}j-fB|42 zI0zgJ4grI}p;{hJz7c7Knk_paC2YW`af#2Xnx2;6yMN%mYneK3D*n z!9vgi5}*~F1lmA5=l~~xMc`y`3RnynFZn`}c?f`UG{(w!na_tZj^?XyMCQ3F#?yQT zl5sTO#$g=IXCN6z^O;)4(R`+~4{=|xJ=g(Y7v@u^I}z^;b^)V64Hyk-!Qr49i~&c0 zvEVRJ1;&9R!FVtM)Pad$5~v49fyrPBm9jB$i9jxfd%#yG+lM;PM> zV;s2yFvgKP0plEDoFj~L5p6D_&5^sn-GDwu=zD~=7SWFg_7|xF=qOSS&`AVc716&4 z`ih{h2>ObkrwDqA{s8W;A8M9_ze6P{1bcv{ssOA)>4KI=yy>BL_rZK1|^^rlmQEL1KmLn&=YI{ z&{Yw-D%uik1$u$4!8Snj_iy5Di9ZA%!Fz-4z*x`+^ab049l(yrEC$QLGOz-i0Xo4F za2hxjoC(eX=djOo;&Z`y;CyfaxDZ?fE(T|VOTeXICAbWHjs1=VGvJqlE5McDDsVOF z#}S_nt^ucjrQqw}TJnx3z7AXuZU8reo50QBE^s^e2Dk^@4Q>Hm;6CsG<%}bK z5Ih9F2_6QIfJec%!2RGc@HqH3cmh1hen*1w@Tb7j;5*REYgIQpAFaqoZb_Iul!$Ay;1XZ9Ki~=?I=4;?h@C)#3 z@Eh&{i z0~`mA2k1P4{vzlvg03RyBr+Q`fEz(0xCz9;955HG0yl$spb5+elfVKn7GOh>X0Q;n zfCOj-CxJF>1KB9;jne)oZH&^!DD92X_9*>{(zhsWjMBy^?TgaBDD8{Vz9{XB(!MC| zi_*3zeTdSwC~b?H6P%sP(2ZsRcFN)no4+kSb6{rT- zTojv&js~^h05AsZ1@;9;0CXB`}6x~J{!>Ek$Y2b9Q9CU&efIdV{1&hHM;7o89 zSO(}@WC=J0oDI$a=YsRV`QQR@A-D)!3@!n;fJ?!xU?sQ=d=1ixE5MatDYy!t z%LqD*Tn(-PUkBHM>%jHk25=AfCU^im0v-hqgL}b);C}Ee@EG_ucmg~Lo&ryU?|^5( zYVbIC7CZ;O3!Vqx122H@gBQU|;0NF#@G|%YxDWggyaIj%{tvtgehhvBK10t%zy>9t z6cm&GKjH|mKpE%;dVro_3$P{F3iJY7gKa=}ur25fwgY`YU$8yc0qh8N0y_f=j_yJ| zpMtgUT|qxk4t4{(gFV2W9P*)48=czd$VRXBjR3vd=)y+l_Dx_FxEU}uHu|&Cwau8= z=-0jlpii4Iu+gcF{_I=9ZGiE$(Z78Mcpjir`%Z8dK==0D06p4_jm>!4jGc{6ZN|=K zJZ#3+X3T7Cz-CNsbZ;{zHsfV8ZZ_j@Gv@Lpq>avPbZ-9?yas*-7&n_Su^A)#=iqhl z3&0rJjHk`G*^H(AOYjDG6Z{JN8vF+Q7OVlk1HT7vfw#ds;9c+@_yhPOVEpYrf$xJK zfcL?l!C$~%!Qa3K;O~HOu|EU<18W&0x?ukU{0Dr@ZyS&piSqlu;Gf_V@GsJefY?w8 zC<__5@IIg~*dFWv zb_6?toyqS9%E4}6cR;?q3y{1$z@A_)z`i#7*fJJ-gMGlhU_Y=w=noD62Z9PP01N~N zfrG&zfO_pg;7~9a3;{F2ED!^;!Li^3a6D)LjUW!DgX6%7U=ElI=7A|8CcSXF<(4K%>1NfM+{yF3#$+Qh61Dua)Ko(<60=29jY$)g#3N!0_|^?qq>@?-tNJ>TX}>xxkM z^1g}nZ^GcrY;>n3-YiqR3)z znsslcRrzHNTR3{+6f?OP($H#6YK^x{ZHu+E%hDv1qDg4&NWSIbCM9L6TU%*MMZzho zd~%#U`J_c#=I`3+^|h17)zwa%T0OyHI&59kQ9h-{E`I3eE)2&2O+#lAIc&x2q9#v}*&M%QM{lp|R-!X#fW{zqmCR*Y~KGpJMHLkoQ z&?xzeMxr6n%rffv)oL}09nEp~GiD{f#BisUOKa4hG7{aUySqAW7bI3bHQwG4lv&}E zne1_Bp4-qYX=!-{oscR`r;hGuYHpWbsg`SYDk0g7R0{HL(JJo#PD;B8PHW1Uf+Q<7 zDmsQ4i4^CGCW}aybkn3ON#&aHPUbM0l9$$6tG%_YsilMQv}zVMGUddj(sTj+zbfgA zTV`5Xx@gigtfO0+8d#rF%hS~OV!pjX8uOAj)hSb~$L(8sk<#1)Q*BHaOY0P~?My6u zrZ}fKZCd*pEWS%>e1KvXb+kL)k)hc@kz^g7LYh6LPoiMr>eZNXhd+enI-6m)R=!Ha|XpwGdxaWTYDQ;=kO%S zY^hJ3&K{Y9GAn6e-mI)P`|a=9Gfjgzs`pB!7wqOFZP97e2-a!?I8e1TF(v+d(q^#R5DAT-M`}ayD$1HadYBE7pB?}4`^Y5CYF}iC0ORs{1 zRyktfH|LV=15%coQU>c%D@VLbI67+Q9J~-Vss^~4WR58|Y9k7j7c{W(G>~?9z402c zF>B`YAY70vgn|Yf4RFUHXQ&bhZc&(x6L)V(v~XFg@MQ?mN+A?9@MX~e{_J^UEx-Tg zV=umEO32zqJssm7rX}Ehp$YOZXfim|%qQG-v#iHu^O$TNmCeJlc}Q-H8o^}D4M`HX z?-&r$DhQA$Bo{PL&_F>0n}Y^;Hlz?XZw(Y)h1Ccz)keGSkg6E;#U>*T;e`S+iBsul;=P~Wz;8TxzzcRT;{`Iu-=yh=L zsi%Cs`qSJnC%Bfb$(dSf_0r>mzH(oi=Q8IsEq3mIJC9JM4W79OCI=H~2NZVh-MXfw zU)oaAltCM-x(&TE+0<9N{c-7o)n{1ScgCy{BcfMb(QW8?RaIwLWy9C-Hg(LZs_b05 zcKl~e=ff`&KGu3_N#%0O>a?xLD|fRVTvisnp~zuKi@CZIxZI z+u8?*f0s9~-{#HYwKxA|-+sJ-e1>(vv^Axt68Gvny|_ji<|K6Z78 z9Iq+l{`N6)yuOh8+sDZ9ibL*iA0x+W5V^m7ybf~t_Jdb0a)0}ndN(UMuA1{oN|*j& z9hCoqa4t1cY~KNl2t z=g$I(WvM8sZ))KcD(eMM8mmw6%9r`fM_Jvt4tX`KjNjel(O39B@a}c-7J0>sSF@}h z{86F24#w+YR!^wB{&hBdi$r^z`sEcyYs;qQX5r7nx0*69A+Lq8Zdkp>HZ@B5ayxA6 zy2QzF`GkYD4S$k|eB=oDwouw8uQ*%1m-65&{CxO!%}vrad4<&Kqw+7q`*ze!rTy|6 zxV3#p^D!}0Pz>LJKQp9l@>;L8V@*eM4|pwnr{+cRZt%JAotwK&p-lPofVGP}DG0v} zzU%0=82R$5z15G$;v3*UgO~Rt1O6}gZuFy*yY{`|yYu9GBm7YK9?sKv@(BoQPx)g+ z(s^CZd?tmc=u$R&147Y5Y(&3=qF31LYp7N9C*u7;xeOce{(w*{asSAwt2z8Embv}_ ztE3VA70-en2nfZevEg+=3B}j38Q|DM@gwkojy)8=4nN4ThvHA*2RrsqBDWq7aqOW) z=B`1GJ(S3y z_kfRZ?4fid{4mEJN_kDuI@}rGQt8V`#~w_>qo1l)VQZ@7P1P zBKQQy9=h!guLCP_8#(6SbwxQ$>2@M9uPutNcH9a zUTc)k#B}>Fv9z@}?gz|gTKd8df*%9$`R-EoG{7z1?hA>h^J)w}+g--xSZoSA=`Q0k zLtZ(AzW_f@UO9xn1wVegH7bTnOJ7cqR}SIQmlJtMiKs^no0-kLfC!%rpCxbH!8_sX zC-&dt2Ka2p{(C$FZ*c6thm1?3ykLWj*cg9AT0WX7V|(}tR_$ezmRx7AoRSO%>qNMp3f2UB8P;Y@34{n6Sfc=Yk7Z`RSXy3O7M0suaRy+ z-B#;Q_Gf44ZC@%cCc!U;pX!vq<$dsFPWfBD3O`NW zR6^$O@YC@l#&Ij`z*;Vk)xihCJLQc$_*D1`K-fy=7U=_FtIOD&X{fc;!^CGPm$~a~ z<$ohS$Kk!UB0g97Ama0si%p%cd@1n-%D+x5<3Z>pc6*VbRxcTA=^LTfzt~)2sI~QW z#Fr|UxpAfPqlu-hgsm5|`I@2D*4GeUuKY>jE0q6=_)6uU5?|%;ZFVBQTDi<2vOi&) z6WB;w3EM1VbFHD)Hk^N0GRA~$R``u%x z)%*Lz_bPvnSnP?gT?w1}4Yju0pZEdgV~HPBK9~3*9-BuDwYGbQ z_)&-V=}!DD!z3x-_lEM1h~IShPJM_) zM}(b5u=%y2)=tM0|3>*~#L_0hPGawCguz9ZV$+13B>ugj*3MDlx0H+Ry{&u_vDgq{ z=MFaS8fxt*t~D3waakgKPx|h_%F&&C;qGQ zRm6W&{xtCi%B9VJclfRm;t!RJp8ug-Y~!EG#WqBDgk8^J^DjfKU2i4+Sh?7dw3V={ z^yA-#TK&q1|D$|A;{PfiOZ=(wdBmS7Ka==>%5NuLtNeL$7-4?&L*M4Y7{AJGxb#8z z-tee$%9l?GN+@3lFIK)1UZNbow@Z~{C+4_cc5@-yID zD8CWDrSjGAt&}s)@+m_JyR)AyJ|p}f_%_NX!?#tw7~Wg?HSq0}KMEJyCG3tL*nO3w zd-ggtN``Rs<_hr{`8_)Pdt%F(lYlEb8L)a64luvvZem;Cp<@dtb$D}hK*o>3m*tI#o;>TX-RX+bAVXyvhu@&JH;QK4b zPUN#7685?Tet>cr#{-p%PAZgRr}hAc?@hn$fy!kZ(Yu7bkAWYo{ABnc%9&T~LCPP8 zAFBLK_+aJOvV3wx!akz&O65}iP~~DL{FboKa`15lP6)n`{9g@;Xi@bDHokjbol;#;FFZoZ~1(Qg#Bm0k5Vo=$L=NUe-(U+^2gv) zmHz^Mv~tn;F%Iv)6MUL-(fM@cQvR{ZPlxkcLjRlK$0`3VT;_U0|99XgIQ#(7Ib$Z_ zfW6@}m5a`2DUZWr$}fP=RxUbkQ2r9UQTd?7g8t>AN%?+>4=Ty&0oOE_==yh-^* z@cGK`f|DoVz#qXG8^b?@Gsj7&5S_Ou9}G_@r{D7V8VMCk;U_5+lZc zpTHM6e1PcuWaY!*rzjVlFIFz)FHwFYoZk`#JOe*f`CIU14j)(yKTWyloH3Iya14C8 za?wera?$w;<==pxq5Kv2naV$apXKm_dceJ%alJ3|C;h&!Y^0;Df|kDAF?z2 zO68(+#!SK?Qa)uFj-KSRSB9^Ge_i?W@N1R73%}0cgG%AoE8hoxgL2UcV(- z^xIyg{C@b&%6|;MMfsoMw>tdLE#S8)KM;Pq@`>;}lw;TSoyyUTeCA8Sp^w1tR{je( zV=Up&PvG}BeDE&tdzFjM?^8Y+qpF!Jktu?fkCt8FI@z2L7Y9{~TE^3m|0E1v;>U3okF7s@Y!<4Y2T-vEC@Ib$xLY?CmY_R6Q* z41WiX9Y`1vf&WJN_Hf!KVT80{jiJ_vaq!Q;LAeX3NKNPJw{5EGY;};JPB2lA)m-I92<~NcZ6=M?F*5TFkNj|+Np}H@8JLLz$nMWj4*TBg${5UvmHM|wh z_!)iyd`IPsg?y&Zq+^Hjxjw_+fbXIldy!9EOBjWow zjStG_ybZqwK1})j@Zri|f{#!x^&Y1DGx*^Suf=xcQ{NJ5(O;wrLa1fGNVW1Q@KMT} z;n;+P+B4wHm4@E{uT}mOe2nrp;pkC9?FaC&4j&`qj-N{y!#K#Nd<~cWj#u6e=eJ4! zIvk%g{Au_^{mWhEMXivi_9|A8rKAm zDVH*5D}M~$pj>>aQTbYU+~G&=2A`uGeMjah7k$oCeg&L5BpmroIDTdLYw!iiKZY~j z62|WcU#J{=iL@xEjq)jE3FFU$w<>=aj;hL;g=c&rYhLkvx!4tUAff(V z_?60EfnTNkZ}6)fe$>`*`X=G1L*e+O;rL4ATIHgL>y+OCzh3#P@EeqW0Kd`Ulcmg? zln;ilQa%%YvvTwoxkdQ{aK=o+WU+zUl#5T@?(iwQ!>L<>+-Hj1seCs4F6E4QI^t-G8`L`&paDW*^!5oOFQu~ll~w0!wx^X z9R7%M8TUt(x5K}s9R11Xq9q)?8qU~DIQq}$?ch%+KMek)^7-(ml&^$8tz7!} z9p%4+KjZLeTfkQ<7dwAe`3dmnlwh=xgM8;V&p30RO)7=$IQvLA7CVi+s+{^GKUV$`{3i~d zu_K)EkT9bP{+glIjJfciDaW@WKUa>9<}5Dl;by1d`7}-^cTgpBs6RfXABINw&9F;k@KfPCDZd`Rv+`%*yC}yVqq{nM4tot@I92Tf$!}6ElOx%%;oPe3>O=~4-H=mpQ8L`_*CWJ zhaat6#_kx0C-8&lH07czd`3c|5q_-li{LYqOWTf9{yO}4hqp=}Pf(7nMd_P_R%zQz z<)^`CDZd4d9woHC2%oL|@9+kPpR_Zac1SpB6g;kcK75XHnY$QQ2`4=XpQrpCc$351 zm=ENyMI^Khfiu4tPMuL~)bNYp3zgpwZ&5DuLqfUuPOHP)`@v6A&iKpUkw|D4n{QVx zHs7KAdH5pbAHq*|c!%i!6y>7-#mdFTmMA|L&bUhGcnE%~@;Bhi9KJ|w1N}-^v=97r z<=9JfxpL}^b}GLPzCyXQ?F{9AhM(#1lY7I@QZ9NsTe;}%9OW0lX`h6XrETXa|1})n zkZ?)_j-289!Y@>Q6dcGUJY7#MyF9DgwU2XMyN@V~)VI(+d?@XM6rm(i~&NB7ao zm0u0NLiuy>E0zBR&e%v;vLzfj!w-R9qa53heqA~Gi(aezPB^-gu;hpE>y`fteuKl8 z?g_t9Ier=an^D>%;Z*t> zy-WGG;CCy39sUjFpTh5P__A{Ny~;B0GVzOV zI{dU9;SVbx0e?if*z=>xv5_e4lW-dCiaw_NNASm$%UFNg;ivb7KcW0E_>;=#!koG+5Bx>tgW)eJmok5#9KVddteo+TVnY%-UxmM-{6BDXAz{Vt@c&aj4vw6J742}w zz;J9c`V-}=;XhUWC-`d)KSTP9E+m{G-T=f68akj9i1TWU~E8r!{X=hQXa>lTz%;6UxThvYY{&4I@!UYrHJ(MqkqiYEl;9o^s zD94|Qwp7k|7H#G53wyzVgbOR-TPr^XzK!z5aBN+|h1bD*D`!3^+Dct7RS;pNJg!FN-B6MT2& zQsy4YrOZ7YehK<3+DrLBIO8PYlB40+z2QsY`zpT{zMt~v;EaKUOWuR`clf2!PWmk2 z(n0V8m5V+rlrMn~P%iDHZV8vl7#*biPjLDvVI^Z#bck~DizrvZ${FxOm9KygR(>yh zi1Jt9jH`r|AHjz@{4(@fG)(z$IFNAJ3GflhJK={ZmoYkA`HS$8%B7uE4*wdqQ&g>- zGKxkip9`;1eja?Z@^8RvmH!q##^IM^<3&d(7afjOE@M4T`6=)tmEQv&ulzOm1m*Z^ z5%YqCE3k{AiOOkv5q>D)ih1yQ<>$bUQhqypvht_lQtFgw^)@a+J>JWq!m8!q;XEgJdx z@a?U06WF48wrhvh4kL30+Z~0Gei@sc47Kc=iFa=1FQ7<&0KN;)1u%cuPr-NXWA#G6 z=J}C++nXKCa~9<^j5gZ%l!dPCm4GB`cO&B5-`>M|dQZ~LedayauKnj4_(A0B{2vkh z@BO~fsks-okMk(6x!1I>ReJkCxd6g-atG^`C{TjtLy70Ab%lz z@DbA+7Qk*Ldk#FNZo|e?rfS2wMzFU7T+e6ifv+d z5=v#vn+>%}r7sJW&mwM7-bS2IzKpn4`PsxLDZh$XiBPsX8__kPOnl{ZL#;Bg z!R5+DXPwH=BwnHXCStKaLfN<2NFJf=6*gxXYL$r(N}mbl9maE%e?ok&!@G&j&r{Bv zYMrlqA7UAMLbpmb7aD4H8%=zXahR`>UbZ*h2!Es1YcelYQE%7+q5p9ww0 zzojff53$)h4YhiRUEHNyWban~9pY~&|0VG~%0DE&*Wo>PBEC;~1@ZmLWxT|W2|Z=J z#EuC)>)1SGsMS;K>YK`COdeJ)ZIrPg^n96(*ge6#Ybka{*kUU-j~Qxhu@~{<%7+tw zTe+0~gmUrMCzUTDeoFZ{#7`?19Y~)ETZo>YG1S`P@5HMezUB7B&nmAbeopyh;_oUK zKNfo`gs#>=i4gPX;%kG)w>{HklG_c8OpkI}Asi^+~IocsUu}b1XC}?1_(tvZ5$>)pD&I*DqT*4XN zk)sleS{nKDV1FHrB$ymIvtuFRQlPmSIBnd-(JhPQk9!NDpn-x0yoYoJeQd58kZCd* zqAy=QEBdlt2I81Ky7r9+ee%l}*km4ZLof1ICij(C_D}y^;9aAEc^w_C!v+kP&skDM z`=XYLhQz`Ft!;_MMGgG1@BGFE0}iSG;Z9b3z-vHJ)1J9?6_zp{QKEGO++NgL38>|(3isC|sod2Wh**dKCblidE0pMwj9`R?6d&`?B3Gq1D2Wom5ohUz2x^k)M!$Aj~rm{IPr=99W!?I)JaYc z_j-*z6Fj1juy+>O{(X#OA4XbE(jVg>D|MQW^ON@7Cw|rE)3s~QldIB&uz70W;UceH zh>d(l?1CFz&u*S7DfAi*v~%e`eZCOh;mj9bTUOA}W~+fm&E?x<_nm*Kdd8VA`ur)G zT!=S24Lrov|IGPfwPP0x^{($`r;|d5x#Q18x=g!x*0BqD$e<85OASP}tebo!7kQhT z8e(#bFk1;Jo5{{Aq!ctz&_F>01q~E5P|!d@0|gBfG*Hk$K?4O16f{uKz{b{qyz@~A zo3#d}RZo^TzBVirt^jSA{tA0#2c)ALOmL98Yw$cf{KC z)%CoKOc%7}HNIph=9dIMQ{hD12_8h|4f+ohl9814#K;XH;l7ldJd~ClkUN~zr&4mB z^4uTDjYRIYJmjj7yFVr8sW-j6YUG|v$+=l1r|xP}Z@zN*_G1|3KNwK2)NKOwZdP(U zsg{>rAt&$FUz^gOXM9zA5*6RVW&q|{>byl_4RkV#-z7fC;U%cqI+&OEOKiJ@@3qLc zWF(Y~VI%D!l+0#xD5!#;N_m4Fxsq#06C2-^ba^Mf5)evWV>49TLdzRwp1HP4wVd{9})KqSj#9p)QvIj*j-Z3#Q{g14=Me^nCS;>Df`5rBLK1$@sZ*}x% ztrq>+r`yYyujM-@hggH8Y~*H%oW0CGjr5o`$Uas1Y>|=IVY}7E+vdj0Cpq8K$r!La ze#(1J=6gC+RuZddYj11FW3K=a3?m@tb4%{h_IE)mWun|@VI$~&qxChQ;*F~RfHoD9 zzd#KXkDegk1Z!$!s-Hh?N`1}3hLhXnErire!4cJc-Sv&5l3wzyfaZ9c{Gt80a{lhV z?7pH^th}BeDkNqnDjf!S)e+aTITwSjA^2!tc|UH0v62} zwWz7NvAlM1M_bIdfWejHGrI5Tg=CLzYLB(I#}~35C-s*l<55ypZkU<|)b#Jq4hvi9 ziEr^WMq*m5t;Mg8M%82LXs4fs$4xGu)Y{RsuxY7(*>$moNmI(5#kD@x5^we^q(0W( z5Ni%?ql$zzvcA5CBGT>5qbAADrD%}}Rh^`$VSoZA z$Cn27o_%wfx`>3Cy5#3DbrA_QbrJA2b<6Qc$qzC0WOmpHBxU?ZAekFr>XPjka*(N~ zB!aBSNcft%So$a?wCFXd<}pj z^Ws=VBJ9}NT{X-SW_NNr4&?l6`fB3vP&gh-esD8fPleoq2G&;tws@$sb1q+A-zp0G z6*Tab&_Jmc^ZVL695a0B zhUp7$P|_}{w)T*Q*c+RNVib1!qmsd_1U?~PTPyow5*u*nZ`p@Rht6f}@S1Kf$>od!bI*838T z%$I^4M3zum#4o+8nVL(}?v8Tjg}Wyk`YedtyKw$z*zU~ZNiU~uAzLXgB%3U|GdzEt zbLD=KC#2NnNiO_Y2Muh@Ms#X=RsRfzW;-^9udC> zZ9M`$588V2vmdn0%JzBCHcQ6y{AX5f`a_=Xlhmpvzh}xl1^7Nw?v+S?(AJaV^PsIK zUv50hn_8_raOyLLCOf3@CjXc^zg>eK;q_GS{|K+2NX8?)UXiRvc(bx|4WT@w8$5*G zM|iUe4^a?%WA|imxq*)BW(GFc*J)+994;#&p$?lS;P0+RAjnyhnc>3}LwH@&lbSnHgkp>5l2KIJy$PvWh^k8%l1d8;U@P8%lPt8%l1V z8zwV@$2TeAr<7!#uT@KCm>Z_@{q54^`?#T`2fLx<2D+h}8Q5T7H8tjIW8|H?RALxe3%%H9Wx}m&` z>HpHCs!;@j-B5Bv-B1KV+)%QE-B5A^-7uLMJibW@Kcyt|e1}mo!`v{H?{AkT-^UFl zJ=hH;H_#2;%)kcwx}ofr!wp3w)D6=Fg1d_W`n#doJJb!65<$ZxMQ6ETa;HFbuvdT^ z%C5m~D7j&7DEWbIn9L07O12xiCsDGH`a7aW#K#pq`Tox65%F_JkAROudh)YfGArB1 zDYIleO3KRh4y-5J*EM}4GOX1rk?x+J93KbuHfMI_ABB|nF$i%6)ci$IvEOMZx{C$qy$Jt^Zq0?FI} zHA{B5si!1@tjI|Cn!4nKnY!fXG<6ZmVd`ljVU0u#`|y2H-;|KU)RQv7ik1petBA)I zO_t}ZDl@ZVL%J`2!GB?1~ zB|F^IQxZW|WF&k|U2?)qUGj69x(MYk^)!*NMvA!wn7Sz;hp8uJf)yQL>d75L#(+Ho zO%0vQO;O3P4NYy>tZvd+AE}^-?l( z^-^R)dzlmn>SZ$D+eG(3SdI>|M>0D**Y0lrJiGe#&LffCx2$YW&$4p84NDt6>6!l_ ze1=f?VXZeMnjK?kf1TwwCPsZyMei9y^@D*rkPqgUg_Wbh4G{+Z* zWT$(S-|mv{8POmi9nhfc={mMvRHiPm*`j>v# zles(X&pAJcOz8X|0=eb~kqMt4L?U#4Faja-gQ+5Tevs_2`5~DfIzJ?Z0@R(%4l)_Z z&oMuwL;@Ydi1^PBk`+2Xh(Or<;O2*QA!vS(opa9*B9&`?NRtUw19I*f?q}PQpL0M( zC}cn-TNd!3^20R6zQOYYnc=Iq2;`U_L?mo}Nalz1FKm901B}e$>P$U|Oz8X|0=eb~ zkqMt4L?U#4Faja-gQ+5Tevs_2`5~DfYEDU^0Cgv`gG@&9bIcDZkwC{VBL4G(WQEQT zA`mt|xcOmS2$~;c=iKvyNadOz(qux_fcXc`52lP<^FvZ7WI&}3zxg4#YnZ0kH+X)K z0z!`;L?FlfAR=M&Loz?4f9dmsI7o*7XUz|OGM@RtPat!C@RRY*4}KD!`N2;>(vs(V zLHQmB4-!&+1!X74+%G@F!GlFy%k~v<9NaI<zZcpThMK)RoF@SuE;g9izzzJjuoWA2xq;o!j{u4Vg*I1cWY<#BL7 zfpiBC%n$2A(!u?9&USD=sZ0kCkqK3U>=!nmlAm)xMJQxIC0kZ-i8GDgJEsbJuAySSPxqo2e+xU1}bBGc|TY2?ua(p3w z`c`gw;M=$AMIDI)JIr6|_~x5#)lELKy0z6z!2Dy3UwfT5`^v{O$M{DcKY{vK!vZX< zEk~z3?mRi(-kRW#@!E!YhA5TnS$R^)S>u(;%3O+4Io<^*mF`(~Qps)|>J_ORkIGZY z>55LJW}aZDa?)2bZ)$*!d>zBPr_K}J#N=9#O3hfrGBeycJ1xUmuu=)B2_co1?I9U? z?lhEfGg~zyR$$lU9USOhbJ`$xFXq8fH%vX0ldlH3Ba4D{_jo9 z>Px2gyW-i-?`C;?-%ZVM{Zx9I=X(?3J^N*jQPS%@LRlU4_`ECXad|g2!{gob zbcgpO`gg_G-97sS8>8%-+0k@gcbu)dx!HRNkV^J=dn(y=$E3TbvNHWWmE(2zRJz0T z-vU#~ZXN3KsT_~br;^j1K9!p3^{Je6xA&%c2Ev<_rE71tD%+cww4zjMhVN%)xXz!J z;dp;4A?5z5v~2&+$a5!vjCAh|kddiVKt^hIr!psj)DGFRKq}8O4WyDY=7F?~w28o* z8Zc_UQ-OEStWir&241o3E_IlIC;TDoHt@#&|aeNaXu<8>MxQp%?1diO}W7md03gI9rNAvDWF<(+WG@76p8q=FW`{2WUNPTQz$+5G2zcdu z)&Q?SsNTX>0Jj+2G-LsA#RBJlS0ZHkPss$#{vLtcwwH76_mmfAdww&&XXj7_2hRI$ z5rNabD-km5yD~wOzDFRp+Hy|$p7Qc@?tls3Q<9$!Q>Oy;#!StftEQH@mjALLG0m-#o2t z;pmB(r)$nlDerZiU@_1794zK8ipfPfSk!4*xqBNfSkk*(2g^AM&=a{}F}IC5b>msR zg9}Vw!h@wU*YOZZ?@K|s1l@~4!GfuUFjyvgH4K)`Fxg;1Q>eRk=8*F)pgDwf70n@) zoTb9W(r=CjOX6)Ai)yf}XJri*%UE7R1XELim6udoC-5M+)I(CKb+* zXJ0uiA3b}ncOeQE^DIQcV(uiLT!?~2GZ&&@N$)}wEazE>g2mi6s!<0^dKRK!vGj!~ zSSoWN3X$|KM7adrg(z4swGai%WG_U)vKb3eu%LG#$|2`nh;j(~oX)w&!N#yn)w%6o z#L^d{;A+XqScrmUJquB=SjIvWBAB)ioC-53sHEP zNrk7DtK>;q_z6*llxGwN_P@t6CKDFjRLC*pdD3!kc z1PWy?Kf!|D)hCCXyZ8jkrPiK6k?f@>P&8xZ36%3LJmF&Abthal>-;CfKs~EYXi@2l zPM{!8m9gdoih7ouK&gxsCs;0R!O2G`x!#1zdZw&U$*d_TR66UdCu303){k`%&E=fQ zl_s=lIp;mkIulrwXPF6<%2;Ir<OHArHeHF^Ef}D55 zbS3hvF(Ku-vv_iq36afQXF^21D@}-?XRQg5bK9u~9U|&kZ$jkKSDX;B%rz%W)Vu2B zlyujf5Xsca6C#wo_Jjy$tUe)<-t{M!pmznzCGB(SlVR$fRVYVs>FZF4C|MaRQHZc- zEees#SdGFY)7GQ>#F8sg4r$L6mqRvd+R7pCa}ty>P}wU}jv~pHQ>NUmoLrr9l$Xc6 z=~d8|_D?F%(*1oK#-kjlM2A(Y?hB!c;@O|D#yl?l0A>k=aQu1bjJ zwI(HtbnglI8)s51pYo9ZbU61)L~%4cuXPB~JXRs3@>+vLF6Rp56$)Q} zR5p)kOC|H1gj70okUQavDzS&*II*I?v+M` z@*A*qSY@=_{D(K^3Zo?^^;{Eco6Q#n%|qj!f%TjZ1xk6=mq4kk^(9a;bA1UE^sX;~ zVxILSP|9tensA_?XMG8jN?%_Bg)-NdU_tNtl0(j2UjpS)>r0?W_WBYinz6nF%9$eF z^(9=)yS{|W>iQBcl+)NV*O$QJve%bDQP27kD3!6k1k0tZFZl>1*OyRP&vX?knKc20 zO6&R(D&t*W@)6DDoXPbiw5*(_;8|Y+i}I{5fl?XkOQ2l(`VuOX-+=k9FQMh;KfGz{ zOK6E$nZX7`G@ZS=8DuVmmkf>$hPqW4OLoh4*Q2A?MQ3Hr|aUP1p81+Pr_3gDFuIaTlq znnHco0w1~H1;Izy=WM}8D!2WItqc`Y0{ z&MZ<*%DrHOom-^Jk?AjV{xFBu8E*yp8-Wy+dy+Bb^~%6KPvUlF%WNn`Yt@=o%;BAzn|Ux}0P-4=gdyp_5WWht4y}r`>K&trNzD>tZcUbNF`I zfk#%4PQHL%KCWd>B4i=PBIL!z>23pjiCEa257i~+l#@jlUP+)sUK*eS}CXMkP zASCad>+xh>eR22kG%%^lwD$Q6`t>9w8Q2k5#O;T(SjqP!a|}dE(67Y-xyfdxWqaC| zN_QLNd*qdRZEqbdCH*C_+}4sUo$NqxtB}ZLHKthK4kpKAUHj+R&u=`^+Z!zG*E_Ft zVCOtyslNG6C^;<4|H#g_SH8u2B;79gi+cOxE8}*?`f7w?t88L|uj_{gyRNRjEnH;7 zbE_!A47Sc=0VQ^yJY{lCKweiS+f!?&Ppz2Ov9NgqN|pYE?cR$_Q_sRVVPZM+a@8XR zw6(W2WME7ttV{k^38LO@xrAyv_HKl z-qh3D5b8i1T2E(MJzv*2K{`Gu8=eb|#+!SleF>B;M4ndM%%Ja_v zD=;UgK^@U_)1QX0qjc8P=d{NiRo0~$f|94q` za$J+|vhhjGo*!llT~<$9Jmje*%M#~_x2ws8H7F#H>xJ?iHjYID4_0>C)UL_bjXpN# zb3SP{>8L0~he26&>ozy7e?9B=oX`5#vu@A%tbaY5;`Pt)L*vvjt7y&V%&^Ok^*6Yl z@D91h5F1=i=pkMg>MhS>$qnt#m+gAtdiKXXHqQ50e}n7!e8YE-8bu4#)ap3H)8|`h_p-dST;|(4+mXIIc2!%W8PoBs68(vRnMP1gP zFWa&Hdg{$RHqQ50e}n7E^H_gF>uGNIV(z=-xbNR(_O>v*qAsf^-*cq(9G_0+!gV@F z>VkU0G`FF8OYcwE5OrNW&7l*v=X(Nt`(t5iQ*-)poF_kRT}r3U_%w$;Z0{*&L+beg z&Mi%UPFe7|KXbe}p2K#yq-RiE{n^Qo8%)#pT+Jgo>ty zmarR1T~^Qh*3fsbx~!hiD-7m6lMOyUHF|3azi-)P{mJny)-WQW}tIy zDq6#@1#W16r3RbvORbbvii+41C@$+4VwP(7-0AfuJvOS)r@(CZq=2 zvYo&Bi9E-csh`5{uf7Im(S`)2S}x1vRS$w=Z9IS<}D&OcL4>?G>Tt(!M2qMH8piS|v2S+Uni8yyUEImHR)~ zZD^&gkWs7KAD2E@d8TD8i$)iZE&7oy`IRwiYUK*6e*D^++7uu4yQ#BEMpm7%_Fj|L zxpwXNwWGRK&a$jd+q$N5H*4jxvgjR8+H2QL?6k_4l|}A&=GnEMthBAND|TDEdfDka zEgAkc%{ar_w=#0lS(T?-R%NGUt=!eJ#;+ay(egE=hgPkydP@5EwHGdwm~zKbZj~vQ zvfOf4to=aCUA64jhb%es_bV%hKW|yX&o!pE6#entNudvhSsDbF`SazZ4f(CMH zU^~5FQMhiATUP~%Opn-x03L5zGXke=^Pg4pl**F@gn=-C;>v1jot6xjJqr7%;YjdJ4{r#i7 zT2;6LWR2tfQc+z+eDUZ0-cnv;QV`k*8dw->NVG5B2o)EqENI|Mq=AAT5GvYy|yxfy%nHh>fcMF6~L7 zu=UbF(dY?BOmA4w)W}y0=1-f#e?;+2P->RoeUhQ~ta3^&=_SvI@;qhl&X_%GY*FJw z-G<)&;8@H4L+5gIjV)SG+i9LJ<@e3@@s-covnqdQPj&KU|5);_dV&~)UieTZbw;Dd4~5nQ{Ji%dyg#|zlB?lw;jc_m8&d!Y|&sdjw_cJj~cORPP0>H@u*ap9c`3p z`Z1!@>c#%`vR~y{Yge=Hvr;bmjv2A4HKF^CN$tC+m3?<{+Cy7!vMkyXH^2K8?>cB! zQFnA(c^>WB7ej|1K-|wfgG_u)6d!n|cvtDSvDMp)x{r;#a@1JFJ5Qs}E3B%@RS&GC z@B5Aa^vctW%*~{Uo+7X8XMV$JFZx-&c5LL;9V(a0GtUo3SJ0Lf7g;057R`Iq=!W{m zZm;QAazWCTWPh>wu|+d4Gi|=9c-PJ%>;KvN8o)M+EB{%2`Gb%c2oRtoiV=w^iEKy+ zO5!W>S5Sx@i`XTBgIKby#7bmKwPcXNAu=RwN^^Ja9c@$E5(OG+T6#4-=;f}oB$T6R z4}#lLLV>pCqa{$oGqd_|V(rS>wXE2?_Ui4tnR)Ncn~$A&J0H4cQV_F4 zR$30k@?nN7HizNj_h|Va6iB65-tTn~9oc0t7K`b#a2Zht_ZmZSKA?XYrH^!(Q~bNB zKS3RY{~3%wt?S7$R?pID*!Ujxi`VT31;NSa8e)H8v=D#$F5*O3l*f%fVPW?H_q7x2 z-CJV4dkbay%6$obCzVN;G2xe#g|;&&n;X4x`lIXJDB8((DoIUbHkM{2({Y*QEY$dYH+Lc7nHgucm49T8dI>i1BGYK({w_G8!!Z z1!p|}`5by-muC!Q1W;XVleC@J{AAr|Io$mxVX{}ZW9S1@~FO=E4{Li57fs^%&H=jxi~h0{qH zGo=$747t0r9BeYeqXsF3tRALeSV+i~@cN35mt2(2L=hep1L3X$;@)I9TDcQfA`-&c zfxR$;a8(F5H-m812)8+da5V_$$sios0J15AaCHcGPX^&uA>5t}!Z{G`v1GV}%79L` z8_?fsxWAr3xJ>C~6b_9j9oEV6MK~M6eN|4r2?$5e^^(mGgt`0=gqQ@uWQpN|8<&aZ z5q+!RGPUC83fy))--hD8jE6Jr0gl!Mtp75750Iu4m+3kDET`2%q_<+LKwuRrI0N{V zxDmc!F<_-G`n3^+o15ed>$&CXTF=ei zHcrnUq~YN76gPQ7+{aUVb>i`~8b3a42trpo4cFD~317}Ir>*X~+0z|j&qeDzZl4&= z6QbeVtv=6;PX^lEA&=7=Zg2;R8{AtxtD7mlsH(kP5Zv7~Tzfm+^X@couJL(2U7^*r zGcf22dD=pCU7_yY8G;b>&~Vg2<)c`yCs=T8ZC!V_znezH(JM86LFg``;d;VwGIAj% z5cJ`MKJcz3W*Enw6NJJc8qO11>!In_Men2j*tv%JA7)Q*K+o6GaPSXs{gH!If5Pp; z_U$p!LY9!Fls3Y-XgHxy*ap0tEER4OpId1dVkCuY{ate3g!By!PMMwcPRQsioOZCmf_SV< zZjlxUWm1xqq^Bqw@RO6FxX9;9k z-sn4umA6dykk67&B!!Pu$yS;_Nj}m&N$^oT?qGV88L_SN+=`T^zvTRBOvWeZneg2s zr`lfV!49A7d(K(6O7Z(N80yIhA6gc2w&%Lcd0uMtLs)tOfeG!5xF{1`3TH%dA`-tY z`0>6zo5+rcl%o}9fSUFN6a`8#*&yrXhuAIPLlPt|j_&%7>6?qNRN$L&x~ zcG?~1xIKG5IU2X;v<=MF*?jpno=#H=CgNwQD^8E(Fn83MhRV{D`PpxZ z_~hGoI?MbS_jo!>J`pH8K5oxwJgu0|)ak46@$^`wHHm||s)AC7&?SSWecDjdNaqAd zUNlT%1ezv~BniNCnvdJF)EXh<9Jgo9hvr|tjN8>HZ!A4iXxxt3B1 zt~{XWmb%n9r-8Li(p`w;yB#8rB~Rvk6{$Bz$~OkZe54bUu=y(HlVP7oIUh+zs->sh zqih1fScT&R94l|qpUxDQw?v;P)ANe6%%3Em4EwA~`S`orPf(}o&UhbBim=l176I`% zAE_2WJwCbWOUkx8X!_UHNa!BqQZ(x9C`l+n3BCzic&+KqY9I*~{Q?uMyoHL>fgiqn%}pH(TJe6zD=nVw`{U#iENGKN*mXJYdwk)BNFVP%<~ z2-?4K_4O6=kv`)*gXUN=24Pgvk4zt24L%SM=csRx>Ks>0$G$PmxJHSUTAB1!8ZsD9tHLLPwGgWCan{Rz zYf^>JRO}n)YEFeaEZv!;4W+`1y$hwg#>qY_`uS!Q3NvkGQut<4e>k5E`(BFqNGB-K z*H_F(I(t;)BgsfLKKa(y&oX~XP&Qe4%k=w!Vm=x6^_BARwdLmfj?6p;)yi9^G$Ymc zWI9I5GJhiR@&-OqEu7Nyr1Fu{oT_{z7^%m{C)r?QbB6$Sjp)Va_F%|N_kQsDQuiAl z<&V4=>tgKzcH5R>G>VQAKDk=sbIVq_JtD?Z#kg?OaSW?Qzn3aGXLERD#;~fTM`oXj z8h@bI-0U|y-ECXlo5x=?onpzfdfU3BK9H)$XG=hGjht$HBrObV9gyPjDJySXUdeT3 zs-;I}{|LHJ66L_3?yG&K3n{GuXZM(p1~d&!x&~&65@4HmJQ?iRG##h8plSvm(6JT;RK<$8ZpLzhCqeFxh4Ndi0 ze|vKCBpMO9(+gX4rwJMAM$82WJoC&da3h$CPOxj=cH(W776Xr zG(Za9c#ZI{lmedLE@3@RQ7?sWycnJGDuOK-^KdY8nocsT_jYYfoxwKcQ?DgZ(*UV) zcemn5g;5_vrj=T7lHrQ#98LVwiSqGox$wA{nT&Wd?DEq3NCnvR1PE9@m$;?$RJsGaiRhmEfwuU;(^iW$JUo+Vz zxn@Oa{zOvvNR@2$_@rBv#ET*LC?0n(z2#~RsCLd9&$tYKT?m)SnLye(Z=)6J6rEO? zeT=ko-bU-q1t+)rYv;U;){Jr&)oniY@#O3snWUJ%bEnQbDp&S_OxpBJ^e-1CgKv1+ zId3&`S{&Pdoa1(UNFAGw9Ck|X*PE+7$?|PHou(8_#GYhhm8;D8WH_EyoSqEF)5`fs zGD?isl=8{9@pP8y8TWWPOFj`4()hSNqw%z2K2xW!!t|DQmkD0`p)T#5x0Hs^h39ooGJ5`xQb2=B8$o7EDKfmjBmbI_URele6Q^J zOvx+Z03jLH@O1aRSI?_B9uVX|*bwKm3xTCVR!W#N3JtWhcRi4^mZPEeu`u9%Nx zS6@ZFW!Ps`nx1^Kvu2r|L?2voCw}GSEy-tM^C!V4(=!aR)LSH8PcuEkKrtWby`5>h zq2zKRGHZ8oFb``w?vbxX|A6LizU+~&T6$#WN2$RFRZlzTZH&f&Hc8q!Z=)5H$(#yR z^fndy#<`kPp`G)V<#}=$_VqDtQFd-!V05KA!IcDvKMFUKu}W zb4xPQ8x-vDxZ6G5LF;sEH<0|srWU>-XiLqdm$m@W?GIWd_ZrE#5S{`7o9hTWO^0<6 zjHiIWnus~n0Q!!k)9vlbA((E!_Wxpf#5W_`cu{E*kBAbhlh(Pw(=97WGLj42Z z7h)r8BgEWSsC(e?L$MDAuu1vd^J545Zoi;+WozVrY1l|7FaglDc@7GIu?|gN@w(?0rR_Qn+#i?S-4zZM5C_(EC|D@21ZV@>s8JJ8LTr zpsg5UZN!my~BDW@sy1dwzLF8 zZuZk18fR4b;3cAHXVN2eo!y>I-Z1Y#p44lkuTK^@B`tz{Gbs!6+X;Cx{Xd0MO8qjQ?iR?_g5_B^g)?Gut40+q!A+LW7 zGhiZUBC__NX+YC}rU6X@ng%otXd2KoplLwUfTjUW1DXaj4djmoX6274nk18>frh5l zb!V*Z3VFJ_JRx&kIN)f4udq#q42O*GKyZu1Aj(O>(Cu&U zX$u9*x3q69zs!1BIo9|E%iRHQ`4rTQfsi70M5Vd0C(F5;SK+F1I_nAQmdR~Zc0vcO zpgF!&m0tzfSExWEYZ8;7|Ey)5poo*I{|QQyMs^$;Fx1vxy`gQZx82$8-?F|5ry`AR zlXAWWV9f-r0l;bpl5G8u%H`jRay^+!#I+5xBW}UvFtk5hw0!@Ng9yhW+etCjLKQj; zTkGh$0_NT)+-N%_xNNTo&D^i`XVh=7A8z;!3c?2J&%z?iHUn`E?lpF;JWL84hSvQo zEDZy{KJw1&Aw1KtG|V94M-bOxX!|hE&tbTv-_|eK0UsT7BOd2bn<7I&pPo2}=9#`q z>3~NS)Zd%?7>6A4Sgf;%qE7F=f!zImU_w?=_wHTZ%g=5 zd@{WThr#_yoW70Hh5RSdb{JOtAQ3-F+aS`O&;3kKlp#;Bp2LFlH9HJtXM+a^hx%#jza@B|}N(6^t8Oz6k z?Z)cL=q4YhX{=7t40VGhOGjk{Yh7A*J83-Ij@UuOJw)ju?yAaYz%RyKm5kdHK->$t zJdkxSA&|wx-1Cfcm%0qa(5-D38^NLqVZ{&cNkhe&t%?foEtF^T{9_&VO`N8S`NhWVTLR= zhvDM)X!#!$NTpcb?{yFz*<~;mi|Mm)8BquK8bfhDpnn;qk93(+{JW_?K^=tu8H_)z z>&Y@!&(dkw_#X9(*X;)d!O7?vVt-+@5P$nF;zU@K$BjQ>VfO*|wG->zTVlO?3uXGs zeF=Uil}VQ|;g^(!wlgT38@+M*qwC!$+R1h*Nn5OUEf1!ljdp1xt7m8fn$y!>_GTh} zlD61hv@|6AAUDm|r2cPun8}THg133ErfKt9ic)Ea@o7Flw>Nb%8Z7?>XFUJ;T2x_| zZN3m;>&vkIG_k%cu@?2C=*rhnH``FY^t5~#zm2*u!{&+|LGdX-Ex;RZ^h*!LbsX1( zt_aIS4y3{8R%RilZ^nkY4b(E?VQ*pc`pR(MB(E97S)px#e(^;pY zVJ6?SoWJrYZRhhhWtmB)_#KiNoBRz`n7^V;s$|84u+{J8W=}9=Zt}Rh+d58|aF7`N zGeM~$nKJE!Y&ghJQa_rlkZ64msRE+;Db|2MYbd88SCnWGNNyq@#Z@gWSYW*pE(g^0a^m5y9W4f-~1!y zI!`d*@51>x=GxZH(?v0`@UE!t@%q}4`W{~>xI+8vI9;-S%yb$_;aY!}+}d}2LxXd? zy9w3DHo0{6Xy@4zjnZ@}8e3FbB)|;K4Zs{;m1S0Uf28#h@ zUbUy0aar9xQl!W=RqMpF46}P@+d-Z2kvW%F4L<9d*40QRMg>n7XsA(EX#U8F!}xTB zIH+$6z8-Ya813hS60p9n-%wn$Vytu%y7 z3}$Wfxr0H`F$R#!pE070iL&164UFj_B9-!?<&BR8mf6;%Z=fz@YjLFnQNhmz8aX?| zvZ20OPUMs`ETJ5^&|~{IIO|u9?UTf+!e@O=ZIa@|Jh?!&vHBXb9Q6k0`uKUfS?kSN z?{AgkqtxF80y%w0wRT8~TC;ra8^inBV z)Z-)F5mb+lwAxkVBgsfLKG@SrDu2}Q<9#A&N=nmH0`iPcpiQy@DdiJM;UiVD)#D@G zlLQ~d;|``bnGxGM&#jUfDCbWjGd@Ajgqu&WZHn{&NirpwdAblRSAEG~hfnrB=d4?$ z_4!pbL<95Y-G8|7U<|94)R4+XNNk*#i z$+z)zmiaU8@pP7aA}I5$Tw~lWRkD@lPm<468&69&8pxKaV5f8#Io}O6jk5M>8YVFU zO;Z-+_)PP0dzM-wWSryntohLV%a?Jx8s&|pX9|tm*L%B`Ty75AmZkqjcW!c3clz4c z*t#WdO9HX<;iPy^GccPkS2%ZC=lYs zBm}uoYP{zt53AH9EcmOD9>ynE-%Na7$xj)orAOx5jB5GgZ{0%mm+n%75A3WRiyI{u zOI$G3zL_-CUSFS1r*V8rS>Y;*dyE4%jIYhep3nH^du7jOeDl4s=Q9=U#?r6CWZ#%- zZH$qw>SGGUe54bU=o2aCBiYqgQEwUcS(TqX1|0)_HE~kr$^?inA|)oIe#k1RI{_vG>xMVPD{e#Mj9!LD`)o4 z_FlBkN71ObAObcS8IGi49rB)_=l}!4^s_@C6Z>$QR z4En~Z@R^Ez<6O;`?zP{r0 zWZ2hN&PS4wYU#2E4x1e%vVkl=o8V zBhMe7Uy9KvI!gHDU~I|KbIVq_Jt9(7kk#{a9K)*7@1@Glmocnr>57o5Zrng+pO$!7iXU;TrczlwI6Xi_@BBDnw)~-=jR_Wvr zrRfoq13sZ{Z`bCuO@eCWAmrU7)xlQEhqZ5of_9z*jvl?pc;q?#e2Z}I_ngOhJ{wEs z{PlI$VfZ}z2=)Fzf9@lL0pqhXiY~I;{1QdKz|n{W$BdnN!T9Xk26+CSy5FUqAlX43 z4>8YSN~K?4Ku86`6Jker`a6t@Z>JI8@7J4*!*h&BAVLLV6B0#S0|*QodE7!mI#}E= ziyLNf@!MR8=nsL_@QuPnmRn{~e#1kcdCLV9d4M7p0%^JBDmWolr4g8=)bC~Lw+8fw zX}T#oUd}Z*jWw{)`16-zv5zde()%`$PoY%&d8b}yxg}-^pM8<>`PcRHzmCH5{BtNb z&%dmn??br>&!CvM=?}aMDP|rVM@vMEjXFMt+7qVb?kKG_G$5@YM?Xdj>`@yEY`CUh zUu-;T2W?u`pMRNHkkI_7}F3&ah{_Rty^-jAp6_UpG;!jD?Q2Q1<5Tf#rIgb!K5 zPg=r1wuFCb2|r^A4_m^&u!NttgnwlTzibKr))Icz68;^Fg7C*I;Xhcyf3k%CYze<> z3BPX%{~wEvDELQQLHpn(krIcxDQLX{>9Z9F#+ohb+DNKRZk18@dvfZ?!$oBD} zqUE*$o*F$Vdh^yH+nt2;>GffU;fO%}ZEn(R>nF}Nv6{N%bM-6DF4I-^J7NzpTqG7- z6RR$=xd@2}WRGn=8R#q2Kk$7aHnKKC%zcHr2Od8Z`(OZ@l;1r+cChdE3wl@L?O1gO zDYNN5wZnEhA+`u119J&k6RUl1`$)kR_I@&h;@8AJ*GVDhIzZRX=z>;)Zh!2!NH^N| z>m|K+zB*uA`6MALcQX1r%0Lr*`fN{ZwJ|<>h3j~FwcqKjfq8bCe#52r{guNnMVhRg zZ2{^KuhX90d48&Y93O|L{fwMW3TUe$zH#Cn*}#^TV94D)fpfRyg*xpwQ!G7yz{y*B zmhO}D2)zR+TwW>T2yxmwviwxd;M4v>A~-`zD6R{8+Im9nR^Rxo#yCEL>4u!!S-ZB$ z+3nfn4f76Ut*5nTb86R-6?7VyN8oyuk2Veko{B+v`8pLP*JPd=8aP|5k15r1Y9vfc zg{FaVYCx+4<5ZXyT+@K2fhnf}&Gwnd(!?u4H?v{gHS|;lZ<{;h^^ai&Oax6t)*dts zXd2KoplLwUfTjUW1DXaj4QLwBG@xlf(}1Rd{L#Ry{1HWyWO6jn(6qYljMZHsPj{Cm zWUdPbeE#m#b&eTHrCET;YOG_Ysxpd%CttSB$v;=b8!4fb?d+x(s7fo^|$Pg^KhzNLL@ z`DND2%CSj8u-qN+mQO(y7zin1M^u^{d$OF{OV7e!`_7?mncNCjC({a=<4aZfRgis! z3N*4NF$wz5TGk1QIH~%dpfqV@$Dsj3ZT;07+O~Szv18Dd^-Va}a&((yFIxVMIJsfE zCJF0z=0x12vQq!WyNj0ZvfJ+?QFf!0jw7q zV(W$W#okS<7fP-}8d~`ytP{dIq&2a9e=M8F)(I_IKT>cT_?1L%H=bkD6T`q<<2ma` zOz)@QN+^!5CF;Ky_ipl}!w@KnU`>^{{%HgFIw@be_8{5%A(hL&73B#tm5Aw@9dQdT zhoSx9qUHOC97H%4*-nbF7OK!;*jh(vGWR~=M%y95WqUp#6d4No^u#$d&-7JF2Ry16k9|V(_TLmNW%9U%ddPQO-~M-I4;~V3=o@)w zHZiO`FU|w9)KlI_Pl3pPTf&dxlj$`$4DMIr^lg+b<)W4nx`5pw*1Ea8EFL(RXG$3~NqL&>1a<5prCgfgD#P;wR-WayeSRobY?x z^c2#Lv{l|jah1`V-H_ucTg-F>cw#$947U9~ufuTtw-a%q#2DFYdJ5&`R}_cxNg}Pg zoBQVK!?ws(Bep0J9EN2q9|yJ@t1F|Me4M7SI!QCs4Vo++l@XNl*4<7T&$c6W5OEJt zx`?}~G8*uUaaSed_5=|3LM{(v-Af2$@i6y1(UaV||aE35kr#SYyw*E$e9vJGkOM}3ZdCx*0NvgU7J*v7)_qjem5(mittbK@EE zLOUvg%m1K2D#h}CuY>5wE`zaHOrM3zh&s5}7>e@&{mUqQ zq|2P*-%b4q>LC2jVEk!aPnNNImQKUQ_o!dIZa*jpPDa-d`wOFm_}h08C&HpUZu|)g zyAQapomlVQ66@VtDAQN&OYl3XOuCE-zoaa*ok7{$=#A4KUGGNGPPS7?+G4$Hc`yxa zv`ZUVJwqGNoSycwHxu!bw8i$Kr6J)5xoN&8^?%dDOm4Ijyv=(xO`F$JluAR4PxAq~ zy{VJYVEHdNrHA4=j*Bf8oXi!2NQ2R>%tB1xj16@gsExqRlU_0o4af(j`aKoLOY$kp zZ3wNuo?CtA8#kSGDjH_;P0RT!kJ5HNe^Zv3WQyM*nX$>=P=)y`+N4TWOvrh3(Y(3Y zgN>DO=48O%g{`N|Y~ehYEHV0LPTgaK(3|#mHXLLqsUOW&NVL9(Q~}Za6l*}BHI!44 ztILXcX#_M4P$3u_u{>lT_9)>buF0XSFJG%q}5QZM(p1~d&!x(4EF83gt&HrfgMVxYI?=p8%h>DL61)Bydi zfo;BH0Qh!EgcS`<^*QSoNlK=9PNoLp<-(3*5qP;^2ii9E;Czyj{Y2oXk!AUW|&5Qx{-F&JN9xI`w_(rd~^# zrUAlt`##|#=5*2vEW9hKd%V7Oe8TPVg@P-z&yLe6`%g-%krb}=cgd}N*EcjcWlkzS ziSoD+cEn(36}L%8UDwmq+|<<8?G0qP$SlrB=4?`&S)J+eqhksj0rna!2E{!qaB;zAH+$6z8-Ya813hS60p9nChn6=!7FcFmlfHqv zkgdf{aGjV37s%3`iYJD8k}BoX&`@8U#Ekt^?`;*^S~HY0b{IKqgR_2>To_gOtgoq+ z0ckZQ^O39)gAL(XYJwDQESCNk-Bh~m|Pb;bXQNxe-iKHnhO-~8P zGd_Vf$qJ;DPb7tpRLNG4kMye!d=!s6nBHVYZ0kI?N@k#(KaI@z1U;K238~8|~iMpI3VXI%7gBtx1%E5|n$+F6m z7Ddw6CHgAdpFpeSNk$4@Hf26-~9i{&ei)ol3T`T)m_6sZAfN;*A%V!8g3= zK5lnnpPj6(agN)w=aZvxdrsRxcehNBGpj5a6^XSO`8J+TQwrE%dzADiJ6L+!q(&Xg zrpU+biup))&l2Ms#eAfPpDOZ^WTYCOd>c<^nLpzmPiM&|f@03fHOB2yC0l9!B>7CW z@$|{#f<-XLrtTseVT?zj6lseB7R;)(9ErxIJq=H2?Bt+^$A> zW9gYfpiWLQk_wl5NC@pUh5Rdc8s4tmi{Ymo4RbNuJ-9gi@Y#tTUTenAIwhAGXi6~}Ud8N#CNRvZy6bSKR5`tVPHQsZShgE747W~yn595=oZzevkOk*tZ)^@J;nhW#@A+K z&u4t|y|U*szWH9+^O=fvW9e66vTsbaHpWO-^)ZEFKGF$F^obPnk?cyUsJ9IJtV+|9 zZ+6x!)060fXF8@(%x7ZrCy|~^=V4`;o(SF_P0ytMus*~%=2R%-Bb}f`Ute*0GVJRs=Of8Twe;j$Uq4H|CHlsh_VpF>$*`}l zl#j11H{W+;<}s*N-a4fjsm3SMF;bTK6G2-zt}&7de55p|RBx&2k;)?V`1m9nY@E^< z@N|cI@wpwx9Mt$b172ThKW>zN%6lpGk>`(3axugxI!gHDU~I|KbIVq_Jt9(7kk#{a z9K)*7@1@Glmocnr>57o5Zrng+pO$!7i zXU;TrczlwI6Xi_@BBDnw)~-=jR_WvrrRfoq13sZ{Z`bCuO@eCWAmrU7)xlQEhqZ5o zf_9z*Hha9tc;q?#e2Z}I_ngOhJ{wEs{PlI$VfZ}z2=)Fzf9@lL0pqhXiY~I;{1QdK zz|n{W$BdnN!T9Xk26+CSy5FUqAlX434>8YSN~K?4Ku86`6Jker`a6t@Z>JI8@7J4* z!*h&BAVLLV6B0#S0|*QodE7!mI#}E=iyLNf@!MR8=nsL_@QuPnmRn{~e#1kcdCLV9 zd4M7p0%^JBDmWolr4g8=)bC~Lw+8fwX}T#oUd}Z*jWw{)`16-zv5zde()%`$PoY%& zd8b}yxg}-^pM8<>`PcRHzmCH5{BtNb&%dmn??br>&!CvM=?}aMDP|rVM@vMEjXFMt z+7qVb?kKG_G$5@YM?Xdj>`@yEY`CUhUu-;T2W?u`pMRNHkkI_7}F3&ah{_Rty^ z-jAp6_UpG;!jD?Q2Q1<5Tf#rIgb!K5Pg=r1wuFCb2|r^A4_m^&u!NttgnwlTzibKr z))Icz68;^Fg7C*I;Xhcyf3k%CYze<>3BPX%{~wEvDELQQL?8(6s;*peq&P$-w?C~+v2nU(d`dfCHqZNOcy4a9=c~bcG1@^O#>+!K;Myc zy1iW~sOk5J`f+ZT-`wW!>hiROy#B7B8T&JO-K{=P(VMpp+3qByPp=O<3`c|_+sB7& zZqjV)C(bpon!4n3^()OT(^d96Vh=G~BodRvNl4@ zeTBLQ9zPWOU;vwx-#tHeu8Ng?PuK-bRbf>wfVf9$wOH`@2>CB1jPI$&G*Bq1wzGWt8pKoflWY)@>p zF+O{R>!x3(Ybici1M}<@XSlfeTO7KFuW=H0d3AMNyEi0)$ZW%c@L!q#f`$m-X}C-F zJ$&dF99KJHSWsv7b@+p!^0l7OEs-KJw3hEAH*}kmh8yp_wu!yOmzP(T^Kttpii?UW zgCWC$>VZ#eg5`cic{%LxZqD;Mzptm$6D)5E^_0{7CcSNLR;4e#yrQhZO=jS{5JP7L z5%gc6^5QxNw>c^QYGPP$joEs+^)kwj%p&^eS*P2HP_X-~TF=eiHqYYDu2A=~`1nDs>s5x#}P&;5)aCHee6Xy*Wx^T{7@eW2EFW$J+h6N5Yx%wpjC+KgL*|1=h z3-n5GyA1OpcNs4JG}rgV{}vgq?r3Pu?3B8u$&${z0L#eB@(w>O0IgnMs~@E^g`U4FbjP<`EbbK|WfX+Z2WRDVi=VS*MBy&KB!q zO0_&yMAzihG@z;mv^t=wIyGXN1~d(vf*R0lpNULMyb^RX8`fQuGD=Q_NIWEhCNSEA zrU6X@ng%otXd2KoplLwUfTjUW1DXaj4QLwBG>|_Uz^s;8^kB^zdX}XpXx-vIX%*QG ztDWYC^*7*?FD^dPpOrre&?K204Ky^Zt~+CO7tSE=@`TKF;egNIow_g1O8t>u?tzACm>2$s78-tsA^0s|pM z>|jfCV^5a5Inom}*lu;GTPC-{HO;hw=J--oeidY2p#qJpNlb$NvzB#&B2KFQCn!xC z*>PyVP+NcXhPJKVcI-2>WqlJ)!yMfv*^8EcBTjCZu4KZhpE(gXsjSq0@$RDKyX^Km zNa4y6tTb{HyN#|``q2AXJnyE@4)R#9Z97|KbO5W2hS(~jeX(~FtBjJXmxfmU2&;>* zdTC8;-yh57vDHP3){hk227V=x+l}Ygu;yZ5uJN4pBc}ILa3vH+S0MG@i+eYD(qRY` zMXJbban;lY@O4tYSQS8$tshdk{993;FjI+`uGtZ{;Bpw+A1+$Hf5<_EW0CEo7%Qj> z9fqxSlqPfU6K=E}5?r=dgl6v7`ZMY`*bg`S1_faQ^=DxbW}AUH2lpDgRvsn=4nymH z7M6yAUmtmA_7I+FSQ=&!@gs=qFtmLb=jSk7(r@b*?0}CBx)G1_s7;ZfpifVnL-R~u zrF6ifit*SdG;jY+!BQrVTd0S8*Y)jxXZGMB;fB7EcV-jA%JbqpAWJ>vjr0_V{I@0i zC_b59gTvr{B~IT)=|cV!X*&!nevpWtq-_vs&*y%oC(4i~$V&^-*X%Hqoef&ek?p1@ z7`^B_vmJ&trzhx)mcs}+uFpV@D-!XOau~TBtzS;~J#KmmX-C>BZ=$%$=*@1(@surQ zIs!bg9V7@_`w^71Q+L-{0;*4@p0bM;|c*1(^#FP8R`a2mX68@%6aQ)HjbUey*1OBw!?wM!0D_)jpd|KumhL+DWnfDs!MhryPObTLXQx?&3AeIj^WU)C6 z7r#f#|DZrB#qxfy0}G{g8H~kZ`Yc>V)WN;RP@E6wUql zFGSe81*=vQ@0P?0){mkqUqjt&L;1qWux4WXHtNC*n=5vNuCv3SfPS|a{ni6YNAdJ3 z;JzmX;Jz9CwzLF8ZuZli<+vjwHt7S+Oz%Z~Ae|z?Ms=j2FQZ$Ty_&wK8|pSti-n!a zhsB{BsPs$o@>CqXsg!)x$Y`Sn)l_*(64F_xqM7KF%!)}7T4_5!DMG8rRPj4RMOh|= zRN&3~rFgq%ee{+pXU*c=+?W(NFS*gh*&@PsXbA$5$g2sVX<(u>02?9jqat0|doe#Wl-LsS`p389eiO&(!`T7H<^L1|n2Z*n%=U;rc z>HJG@TR#B)1JhXqx4?QHrt>Q!rn8nNi~}dSK@(SGUh3{Vz=(3D^wQzm7kZ>BT##jy zIBZu?8M=1r&#bKY;jnmZtws9~c_#{OgC_YRwIM zjZ1!OfVTbE3OxpZFSSHi(a=<{jl=Uh&vGGMyj#xx^sNx;!}S)KJZ=htZj5(qV%EV@+W=F(LUB&y@An~(JrNYXnEsf zfn~Ne=^LmE*~;V5tW|fpK#}g|tUBQn2vR-`4fWM0pigqvdt1e})(qvG&;p3ZnCz8TPs${FjNBUI(Q@P9@t|uHI4k)TWPB z@y3hG;2Yj_AGhNJ=XkZs!;f>^o;{x&joWkD2D-atdYswiNop+CX5`y=I!!5HgY8t| z$9QJxX_Fdt;N2Y`w=3o&-91x}kM!_UMLv>@RO6Fx zR+>LaK2vQxJw`*3fTKCrWPEN+xsEOEh9`)1NmdwqR6oyPGgWreFK z?lBJ7Fupb;dp_fv@0C5D@y++jp3hXY8%w_mlYL{VwJ}Dzs*foY^N~(aqEDolk7QR; zMZIO%XH}Y>e6zD=nVv)+Jkv3SVm=d_KZ*2YIu9$$^hEIf7*}6kF(02dnEetG*|(iD zo*tR6Vsi7W90uAJFF+j}K@;cE1&X!7OeN2$RF3U6#O zZ;jdL}p!kwczyrMSs9JhtwgsRIB~cFi>Au=$x{%TukhTRXO%LrS zGQH($ZdxEHIdi6|!{d`woG5QP5D`6cv38BJvPvh1C{2%;9PkNsd%HHLZ4y)~2O;k! zsSdVMKCFE!6vjAZG@jCF1BHu>N1oHqw+QEc&v~4u+E_B@udllf!{^yYsP_l@a~~ND z7@wU{bdlxemnixLjz%mvX6)1p#%JF)!1MRi{Vw$c$qwpxh;)|2hiK^UtB&JpZzOz7ORlJcDB1ra$m5 zq?mbd94!$sHtP5oYEPJ!yQ8$$(15gp9Q_zAut#kuu;H41eX;SV9kgj#fBt1cjv0@# za{v76guH;Ts1QeOEW(X6LL-aNE+0W>JbI(?Xq489W0dJ2{dwC|dVdKLIfDC`sr0_% zfFJ1Q-+#gUPoKFE-q=@uPjTsehsoN~oda`AckY>4x-(i_x^u9&^!`FY3{f_-^uA%E z=$HcvFAz5*+e2$yct4)L+pphZ2|sEHAFzbKZwdd<58-GYX~CdQ^aULrHx3C5S|!KC=?l^+?SteuHnr5ayIcKTE!Y;P1&D5c&??z)I;B&5 zg7D3C#6(ea&vfjfuU(o3QZ#_RBk6Q|yHZfo?-BLm+%CVl&EM7KX$yJ%T|qPUXY{&T zeV(E>ZymDTNl2ewA9fgy2t~G!582$L+15{-YhpEZ$>-`EZ^*ylPa1YHN{+8JHYO3>|(9T(|F`+mKo z_s&-bY%8B6WaUmqe@7W;f={3AiLExqXRmPG^viTD#V2cEo}J@X8|BB*lNK2%3cXQudeb^ScYQz>Lg2S-vL+UrM-B?{2 z-Q?pmjnzq-p>EKm=|E&sg7{7v&$c6W5OEKQaaUDF1AZ~?s$|@r0ODRq!!Wdl1HmKPkmi13 zw?)4bL)tG{^S3W-V`26INA;w8<`U+{GvtMSw#OX0;qz?UY5O^(?}se?yGfp@Gr8Qrarg`#e!N&4QZ9rNj$EH?v{gHS}x;Z=0K%&k`XL4~d`&jP{^u zK+}Mx0Zjv%1~d(58qhSLX+YC}rU6X@ng%ot70D5d-~A(Sv>Ei&kpifFP)1Y5y%0oRT^SzmG;HnO{`T)u7Mg_`6H}R z!WyVGv3-9mo5$A)tRE@34g5+Xw;RvFx(36*T;nKu(gh@ zXP|3TMz8;XU+d4PAJ&RAQ$M!O<&fZFaj`CDn}Im#`ht~*Nddy{XJP4D82I&(cV-Xa zIlcygu6+^Lrm*!I6wlY)2zJ0n2i=Irc}%r+H_g+0-OcREX}<0zve!5_Vj#L^QV>I% zvIuL5;_I@^SOa8p7%qN~mj6M4REq1&?sX6y*<~;mi|dLktj^K;vDX-iukXRSp37*R z!+IifihnotC#Zw)KVyu#UC-7R88*I`TDKn*1Sg|wi2a4pLj3K!h!bJ44s7F3SlE5Q zeeJ~cZf}X}-QL1lvMcu`_?@&C%w)l!&OhX&( z(nhwvjjq*8PkY&$iTFv{;yS&ShJ+v2e^*>~hvBA&ncRss@6|MIUQ1Cb4KY5=2k7>u zPDX>}zu=7LKVJtc?6S=lBJACQwXcbHOJW`DN70q9;a$*%@};Nc%lK`)KW5lmu_Gux z1qQtD#4U7^Z$2G=Gq7I+h7E}zDR@K{FV>Hw%bM_)yb8EG6_xbN?5U+C7;>|p?kvYE znej;*YGzs=<^kyx2{x(&4t*Kj%Iw_q{oYWwfm%B3T=EhQRQjcPc`ATWVBI- zYN|XX3F)j;(Y$m@X2qlkt+bt=6rojQs`wqEqAZg_s&G=c=0DS7LeB53UE5?9_Z2WV zdx9Yx-Wl+BVGl1Rf5L{rT(n2RdYI_wY&ghJQa__QTv7q2uAkQTn7VnJ7bz2H4ds%^ z)#bd9R}(_hz(i?)uBO!`4JFcSt{xcWZ=GWAv#X&8C$kBSwiAs?^mK8|URcvuTer}> zu(qnX3c$I#rg`CXNjyzw!g!cA(S?#bBVQ%VQ)W+8ISK2G!Kb21ed#93xcb zC>`WOe^rGGGLI6Q?N};9*G~PJWfngi7O!nwTh&l!4{&x;)+Pkf%N0q|@ZlG%&#$prZltRX^%|k6m??en~_ZKq#&Y zd)j(JbmQi%5|UzBvvUIKeLak)cUDry>?V)KJAGNDaB4)-EO=8RAG1xxbpLuNRAsBV zsS%Iuqs+dHm`8sanHtH{QX!{-QMQw1hxa`?>xF3Pm0!jENVIereU6&W+Hk;h*7{?B zBY-d9K8Sk=?m^rXBFy3v+%s?&;x?GhTJ$&EAK-o$ZUXo=ZqQhC9QW&{vo5y*KU?>u z4~KCd#(hxtrFZP`v+KU}ftkbihdKP)+weExF2a2}?u&6>in|>56}TI4ci_GQ_g8dZ z`rmqfztabrJ8?MbZzoacdgVP-9ntO5UCUlpGy9bPi6QQ zmzf!nrVtwZFF9{ zuBrKx*R9`h{S7?*>HV`xf4~={vqDu%e~nnU(Z=mDomGLm9QWlB(^<=KFU7s+W!%rj z!^P`D+>LhZ#j)<~{36sZ{+tr$g?OA4+)Kc{qWjWEr*XfJToh7%@i1}T4AQd%iqRs8 z7qXBwnSQ~z1B;hT7wvUa5VD8-Es43uY$jwkc~ZpK zX&9_D*h3B`!|ZjSG!2l!6EHrqLmC6hd&p2~m^nCRirk%w0p)h!QY6G{+6BzV$S(ZB zyCA}MCN^Dk_kmAG$oVu=@jO&UgxNw=j5$cPsg@s}bcFai7l^O*?2 zN-ioB3%7_YLdd1;kA^5C5oqpXEFB1UdI=4OG~wBBU-DVEjy{XDbRw-X#6E!o6ta}m zrbM)y`8|} zJ`%+}hK`z(Ndc^dRFTY@K57D z@W4GQqkw}C+|%?V@UP{zt)3Nzrlzz^ZR8!|kyZ@;w=_l(B%D zEPXCox*WLWgXCL_3HjE-0X&0l;K z`j~0)(+;=~n=A)hChA`FbOhlZ0j|(wc_IKfh}*?WOh&(@rbT~_GJ1>tJiy`q92DJV z4v*VK_mJoo!@YT&yWbSu$3*{Q6&(MD2!Aj7|4#J(T?zMpJs?8D;cH^J*F?Uris4_a zp#Ce!E5kh8E22CjBiud0{7s8~E4qIh;rQQ(;eTW1?q54OeEFaVT^znNz~PG`{zWm| zuSPijR}l`MkBYE@!(R@GPz?XvaqfO@fWx1=IDA$N_pBK1i0D2dx}Q1D!#y(~!U_(b z9%gXy#Ro-5IJDR~yoiTiTpH!>`Nu`b<1aQxxO<*RXP%k6=Zg3XMf`;#{$l~|f5AZ! zium&fxcfX2e;(oPIW7**73rK?!QJOXMOeb&*#Qn`i{WOA;m#W1_%n}-(9Yo*V*HX3 z?k-_?(ISO%KT8ZZQ}mx1;o)YQIXwNK2wfs19L|V}(9Yp$2Sq5#Q!M%yiSiVQ_(CyU zp_7L%BpenDiO?m&5)MrR92&)V#(?NHb7&YAVMK%#9O_3zD8|=|c%6vX0gpPZS)?** z*iH^K?JZI#t7O74LKkU`&qvmJFg`D=>XX)kfhC(>d2NxJs2vvK7C3E=l=3RCS)`Qf z@KmOtS)@+o+|y*&G@wueqD6{R(JWF^)*`i_euHU2Wz@8wz6`ewx5KocVFvD|Q5LBM z4Pn!Q6(tFa)PnkUggszdP-{k5&}hKt(~K`}k)rgdMT$XKq_`XOC>|Cm?j91|VmMf& z7#bHgIVp^HOUq?j8PDGtSOut+gCEK&@@BE?|^hpXIq!y?7put;%8ID|!txnYsw zu!2Kaq?r4h5)NUJVs2QZI26OdBE{UWNO5TA5Ed!shDC})h8HbT&<8A13=fMGhY=B) zIfO-u;ZJvokZ=f#6vNN3a|nwRbDt*41B(=M!y?6@h=)arxeJ{lBpenDiO?m&5)MrR z9Ks^S;=v-tp_xO&um~d}tl$t9DHcvI#@CB@Sfm&p7O4fO*8-a##imBlTFxHu6Nh5W zw^K-89cT>ra7byi0^F$GLmsFo!pZ@ooxm_umh2c)OFsZ6dvGqW?C+@q5i2-Yeq2BEmf` zj{nj@5ms>c#Q_e#AVSO)VshUt()pJNcmMN<2*r4xC)_#2hIOBOJma#oU-9#ULzF48kJCp^HOUq?j8PDGtSO zut+gCEK&@@BE?|^hp z>0t(`MT&-7d@U);24Rt6aJCq3wipf;DTaqdibFexXNd7( zkz)7~h8HbT&;u+|3=fMGhY=B)IfO-u;ZJvokZ=f#6vNN3a|nwRbDt*41B(=M!y?6@ zh%Xew6*@T{bEKGm!H@`DA}rz1G{B)zj0cMp3vV=YXc!h@M1&O_>PJK<#@CB@Sfp5d zSfrMqW@~e##?ic`2A%G6q_DkD!+LrueM`N&Yjcl#v!}(=MR)zF3I;vhq2NirIHhE> zCv;r{!2y@JFUP$E_fkTMnBzpKG4zDL{9XT^TpK?P*T0_vbEGtjRBDTrpU;{_DnIij zS5j#fsa$C~Kl#i_TBPEu_B4Z3erDCgNF`dNOiL;GH2X*`-4!-1{mh_g>8>c?9^4V! z0k}JGQ|K}+eHwmGe-$_UpS~OSZrrjn{XH5J{|YPxG%+Bj{6GS4Y)gS-+}upx;^@Oe(U-` za|iC-xbdfV;P(;SkKuj}H|Xj|?<2*+&~S_fg&EwsK`~qzAU3GLbwV$i81lCtt?eU4 z)Ze{8={`#7_leT>k)r!(ohtiCEq$iUv~(Evv$%g=Vp{q}FaDeo=Y@Ej6x>U|y`tMQ^EB=^(?ucW7!Q-=jwj6`HA)?sJEpruDsxSx zgp83dlUXTl?eWyrfS!q-*Q@q4i&Xv>FU=yA|9Lc#5^5HyiPUcX)7H+5%>O)^PzguP zkxFBcTIREvmTm1YE%UhmH{iD8u7JA?H-%=?GW!7T-MDw*-hsOhcNljFcLcW&H)z-c zxZ6$3>J9+jjnUC)i`25KV3~qNia}VU7}P9Msoz}b^U3>~8#as%lib(bB)PA-?pnao6BpMNG@C!R^3pzY%xyiB_Swn_G`gwo1jVP}D#EoDyeJ zEK+On4%tIGgCTxgeByO=&t3S&LGiu>iaj)Ax8*Y_zH2mID1*BUc<*TZCWJ2$>FbXr z{f0rmIKt_t;sqxmdWr}ARJ>t0Nk0{T#sye`xTrG_1m9HrpRjxLjJrESn<0NH-tYnF z{Ki?25~Kg)LwJUK^qTnweyo9R&nBXO`akfDE&b@V_IZ4uSk3X@uL8o#&}8OofCi3# zcpIL}IlhU~vv7Ru5{+IZ|+J%KLvoKOsia848?$H`4S*fDeuiZwkX*0zA#H6gs9|aL?%s z`TV2ttFMJy)X#rkhvzl8ajo8-)KAyBK-4odX*dih>gTZ|cy18!PXmhj`KLeNd4q^Q z4k+r!bv2%^7xDiG$n~TDhj;OO1IMr34aoIlXr_7c_Z&a-Q9v%I;m)7oSuBq`UrESm zIBNj-Ej;|`oq$|U!|kY1l<|ely3<`V}CT({SCBc)o?>?^+8e=Hta3 zc&7Trb=L@fL^;3IhUZ?~xMqF^KU_}T0?0=GO0n2lzAz!DPS_8`-x!({ei@Lymc#d)W6=py#r*QZy@nyS&T=8Atxtzj~3wQx; zT!o*fKfE0N3Az-BV0RNyE{+EC=I^8}WcgKHCLwNdnr<>KUI1=!8D^TxlWOP>C57~sr zx#wG7gg82me|Qn_2RS~1dP+(={U^4QMKgdmt?~3;M92k@Cw`qi3s3R-4!y;%%709$ z?f$`m3wp z_{cAS7xS^J6?oK1dYz6if|%Yf_X2-8$Nv>_&3c*BxnmCSOE|vpzW|qU{O5iIJez$% zgwvq^q8%K6eFyMY@bKpAkzXSHHnc^$WehKzUkrIZ&clb%=IgHH;b)u&_yWhjUI)C5 zho1*NrgIp+*owA9x15JBbOVazq@fFV)PH&v(Dsw(SMj{B1CO#ouR;pN{5mH9y!d`U zXEX3W<$V9`8^He^4NXYJ=b@iD9Df63(|v-Wg^RC&Ji{FSKO)~g$f#Sz(4_c1z)&R@MxpyRY=P_ z*PH3US-@Y%(B#}U=#$qMjl@FU7ohBBkOouNqq z@+B_M|2_=-c8)Kmcu}5PX!(qA{0#b@7v=dR&6j?TzwFtBJf`peGw?_Qy{=x9l;>>d zQn!=ii~j|X=cDnme+M3N)9dtd{P27?-UFF+gA7e(p`9c?mM_NJW5DzGpm6ynljrkJ8$AR`ul~uXf}!Oixm86UxsHyd|#yC3w8k?e=nrq3(@8$-bX3; zO4PLk-vT`IOE{a>>p0&ep3Hg;0pfg9@P&^8ALpBbFX{n4{vJ%h7rYKUZ^w+?=cBCh zex0yl9qJ>09~sX@-PhClfGn~9E9Bwt1>-}11pZcr*Dbmi;dwbRy!$5bw{d(BaaZ$p z&+y)NfbZk@%Yhf)M}}u!0)89Alb4WQ@qJ`?6=h1#%bRY+bD%HsrTt00Sl$YLhIssa zWc-33_z0(S`n7<({u=kA?a_NRenY z_kwXr8}PK;;3~nh_+Bs^M%mVXmZATDQ|2D$W0lA8=l6TgGsdVeSq4KGWEnE!5>}J_ z{aj|oHA*gRly-J84OxX=vXiWpBo-r=UNod!y0|PVW?Lz;nYB}k#mkOew{mGvYY^@G zJm2s0`}zKH&hd7B&pGG&{eHgZc?b_?Z+b6yy~%%dqw!zwfkWIBg)uk zbIJ4Z++FS*pF0a}FZY7i{|mUSo6LWz5+1=NL=cnQf z*{9dHG2EULtMM_7`r+A6;Y)q&)F7ro+>`U+#NJw+3>w`}k6*)=``D?ILofMh_WcF^ zhVZ)9&${lEc5rlM?RZNXxnI1kh42-YI^i#K8O#0R6+F&(rO%zJ`{^}5S3d!N)92v> z_*r#r_ON%iSop!`;<|pW4t$mHqvUU(>(V`NG=qgt@LScjH~Ay@TRwMN@f_8)mkM*& z2ycg9RoC8HeCfU|yyJGBuevU@y3o3vc!R%JUH3Quw|>Fu+WMxt9(EnR&c|-oy~I)8 z_sr$V@bx}-(w?SQT`xHX-yrj|663n=XZ`%H@F@8b=(+)WaNm>XG#yK?x*l!UeP8Bh z6Cc&JH+ww%1K~NFV!AdvV6N2zR_M~m`<^-31kOCn9ZEh2x}I?euvz%1WX7uNHsqeW zMR;EGn6Awk?q%2dg>GygTGe$!8?UXx^PA#l(Y06nEu7k6EyMa%^;^;kPCTr&ze=P0 z(iiX#eeB$EiC*4A%p#`?wFsQOV(x@4w5sbDYXf%pJXDL?qPjK}o#8uu z?oPNB*Y!Qb&MguCJbMmwot6SeGnhM-TBN!*x7q?e5k9>vrfVO6wK+E! zzRTxMuT*-~bxsl-ZCh*lGL6PP)8^-HA3I$iqE}rH`U?J;@SJA!nx8|Shkq_S&H7Ju zy#n95#0GQ!aGq9mox(F+^4Rj3vuRY_}YKP6%Eq{1nd#blv1Q{O`gq9A+%vk6!&^ z#s`E~RPub)b?1rjfB4)A=IL;Jin&+OX>ecLZ)ca^Sj%~k zMs=M|?zsQ-v0IrP*LAw}=Rx5&=>uI)D}tjjYlVYoG`_P~%jGbk8RlUtZ+lo!*rHVs&cs20N8Pqj|paCHQ}Q?9{dK z6kU6BJHr1fyjK>z=53bE$&)@e>Ew2x>q)ENr+gkRXhEMZ_jto?@Y6ECHP5MXUU;-N z{EYAp_(XMWF8>dHR(Qt<=aT$4yPk!ABm9A6=4;&heh>dvcvhpBuDvm6(LLw$P-~tU zoHv_0$F+LHj_;+>xX&FAzu@Cg#sqrRwXOZ`Mdh45$j?Vv)BVopPT@23>YrhEz{wTN zbW5jIUFWxlUsAr0vFdt~_0MIWJB9c#&~;5>;$D&Y-IwAA(X}}}3eK4^lgqgRUDw?T z|3UbG1m^2|_*b@`+w(JjaJ{&$d$)yO6~2PM58lI1Oo5|YJO3T>B+&JwDvYlQU*C!6 ztFEIH;n#icR9j$wCGo9aAAZB&BkovDEGCuaUlGr%&%qF*Z8)w z=ei~HH_eLcdNgz0pM-C(9oKaN8Zth1Lb>G(G`OJc){{3tNJ{YYyF}6y|)aWTEThuP#et8d>$@X zNUw1pPzvX{n7J{KR`;`?+&6WUkD=H1@Uy4kBSp$X~&JQu#C8PvlSVp3vClZ3CZs3B%Zr>bmL?I6A?+YWOEsPv*ey z5ngjzyq@HdBc_q?;XEg(Cl{x}8w(%BIH)Hj_raS8&s)y(b=?Iv-_m^U+-dV#>q!B* zZ^#48%iln&?^i2dfRige|D62MdU9Vbyt(j#1bVF}y;;lLtDHOvzUvo13U49&cg1k^ z+wY%-|6KUwb@ZB7ONfPOseCW%s@}I9g;R4eZ_2TFJxRBDXzv5~%iC$So^;^uFs+qe zjMtM4d}hce%v&%aUQZI&!10~sMKfviyMFXIxYYw_ne~s<6Ymu?Z8C%}C!d3Q^5HUg zrtspW^jc5ur)HRT%6G*1Ggsj4l^>_qda{*0m=4O%(yLzf+4_K1F|YW0TCFF;UW4B+ ze3kWs){~U4;hlu9CeBsP%RRC!4DT#_19c^M|9=NxnJ&WLC2m1I>0|Zy3*nn9WA()R zfG8O5xpwe|(Fv6Vf5N#Oc~6Me zTMO^0{K=RqaVUeL!QFSN1w> zuBVa{h@wt|2~{LizOtji>(_?90kWmmM0g**FBj8e@u7^X9>>xg5A%rlpkiS>&`D@ zZ1aFeq?Xg_x-;;l8DX)L&;TE5d?OY1w mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.18 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + + */ + +#include // For LMGetCurApName() +#include // For smSystemScript +#include // For ConvertFromPStringToUnicode() + +#include +#include // For va_list support + +#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above +#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below + +#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform + +// *************************************************************************** +// Constants + +static const TSetBooleanOption kReusePortOption = + { sizeof(TSetBooleanOption), INET_IP, IP_REUSEPORT, 0, true }; + +// IP_RCVDSTADDR gives error #-3151 (kOTBadOptionErr) +static const TSetBooleanOption kRcvDestAddrOption = + { sizeof(TSetBooleanOption), INET_IP, IP_REUSEPORT, 0, true }; + +static const TIPAddMulticastOption kAddLinkMulticastOption = + { sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 224, 0, 0,251 }, { 0,0,0,0 } }; + +static const TIPAddMulticastOption kAddAdminMulticastOption = + { sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 239,255,255,251 }, { 0,0,0,0 } }; + +// Bind endpoint to port number. Don't specify any specific IP address -- +// we want to receive unicasts on all interfaces, as well as multicasts. +typedef struct { OTAddressType fAddressType; mDNSIPPort fPort; mDNSv4Addr fHost; UInt8 fUnused[8]; } mDNSInetAddress; +//static const mDNSInetAddress mDNSPortInetAddress = { AF_INET, { 0,0 }, { 0,0,0,0 } }; // For testing legacy client support +#define MulticastDNSPortAsNumber 5353 +static const mDNSInetAddress mDNSPortInetAddress = { AF_INET, { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF }, { 0,0,0,0 } }; +static const TBind mDNSbindReq = { sizeof(mDNSPortInetAddress), sizeof(mDNSPortInetAddress), (UInt8*)&mDNSPortInetAddress, 0 }; + +static const TNetbuf zeroTNetbuf = { 0 }; + +// *************************************************************************** +// Functions + +#if MDNS_DEBUGMSGS +mDNSexport void debugf_(const char *format, ...) + { + unsigned char buffer[256]; + va_list ptr; + va_start(ptr,format); + buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); + va_end(ptr); +#if __ONLYSYSTEMTASK__ + buffer[1+buffer[0]] = 0; + fprintf(stderr, "%s\n", buffer+1); + fflush(stderr); +#else + DebugStr(buffer); +#endif + } +#endif + +mDNSexport void LogMsg(const char *format, ...) + { + unsigned char buffer[256]; + va_list ptr; + va_start(ptr,format); + buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); + va_end(ptr); +#if __ONLYSYSTEMTASK__ + buffer[1+buffer[0]] = 0; + fprintf(stderr, "%s\n", buffer+1); + fflush(stderr); +#else + DebugStr(buffer); +#endif + } + +mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, + mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort) + { + // Note: If we did multi-homing, we'd have to use the InterfaceID parameter to specify from which interface to send this response + #pragma unused(InterfaceID, srcPort) + + InetAddress InetDest; + TUnitData senddata; + + InetDest.fAddressType = AF_INET; + InetDest.fPort = dstPort.NotAnInteger; + InetDest.fHost = dst->ip.v4.NotAnInteger; + + senddata.addr .maxlen = sizeof(InetDest); + senddata.addr .len = sizeof(InetDest); + senddata.addr .buf = (UInt8*)&InetDest; + senddata.opt = zeroTNetbuf; + senddata.udata.maxlen = (UInt32)((UInt8*)end - (UInt8*)msg); + senddata.udata.len = (UInt32)((UInt8*)end - (UInt8*)msg); + senddata.udata.buf = (UInt8*)msg; + + return(OTSndUData(m->p->ep, &senddata)); + } + +mDNSlocal OSStatus readpacket(mDNS *m) + { + mDNSAddr senderaddr, destaddr; + mDNSInterfaceID interface; + mDNSIPPort senderport; + InetAddress sender; + char options[512]; + DNSMessage packet; + TUnitData recvdata; + OTFlags flags = 0; + OSStatus err; + + recvdata.addr .maxlen = sizeof(sender); + recvdata.addr .len = 0; + recvdata.addr .buf = (UInt8*)&sender; + recvdata.opt .maxlen = sizeof(options); + recvdata.opt .len = 0; + recvdata.opt .buf = (UInt8*)&options; + recvdata.udata.maxlen = sizeof(packet); + recvdata.udata.len = 0; + recvdata.udata.buf = (UInt8*)&packet; + + err = OTRcvUData(m->p->ep, &recvdata, &flags); + if (err && err != kOTNoDataErr) debugf("OTRcvUData error %d", err); + + if (err) return(err); + + senderaddr.type = mDNSAddrType_IPv4; + senderaddr.ip.v4.NotAnInteger = sender.fHost; + senderport.NotAnInteger = sender.fPort; + destaddr.type = mDNSAddrType_IPv4; + destaddr.ip.v4 = AllDNSLinkGroup; // For now, until I work out how to get the dest address, assume it was sent to AllDNSLinkGroup + interface = m->HostInterfaces->InterfaceID; + + if (recvdata.opt.len) debugf("readpacket: got some option data at %X, len %d", options, recvdata.opt.len); + + if (flags & T_MORE) debugf("ERROR: OTRcvUData() buffer too small (T_MORE set)"); + else if (recvdata.addr.len < sizeof(InetAddress)) debugf("ERROR: recvdata.addr.len (%d) too short", recvdata.addr.len); + else if (recvdata.udata.len < sizeof(DNSMessageHeader)) debugf("ERROR: recvdata.udata.len (%d) too short", recvdata.udata.len); + else mDNSCoreReceive(m, &packet, recvdata.udata.buf + recvdata.udata.len, &senderaddr, senderport, &destaddr, MulticastDNSPort, interface, 255); + + return(err); + } + + +mDNSlocal void mDNSOptionManagement(mDNS *const m) + { + OSStatus err; + + // Make sure the length in the TNetbuf agrees with the length in the TOptionHeader + m->p->optReq.opt.len = m->p->optBlock.h.len; + err = OTOptionManagement(m->p->ep, &m->p->optReq, NULL); + if (err) debugf("OTOptionManagement failed %d", err); + } + +mDNSlocal void mDNSinitComplete(mDNS *const m, mStatus result) + { + m->mDNSPlatformStatus = result; + mDNSCoreInitComplete(m, mStatus_NoError); + } + +mDNSlocal pascal void mDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) + { + mDNS *const m = (mDNS *const)contextPtr; + if (!m) debugf("mDNSNotifier FATAL ERROR! No context"); + switch (code) + { + case T_OPENCOMPLETE: + { + OSStatus err; + InetInterfaceInfo interfaceinfo; + if (result) { debugf("T_OPENCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; } + //debugf("T_OPENCOMPLETE"); + m->p->ep = (EndpointRef)cookie; + //debugf("OTInetGetInterfaceInfo"); + // (In future may want to loop over all interfaces instead of just using kDefaultInetInterface) + err = OTInetGetInterfaceInfo(&interfaceinfo, kDefaultInetInterface); + if (err) { debugf("OTInetGetInterfaceInfo failed %d", err); mDNSinitComplete(m, err); return; } + + // Make our basic standard host resource records (address, PTR, etc.) + m->p->interface.ip.type = mDNSAddrType_IPv4; + m->p->interface.ip.ip.v4.NotAnInteger = interfaceinfo.fAddress; + m->p->interface.Advertise = m->AdvertiseLocalAddresses; + m->p->interface.InterfaceID = (mDNSInterfaceID)&m->p->interface; + mDNS_RegisterInterface(m, &m->p->interface); + } + + case T_OPTMGMTCOMPLETE: + if (result) { debugf("T_OPTMGMTCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; } + //debugf("T_OPTMGMTCOMPLETE"); + switch (++m->p->mOTstate) + { + case mOT_ReusePort: m->p->optBlock.b = kReusePortOption; mDNSOptionManagement(m); break; + case mOT_RcvDestAddr: m->p->optBlock.b = kRcvDestAddrOption; mDNSOptionManagement(m); break; + case mOT_LLScope: m->p->optBlock.m = kAddLinkMulticastOption; mDNSOptionManagement(m); break; + case mOT_AdminScope: m->p->optBlock.m = kAddAdminMulticastOption; mDNSOptionManagement(m); break; + case mOT_Bind: OTBind(m->p->ep, (TBind*)&mDNSbindReq, NULL); break; + } + break; + + case T_BINDCOMPLETE: + if (result) { debugf("T_BINDCOMPLETE failed %d", result); return; } + if (m->p->mOTstate != mOT_Bind) { debugf("T_BINDCOMPLETE in wrong mDNS state %d", m->p->mOTstate); return; } + m->p->mOTstate++; + //debugf("T_BINDCOMPLETE"); + mDNSinitComplete(m, mStatus_NoError); + break; + + case T_DATA: + //debugf("T_DATA"); + while (readpacket(m) == kOTNoError) continue; // Read packets until we run out + break; + + case kOTProviderWillClose: + case kOTProviderIsClosed: // Machine is going to sleep, shutting down, or reconfiguring IP + if (m->p->ep) { OTCloseProvider(m->p->ep); m->p->ep = NULL; } + break; // Do we need to do anything? + + default: debugf("mDNSNotifier: Unexpected OTEventCode %X", code); + break; + } + } + +#if __ONLYSYSTEMTASK__ + +static Boolean ONLYSYSTEMTASKevent; +static void *ONLYSYSTEMTASKcontextPtr; +static OTEventCode ONLYSYSTEMTASKcode; +static OTResult ONLYSYSTEMTASKresult; +static void *ONLYSYSTEMTASKcookie; + +mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) + { + ONLYSYSTEMTASKcontextPtr = contextPtr; + ONLYSYSTEMTASKcode = code; + ONLYSYSTEMTASKresult = result; + ONLYSYSTEMTASKcookie = cookie; + } + +#else + +mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) + { + mDNS *const m = (mDNS *const)contextPtr; + if (!m) debugf("mDNSNotifier FATAL ERROR! No context"); + + // Increment m->p->nesting to indicate to mDNSPlatformLock that there's no need + // to call OTEnterNotifier() (because we're already in OTNotifier context) + if (m->p->nesting) DebugStr("\pCallmDNSNotifier ERROR! OTEnterNotifier is supposed to suppress notifier callbacks"); + m->p->nesting++; + mDNSNotifier(contextPtr, code, result, cookie); + m->p->nesting--; + ScheduleNextTimerCallback(m); + } + +#endif + +mDNSlocal OSStatus mDNSOpenEndpoint(const mDNS *const m) + { + OSStatus err; + TEndpointInfo endpointinfo; + // m->optReq is pre-set to point to the shared m->optBlock + // m->optBlock is filled in by each OTOptionManagement call + m->p->optReq.opt.maxlen = sizeof(m->p->optBlock); + m->p->optReq.opt.len = sizeof(m->p->optBlock); + m->p->optReq.opt.buf = (UInt8*)&m->p->optBlock; + m->p->optReq.flags = T_NEGOTIATE; + + // Open an endpoint and start answering queries + //printf("Opening endpoint now...\n"); + m->p->ep = NULL; + m->p->mOTstate = mOT_Start; +// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp(RxICMP=1)"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // works +// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp(RxICMP)"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // -3151 bad option +// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp,ip(RxICMP=1)"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // -3151 +// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp,ip"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // works +// err = OTAsyncOpenEndpoint(OTCreateConfiguration("udp,rawip"), 0, &endpointinfo, CallmDNSNotifier, (void*)m); // -3221 invalid arg + err = OTAsyncOpenEndpoint(OTCreateConfiguration(kUDPName), 0, &endpointinfo, NewOTNotifyUPP(CallmDNSNotifier), (void*)m); + if (err) { debugf("ERROR: OTAsyncOpenEndpoint(UDP) failed with error <%d>", err); return(err); } + + return(kOTNoError); + } + +// Define these here because they're not in older versions of OpenTransport.h +enum + { + xOTStackIsLoading = 0x27000001, /* Sent before Open Transport attempts to load the TCP/IP protocol stack.*/ + xOTStackWasLoaded = 0x27000002, /* Sent after the TCP/IP stack has been successfully loaded.*/ + xOTStackIsUnloading = 0x27000003 /* Sent before Open Transport unloads the TCP/IP stack.*/ + }; + +static mDNS *ClientNotifierContext; + +mDNSlocal pascal void ClientNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) + { + mDNS *const m = ClientNotifierContext; + + #pragma unused(contextPtr) // Usually zero (except one in the 'xOTStackIsLoading' case) + #pragma unused(cookie) // Usually 'ipv4' (except for kOTPortNetworkChange) + #pragma unused(result) // Usually zero + + switch (code) + { + case xOTStackIsLoading: break; + case xOTStackWasLoaded: m->mDNSPlatformStatus = mStatus_Waiting; m->p->mOTstate = mOT_Reset; break; + case xOTStackIsUnloading: break; + case kOTPortNetworkChange: break; + default: debugf("ClientNotifier unknown code %X, %X, %d", contextPtr, code, result); break; + } + } + +#if TARGET_API_MAC_CARBON + +mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel) + { + CFStringRef cfs = CSCopyMachineName(); + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); + } + +#else + +mDNSlocal OSStatus ConvertStringHandleToUTF8(const StringHandle machineName, UInt8 *const utf8, ByteCount maxlen) + { + OSStatus status; + TextEncoding utf8TextEncoding, SystemTextEncoding; + UnicodeMapping theMapping; + TextToUnicodeInfo textToUnicodeInfo; + ByteCount unicodelen = 0; + + if (maxlen > 255) maxlen = 255; // Can't put more than 255 in a Pascal String + + utf8TextEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kUnicodeUTF8Format); + UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &SystemTextEncoding); + theMapping.unicodeEncoding = utf8TextEncoding; + theMapping.otherEncoding = SystemTextEncoding; + theMapping.mappingVersion = kUnicodeUseLatestMapping; + status = CreateTextToUnicodeInfo(&theMapping, &textToUnicodeInfo); + if (status == noErr) + { + status = ConvertFromPStringToUnicode(textToUnicodeInfo, *machineName, maxlen, &unicodelen, (UniCharArrayPtr)&(utf8[1])); + DisposeTextToUnicodeInfo(&textToUnicodeInfo); + } + utf8[0] = (UInt8)unicodelen; + return(status); + } + +mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel) + { + StringHandle machineName = GetString(-16413); // Get machine name set in file sharing + if (machineName) + { + char machineNameState = HGetState((Handle)machineName); + HLock((Handle)machineName); + ConvertStringHandleToUTF8(machineName, namelabel->c, MAX_DOMAIN_LABEL); + HSetState((Handle)machineName, machineNameState); + } + } + +#endif + +static pascal void mDNSTimerTask(void *arg) + { +#if __ONLYSYSTEMTASK__ +#pragma unused(arg) + ONLYSYSTEMTASKevent = true; +#else + mDNS *const m = (mDNS *const)arg; + // Increment m->p->nesting to indicate to mDNSPlatformLock that there's no need + // to call OTEnterNotifier() (because we're already in OTNotifier context) + if (m->p->nesting) DebugStr("\pmDNSTimerTask ERROR! OTEnterNotifier is supposed to suppress timer callbacks too"); + m->p->nesting++; + mDNS_Execute(m); + m->p->nesting--; + ScheduleNextTimerCallback(m); +#endif + } + +#if TEST_SLEEP +long sleep, wake, mode; +#endif + +mDNSexport mStatus mDNSPlatformInit(mDNS *const m) + { + OSStatus err; + + // Set up the nice label + m->nicelabel.c[0] = 0; + GetUserSpecifiedComputerName(&m->nicelabel); +// m->nicelabel = *(domainlabel*)"\pStu"; // For conflict testing + if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); + + // Set up the RFC 1034-compliant label + m->hostlabel.c[0] = 0; + ConvertUTF8PstringToRFC1034HostLabel(m->nicelabel.c, &m->hostlabel); + if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh"); + + mDNS_GenerateFQDN(m); + + ClientNotifierContext = m; + +#if !TARGET_API_MAC_CARBON + err = OTRegisterAsClient(LMGetCurApName(), NewOTNotifyUPP(ClientNotifier)); + if (err) debugf("OTRegisterAsClient failed %d", err); +#endif + + err = mDNSOpenEndpoint(m); + if (err) { debugf("mDNSOpenEndpoint failed %d", err); return(err); } + + m->p->OTTimerTask = OTCreateTimerTask(NewOTProcessUPP(mDNSTimerTask), m); + m->p->nesting = 0; + +#if TEST_SLEEP + sleep = TickCount() + 600; + wake = TickCount() + 1200; + mode = 0; +#endif + + return(err); + } + +extern void mDNSPlatformClose (mDNS *const m) + { + if (m->p->OTTimerTask) { OTDestroyTimerTask(m->p->OTTimerTask); m->p->OTTimerTask = 0; } + if (m->p->ep) { OTCloseProvider (m->p->ep); m->p->ep = NULL; } + CloseOpenTransport(); + } + +extern void mDNSPlatformIdle(mDNS *const m); +mDNSexport void mDNSPlatformIdle(mDNS *const m) + { +#if __ONLYSYSTEMTASK__ + while (ONLYSYSTEMTASKcontextPtr) + { + void *contextPtr = ONLYSYSTEMTASKcontextPtr; + ONLYSYSTEMTASKcontextPtr = NULL; + mDNSNotifier(contextPtr, ONLYSYSTEMTASKcode, ONLYSYSTEMTASKresult, ONLYSYSTEMTASKcookie); + } + if (ONLYSYSTEMTASKevent) + { + ONLYSYSTEMTASKevent = false; + mDNS_Execute(m); + } +#endif + + if (m->p->mOTstate == mOT_Reset) + { + printf("\n"); + printf("******************************************************************************\n"); + printf("\n"); + printf("Reopening endpoint\n"); + mDNSOpenEndpoint(m); + m->ResourceRecords = NULL; + } + +#if TEST_SLEEP + switch (mode) + { + case 0: if ((long)TickCount() - sleep >= 0) { mDNSCoreMachineSleep(m, 1); mode++; } + break; + case 1: if ((long)TickCount() - wake >= 0) { mDNSCoreMachineSleep(m, 0); mode++; } + break; + } +#endif + + } + +mDNSexport void mDNSPlatformLock(const mDNS *const m) + { + if (!m) { DebugStr("\pmDNSPlatformLock m NULL!"); return; } + if (!m->p) { DebugStr("\pmDNSPlatformLock m->p NULL!"); return; } + if (!m->p->ep) { DebugStr("\pmDNSPlatformLock m->p->ep NULL!"); return; } + + // If we try to call OTEnterNotifier and fail because we're already running at + // Notifier context, then make sure we don't do the matching OTLeaveNotifier() on exit. + if (m->p->nesting || OTEnterNotifier(m->p->ep) == false) m->p->nesting++; + } + +mDNSlocal void ScheduleNextTimerCallback(const mDNS *const m) + { + SInt32 interval; + interval = m->NextScheduledEvent - mDNSPlatformTimeNow(); + if (interval < 0) interval = 0; + else if (interval > 0x7FFFFFFF / 1000) interval = 0x7FFFFFFF / mDNSPlatformOneSecond; + else interval = interval * 1000 / mDNSPlatformOneSecond; + //debugf("mDNSPlatformScheduleTask Interval %d", interval); + OTScheduleTimerTask(m->p->OTTimerTask, (OTTimeout)interval); + } + +mDNSexport void mDNSPlatformUnlock(const mDNS *const m) + { + if (!m) { DebugStr("\pmDNSPlatformUnlock m NULL!"); return; } + if (!m->p) { DebugStr("\pmDNSPlatformUnlock m->p NULL!"); return; } + if (!m->p->ep) { DebugStr("\pmDNSPlatformUnlock m->p->ep NULL!"); return; } + if (m->p->nesting) m->p->nesting--; + else + { + ScheduleNextTimerCallback(m); + OTLeaveNotifier(m->p->ep); + } + } + +mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { OTStrCopy((char*)dst, (char*)src); } +mDNSexport UInt32 mDNSPlatformStrLen (const void *src) { return(OTStrLength((char*)src)); } +mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, UInt32 len) { OTMemcpy(dst, src, len); } +mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, UInt32 len) { return(OTMemcmp(dst, src, len)); } +mDNSexport void mDNSPlatformMemZero( void *dst, UInt32 len) { OTMemzero(dst, len); } +mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(OTAllocMem(len)); } +mDNSexport void mDNSPlatformMemFree (void *mem) { OTFreeMem(mem); } +mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) { *timenow = mDNSPlatformTimeNow(); return(mStatus_NoError); } +mDNSexport SInt32 mDNSPlatformTimeNow() { return((SInt32)TickCount()); } +mDNSexport SInt32 mDNSPlatformOneSecond = 60; diff --git a/mDNSMacOS9/mDNSMacOS9.h b/mDNSMacOS9/mDNSMacOS9.h new file mode 100755 index 0000000..d707f9f --- /dev/null +++ b/mDNSMacOS9/mDNSMacOS9.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSMacOS9.h,v $ +Revision 1.8 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + + */ + +// *************************************************************************** +// Classic Mac (Open Transport) structures + +#include +#include +#include + +typedef enum + { + mOT_Reset = 0, + mOT_Start, + mOT_ReusePort, + mOT_RcvDestAddr, + mOT_LLScope, + mOT_AdminScope, + mOT_Bind, + mOT_Ready + } mOT_State; + +typedef struct { TOptionHeader h; mDNSv4Addr multicastGroupAddress; mDNSv4Addr InterfaceAddress; } TIPAddMulticastOption; +typedef struct { TOptionHeader h; UInt32 flag; } TSetBooleanOption; + +// TOptionBlock is a union of various types. +// What they all have in common is that they all start with a TOptionHeader. +typedef union { TOptionHeader h; TIPAddMulticastOption m; TSetBooleanOption b; } TOptionBlock; + +struct mDNS_PlatformSupport_struct + { + EndpointRef ep; + UInt32 mOTstate; // mOT_State enum + TOptionBlock optBlock; + TOptMgmt optReq; + long OTTimerTask; + UInt32 nesting; + NetworkInterfaceInfo interface; + }; diff --git a/mDNSMacOS9/mDNSPrefixCarbon.h b/mDNSMacOS9/mDNSPrefixCarbon.h new file mode 100644 index 0000000..95f1a48 --- /dev/null +++ b/mDNSMacOS9/mDNSPrefixCarbon.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSPrefixCarbon.h,v $ +Revision 1.5 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + + */ + +// Global options for the Mac OS Test Responder target. +// Symbols defined here are available within all source files, like symbols +// defined globally for a project using "-d SYMBOL=VALUE" in Unix Makefiles + +// For normal DeferredTask time execution, set __ONLYSYSTEMTASK__ to 0 +// For easier debugging, set __ONLYSYSTEMTASK__ to 1, and OT Notifier executions +// will be deferred until SystemTask time + +#define TARGET_API_MAC_CARBON 1 +#define OTCARBONAPPLICATION 1 + +#define __ONLYSYSTEMTASK__ 1 +#define MDNS_DEBUGMSGS 0 diff --git a/mDNSMacOS9/mDNSPrefixCarbonDebug.h b/mDNSMacOS9/mDNSPrefixCarbonDebug.h new file mode 100644 index 0000000..2c31a97 --- /dev/null +++ b/mDNSMacOS9/mDNSPrefixCarbonDebug.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSPrefixCarbonDebug.h,v $ +Revision 1.5 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + + */ + +// Global options for the Mac OS Test Responder target. +// Symbols defined here are available within all source files, like symbols +// defined globally for a project using "-d SYMBOL=VALUE" in Unix Makefiles + +// For normal DeferredTask time execution, set __ONLYSYSTEMTASK__ to 0 +// For easier debugging, set __ONLYSYSTEMTASK__ to 1, and OT Notifier executions +// will be deferred until SystemTask time + +#define TARGET_API_MAC_CARBON 1 +#define OTCARBONAPPLICATION 1 + +#define __ONLYSYSTEMTASK__ 1 +#define MDNS_DEBUGMSGS 1 diff --git a/mDNSMacOS9/mDNSPrefixClassic.h b/mDNSMacOS9/mDNSPrefixClassic.h new file mode 100644 index 0000000..d275361 --- /dev/null +++ b/mDNSMacOS9/mDNSPrefixClassic.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSPrefixClassic.h,v $ +Revision 1.5 2003/08/12 19:56:24 cheshire +Update to APSL 2.0 + + */ + +// Global options for the Mac OS Test Responder target. +// Symbols defined here are available within all source files, like symbols +// defined globally for a project using "-d SYMBOL=VALUE" in Unix Makefiles + +// For normal DeferredTask time execution, set __ONLYSYSTEMTASK__ to 0 +// For easier debugging, set __ONLYSYSTEMTASK__ to 1, and OT Notifier executions +// will be deferred until SystemTask time + +#define __ONLYSYSTEMTASK__ 1 +#define MDNS_DEBUGMSGS 0 diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h new file mode 100755 index 0000000..f58c914 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: BrowserController.h,v $ +Revision 1.7 2003/08/12 19:55:07 cheshire +Update to APSL 2.0 + + */ + +#import +#import + +#include + +@interface BrowserController : NSObject +{ + IBOutlet id domainField; + IBOutlet id nameField; + IBOutlet id typeField; + + IBOutlet id serviceDisplayTable; + IBOutlet id typeColumn; + IBOutlet id nameColumn; + IBOutlet id serviceTypeField; + IBOutlet id serviceNameField; + + IBOutlet id ipAddressField; + IBOutlet id portField; + IBOutlet id textField; + + NSMutableArray *srvtypeKeys; + NSMutableArray *srvnameKeys; + NSMutableArray *domainKeys; + NSMutableArray *nameKeys; + NSString *Domain; + NSString *SrvType; + NSString *SrvName; + NSString *Name; + + dns_service_discovery_ref browse_client; + +} + +- (IBAction)handleDomainClick:(id)sender; +- (IBAction)handleNameClick:(id)sender; +- (IBAction)handleTypeClick:(id)sender; + +- (IBAction)connect:(id)sender; + +- (IBAction)handleTableClick:(id)sender; +- (IBAction)removeSelected:(id)sender; +- (IBAction)addNewService:(id)sender; + +- (IBAction)update:(NSString *)Type Domain:(NSString *)Domain; +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication; +- (IBAction)loadDomains:(id)sender; + +- (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags; +- (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags; +- (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord; + +@end \ No newline at end of file diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m new file mode 100755 index 0000000..1dbe2a4 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: BrowserController.m,v $ +Revision 1.18 2003/08/12 19:55:07 cheshire +Update to APSL 2.0 + + */ + +#import "BrowserController.h" + +#include "arpa/inet.h" + +void +MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info ) +{ + DNSServiceDiscovery_handleReply(msg); +} + +void browse_reply ( + DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType + const char *replyName, + const char *replyType, + const char *replyDomain, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context + ) +{ + [[NSApp delegate] updateBrowseWithResult:resultType name:[NSString stringWithUTF8String:replyName] type:[NSString stringWithUTF8String:replyType] domain:[NSString stringWithUTF8String:replyDomain] flags:flags]; + return; +} + +void enum_reply ( + DNSServiceDomainEnumerationReplyResultType resultType, + const char *replyDomain, + DNSServiceDiscoveryReplyFlags flags, + void *context + ) +{ + [[NSApp delegate] updateEnumWithResult:resultType domain:[NSString stringWithUTF8String:replyDomain] flags:flags]; + + return; +} + +void resolve_reply ( + struct sockaddr *interface, + struct sockaddr *address, + const char *txtRecord, + DNSServiceDiscoveryReplyFlags flags, + void *context + ) +{ + [[NSApp delegate] resolveClientWithInterface:interface address:address txtRecord:[NSString stringWithUTF8String:txtRecord]]; + + return; +} + +@implementation BrowserController //Begin implementation of BrowserController methods + +- (void)registerDefaults +{ + NSMutableDictionary *regDict = [NSMutableDictionary dictionary]; + + NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_tftp._tcp.", + @"_ssh._tcp.", @"_telnet._tcp.", + @"_http._tcp.", + @"_printer._tcp.", @"_ipp._tcp.", + @"_ichat._tcp.", @"_eppc._tcp.", + @"_afpovertcp._tcp.", @"_afpovertcp._tcp.", @"_MacOSXDupSuppress._tcp.", nil]; + NSArray *nameArray = [NSArray arrayWithObjects:@"File Transfer (ftp)", @"Trivial File Transfer (tftp)", + @"Secure Shell (ssh)", @"Telnet", + @"Web Server (http)", + @"LPR Printer", @"IPP Printer", + @"iChat", @"Remote AppleEvents", + @"AppleShare Server", @"SMB File Server", @"Mystery Service", nil]; + + [regDict setObject:typeArray forKey:@"SrvTypeKeys"]; + [regDict setObject:nameArray forKey:@"SrvNameKeys"]; + + [[NSUserDefaults standardUserDefaults] registerDefaults:regDict]; +} + + +- (id)init +{ + [self registerDefaults]; + + browse_client = nil; + + return [super init]; +} + +- (void)awakeFromNib //BrowserController startup procedure +{ + SrvType=NULL; + Domain=NULL; + srvtypeKeys = [NSMutableArray array]; //Define arrays for Type, Domain, and Name + srvnameKeys = [NSMutableArray array]; + + domainKeys = [NSMutableArray array]; + [domainKeys retain]; + + nameKeys = [NSMutableArray array]; + [nameKeys retain]; + + [srvtypeKeys retain]; //Keep arrays in memory until BrowserController closes + [srvnameKeys retain]; //Keep arrays in memory until BrowserController closes + [typeField setDataSource:self]; //Set application fields' data source to BrowserController + [typeField sizeLastColumnToFit]; //and set column sizes to use their whole table's width. + [nameField setDataSource:self]; + [nameField sizeLastColumnToFit]; + [domainField setDataSource:self]; + [domainField sizeLastColumnToFit]; + + [nameField setDoubleAction:@selector(connect:)]; + + //[srvtypeKeys addObject:@"_ftp._tcp."]; //Add supported protocols and domains to their + //[srvnameKeys addObject:@"File Transfer (ftp)"]; + //[srvtypeKeys addObject:@"_printer._tcp."]; //respective arrays + //[srvnameKeys addObject:@"Printer (lpr)"]; + //[srvtypeKeys addObject:@"_http._tcp."]; //respective arrays + //[srvnameKeys addObject:@"Web Server (http)"]; + //[srvtypeKeys addObject:@"_afp._tcp."]; //respective arrays + //[srvnameKeys addObject:@"AppleShare Server (afp)"]; + + [ipAddressField setStringValue:@""]; + [portField setStringValue:@""]; + [textField setStringValue:@""]; + + [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]]; + [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]]; + + + [typeField reloadData]; //Reload (redraw) data in fields + [domainField reloadData]; + + [self loadDomains:self]; + +} + +- (void)dealloc //Deallocation method +{ + [srvtypeKeys release]; + [srvnameKeys release]; + [nameKeys release]; + [domainKeys release]; +} + +-(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row +{ + if (row<0) return; +} + +- (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods +{ + if (theTableView == typeField) + { + return [srvnameKeys count]; + } + if (theTableView == domainField) + { + return [domainKeys count]; + } + if (theTableView == nameField) + { + return [nameKeys count]; + } + if (theTableView == serviceDisplayTable) + { + return [srvnameKeys count]; + } + return 0; +} + +- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex +{ + if (theTableView == typeField) + { + return [srvnameKeys objectAtIndex:rowIndex]; + } + if (theTableView == domainField) + { + return [domainKeys objectAtIndex:rowIndex]; + } + if (theTableView == nameField) + { + return [[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:rowIndex]; + } + if (theTableView == serviceDisplayTable) + { + if (theColumn == typeColumn) { + return [srvtypeKeys objectAtIndex:rowIndex]; + } + if (theColumn == nameColumn) { + return [srvnameKeys objectAtIndex:rowIndex]; + } + return 0; + } + else + return(0); +} //End of mandatory TableView methods + +- (IBAction)handleTypeClick:(id)sender //Handle clicks for Type +{ + int index=[sender selectedRow]; //Find index of selected row + if (index==-1) return; //Error checking + SrvType = [srvtypeKeys objectAtIndex:index]; //Save desired Type + SrvName = [srvnameKeys objectAtIndex:index]; //Save desired Type + + [ipAddressField setStringValue:@""]; + [portField setStringValue:@""]; + [textField setStringValue:@""]; + + [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records +} + +- (IBAction)handleDomainClick:(id)sender //Handle clicks for Domain +{ + int index=[sender selectedRow]; //Find index of selected row + if (index==-1) return; //Error checking + Domain = [domainKeys objectAtIndex:index]; //Save desired Domain + + [ipAddressField setStringValue:@""]; + [portField setStringValue:@""]; + [textField setStringValue:@""]; + + if (SrvType!=NULL) [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records +} + +- (IBAction)handleNameClick:(id)sender //Handle clicks for Name +{ + int index=[sender selectedRow]; //Find index of selected row + if (index==-1) return; //Error checking + Name=[[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:index]; //Save desired name + + { + CFMachPortRef cfMachPort; + CFMachPortContext context; + Boolean shouldFreeInfo; + dns_service_discovery_ref dns_client; + mach_port_t port; + CFRunLoopSourceRef rls; + + context.version = 1; + context.info = 0; + context.retain = NULL; + context.release = NULL; + context.copyDescription = NULL; + + [ipAddressField setStringValue:@"?"]; + [portField setStringValue:@"?"]; + [textField setStringValue:@"?"]; + // start an enumerator on the local server + dns_client = DNSServiceResolverResolve + ( + (char *)[Name UTF8String], + (char *)[SrvType UTF8String], + (char *)(Domain?[Domain UTF8String]:""), + resolve_reply, + nil + ); + + port = DNSServiceDiscoveryMachPort(dns_client); + + if (port) { + cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo ); + + /* Create and add a run loop source for the port */ + rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + } else { + printf("Could not obtain client port\n"); + return; + } + } +} + +- (IBAction)loadDomains:(id)sender +{ + CFMachPortRef cfMachPort; + CFMachPortContext context; + Boolean shouldFreeInfo; + dns_service_discovery_ref dns_client; + mach_port_t port; + CFRunLoopSourceRef rls; + + context.version = 1; + context.info = 0; + context.retain = NULL; + context.release = NULL; + context.copyDescription = NULL; + + // start an enumerator on the local server + dns_client = DNSServiceDomainEnumerationCreate + ( + 0, + enum_reply, + nil + ); + + port = DNSServiceDiscoveryMachPort(dns_client); + + if (port) { + cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo ); + + /* Create and add a run loop source for the port */ + rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + } else { + printf("Could not obtain client port\n"); + return; + } +} + +- (IBAction)update:theType Domain:theDomain; //The Big Kahuna: Fetch PTR records and update application +{ + const char * DomainC; + const char * TypeC=[theType UTF8String]; //Type in C string format + + if (theDomain) { + DomainC = [theDomain UTF8String]; //Domain in C string format + } else { + DomainC = ""; + } + + [nameKeys removeAllObjects]; //Get rid of displayed records if we're going to go get new ones + [nameField reloadData]; //Reload (redraw) names to show the old data is gone + + // get rid of the previous browser if one exists + if (browse_client) { + DNSServiceDiscoveryDeallocate(browse_client); + browse_client = nil; + } + + // now create a browser to return the values for the nameField ... + { + CFMachPortRef cfMachPort; + CFMachPortContext context; + Boolean shouldFreeInfo; + mach_port_t port; + CFRunLoopSourceRef rls; + + context.version = 1; + context.info = 0; + context.retain = NULL; + context.release = NULL; + context.copyDescription = NULL; + + // start an enumerator on the local server + browse_client = DNSServiceBrowserCreate + ( + (char *)TypeC, + (char *)DomainC, + browse_reply, + nil + ); + + port = DNSServiceDiscoveryMachPort(browse_client); + + if (port) { + cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo ); + + /* Create and add a run loop source for the port */ + rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + } else { + printf("Could not obtain client port\n"); + return; + } + } + +} + + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication //Quit when main window is closed +{ + return YES; +} + +- (BOOL)windowShouldClose:(NSWindow *)sender //Save domains to our domain file when quitting +{ + [domainField reloadData]; + return YES; +} + +- (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags +{ + // new domain received + if (DNSServiceDomainEnumerationReplyAddDomain == resultType || DNSServiceDomainEnumerationReplyAddDomainDefault == resultType) { + // add the domain to the list + [domainKeys addObject:domain]; + } else { + // remove the domain from the list + NSEnumerator *dmnEnum = [domainKeys objectEnumerator]; + NSString *aDomain = nil; + + while (aDomain = [dmnEnum nextObject]) { + if ([aDomain isEqualToString:domain]) { + [domainKeys removeObject:domain]; + break; + } + } + } + // update the domain table + [domainField reloadData]; + return; +} + + + +- (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags +{ + + //NSLog(@"Received result %@ %@ %@ %d", name, resulttype, domain, type); + + if (([domain isEqualToString:Domain] || [domain isEqualToString:@"local."]) && [resulttype isEqualToString:SrvType]) { + + if (type == DNSServiceBrowserReplyRemoveInstance) { + if ([nameKeys containsObject:name]) { + [nameKeys removeObject:name]; + } + } + if (type == DNSServiceBrowserReplyAddInstance) { + if (![nameKeys containsObject:name]) { + [nameKeys addObject:name]; + } + } + + // If not expecting any more data, then reload (redraw) Name TableView with newly found data + if ((flags & kDNSServiceDiscoveryMoreRepliesImmediately) == 0) + [nameField reloadData]; + } + return; +} + +- (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord +{ + if (address->sa_family != AF_INET) return; // For now we only handle IPv4 + //printf("interface length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)interface)->sin_len, ((struct sockaddr_in *)interface)->sin_port, ((struct sockaddr_in *)interface)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)interface)->sin_addr))); + //printf("address length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)address)->sin_len, ((struct sockaddr_in *)address)->sin_port, ((struct sockaddr_in *)address)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))); + NSString *ipAddr = [NSString stringWithCString:inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))]; + int port = ((struct sockaddr_in *)address)->sin_port; + + [ipAddressField setStringValue:ipAddr]; + [portField setIntValue:port]; + [textField setStringValue:txtRecord]; + + return; +} + +- (void)connect:(id)sender +{ + NSString *ipAddr = [ipAddressField stringValue]; + int port = [portField intValue]; + NSString *txtRecord = [textField stringValue]; + + if (!txtRecord) txtRecord = @""; + + if (!ipAddr || !port) return; + + if ([SrvType isEqualToString:@"_ftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@:%d/", ipAddr, port]]]; + else if ([SrvType isEqualToString:@"_tftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tftp://%@:%d/", ipAddr, port]]]; + else if ([SrvType isEqualToString:@"_ssh._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ssh://%@:%d/", ipAddr, port]]]; + else if ([SrvType isEqualToString:@"_telnet._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"telnet://%@:%d/", ipAddr, port]]]; + else if ([SrvType isEqualToString:@"_http._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d", ipAddr, port]]]; + else if ([SrvType isEqualToString:@"_printer._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"lpr://%@:%d/", ipAddr, port]]]; + else if ([SrvType isEqualToString:@"_ipp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ipp://%@:%d/", ipAddr, port]]]; + else if ([SrvType isEqualToString:@"_afpovertcp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%d/", ipAddr, port]]]; + else if ([SrvType isEqualToString:@"_smb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"smb://%@:%d/", ipAddr, port]]]; + + return; +} + +- (IBAction)handleTableClick:(id)sender +{ + //populate the text fields +} + +- (IBAction)removeSelected:(id)sender +{ + // remove the selected row and force a refresh + + int selectedRow = [serviceDisplayTable selectedRow]; + + if (selectedRow) { + + [srvtypeKeys removeObjectAtIndex:selectedRow]; + [srvnameKeys removeObjectAtIndex:selectedRow]; + + [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; + + [typeField reloadData]; + [serviceDisplayTable reloadData]; + } +} + +- (IBAction)addNewService:(id)sender +{ + // add new entries from the edit fields to the arrays for the defaults + + if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length]) { + [srvtypeKeys addObject:[serviceTypeField stringValue]]; + [srvnameKeys addObject:[serviceNameField stringValue]]; + + [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; + + [typeField reloadData]; + [serviceDisplayTable reloadData]; + } + +} + + + +@end \ No newline at end of file diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj b/mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj new file mode 100644 index 0000000..1cb8edd --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj @@ -0,0 +1,377 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 38; + objects = { + 080E96DCFE201CFB7F000001 = { + fileRef = 29B97318FDCFA39411CA2CEA; + isa = PBXBuildFile; + settings = { + }; + }; + 080E96DDFE201D6D7F000001 = { + children = ( + 6515F1C002245DF2000001D2, + 6515F1C102245DF2000001D2, + ); + isa = PBXGroup; + name = Classes; + refType = 4; + }; + 089C165CFE840E0CC02AAC07 = { + children = ( + 089C165DFE840E0CC02AAC07, + ); + isa = PBXVariantGroup; + name = InfoPlist.strings; + refType = 4; + }; + 089C165DFE840E0CC02AAC07 = { + fileEncoding = 10; + isa = PBXFileReference; + name = English; + path = English.lproj/InfoPlist.strings; + refType = 4; + }; + 089C165EFE840E0CC02AAC07 = { + fileRef = 089C165CFE840E0CC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; +//080 +//081 +//082 +//083 +//084 +//100 +//101 +//102 +//103 +//104 + 1058C7A0FEA54F0111CA2CBB = { + children = ( + 1058C7A1FEA54F0111CA2CBB, + ); + isa = PBXGroup; + name = "Linked Frameworks"; + refType = 4; + }; + 1058C7A1FEA54F0111CA2CBB = { + isa = PBXFrameworkReference; + name = Cocoa.framework; + path = /System/Library/Frameworks/Cocoa.framework; + refType = 0; + }; + 1058C7A2FEA54F0111CA2CBB = { + children = ( + 29B97325FDCFA39411CA2CEA, + 29B97324FDCFA39411CA2CEA, + ); + isa = PBXGroup; + name = "Other Frameworks"; + refType = 4; + }; + 1058C7A3FEA54F0111CA2CBB = { + fileRef = 1058C7A1FEA54F0111CA2CBB; + isa = PBXBuildFile; + settings = { + }; + }; +//100 +//101 +//102 +//103 +//104 +//170 +//171 +//172 +//173 +//174 + 17587328FF379C6511CA2CBB = { + isa = PBXApplicationReference; + path = "DNS Service Browser.app"; + refType = 3; + }; +//170 +//171 +//172 +//173 +//174 +//190 +//191 +//192 +//193 +//194 + 19C28FACFE9D520D11CA2CBB = { + children = ( + 17587328FF379C6511CA2CBB, + ); + isa = PBXGroup; + name = Products; + refType = 4; + }; +//190 +//191 +//192 +//193 +//194 +//290 +//291 +//292 +//293 +//294 + 29B97313FDCFA39411CA2CEA = { + buildStyles = ( + 4A9504CCFFE6A4B311CA0CBA, + 4A9504CDFFE6A4B311CA0CBA, + ); + isa = PBXProject; + mainGroup = 29B97314FDCFA39411CA2CEA; + projectDirPath = ""; + targets = ( + 29B97326FDCFA39411CA2CEA, + ); + }; + 29B97314FDCFA39411CA2CEA = { + children = ( + 080E96DDFE201D6D7F000001, + 29B97315FDCFA39411CA2CEA, + 29B97317FDCFA39411CA2CEA, + 29B97323FDCFA39411CA2CEA, + 19C28FACFE9D520D11CA2CBB, + ); + isa = PBXGroup; + name = "DNS Service Browser"; + path = ""; + refType = 4; + }; + 29B97315FDCFA39411CA2CEA = { + children = ( + 29B97316FDCFA39411CA2CEA, + ); + isa = PBXGroup; + name = "Other Sources"; + path = ""; + refType = 4; + }; + 29B97316FDCFA39411CA2CEA = { + isa = PBXFileReference; + path = main.m; + refType = 4; + }; + 29B97317FDCFA39411CA2CEA = { + children = ( + 29B97318FDCFA39411CA2CEA, + 089C165CFE840E0CC02AAC07, + 6515F1C5022460A1000001D2, + ); + isa = PBXGroup; + name = Resources; + path = ""; + refType = 4; + }; + 29B97318FDCFA39411CA2CEA = { + children = ( + 29B97319FDCFA39411CA2CEA, + ); + isa = PBXVariantGroup; + name = MainMenu.nib; + path = ""; + refType = 4; + }; + 29B97319FDCFA39411CA2CEA = { + isa = PBXFileReference; + name = English; + path = English.lproj/MainMenu.nib; + refType = 4; + }; + 29B97323FDCFA39411CA2CEA = { + children = ( + 1058C7A0FEA54F0111CA2CBB, + 1058C7A2FEA54F0111CA2CBB, + ); + isa = PBXGroup; + name = Frameworks; + path = ""; + refType = 4; + }; + 29B97324FDCFA39411CA2CEA = { + isa = PBXFrameworkReference; + name = AppKit.framework; + path = /System/Library/Frameworks/AppKit.framework; + refType = 0; + }; + 29B97325FDCFA39411CA2CEA = { + isa = PBXFrameworkReference; + name = Foundation.framework; + path = /System/Library/Frameworks/Foundation.framework; + refType = 0; + }; + 29B97326FDCFA39411CA2CEA = { + buildPhases = ( + 29B97327FDCFA39411CA2CEA, + 29B97328FDCFA39411CA2CEA, + 29B9732BFDCFA39411CA2CEA, + 29B9732DFDCFA39411CA2CEA, + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = "$(HOME)/Applications"; + LIBRARY_SEARCH_PATHS = ""; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "DNS Service Browser"; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + WRAPPER_EXTENSION = app; + }; + dependencies = ( + ); + isa = PBXApplicationTarget; + name = "DNS Service Browser"; + productInstallPath = "$(HOME)/Applications"; + productName = "DNS Service Browser"; + productReference = 17587328FF379C6511CA2CBB; + productSettingsXML = " + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + DNS Service Browser + CFBundleIconFile + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 0.1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + +"; + shouldUseHeadermap = 1; + }; + 29B97327FDCFA39411CA2CEA = { + buildActionMask = 2147483647; + files = ( + 6515F1C202245DF2000001D2, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 29B97328FDCFA39411CA2CEA = { + buildActionMask = 2147483647; + files = ( + 080E96DCFE201CFB7F000001, + 089C165EFE840E0CC02AAC07, + ); + isa = PBXResourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 29B9732BFDCFA39411CA2CEA = { + buildActionMask = 2147483647; + files = ( + 29B9732CFDCFA39411CA2CEA, + 6515F1C302245DF3000001D2, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 29B9732CFDCFA39411CA2CEA = { + fileRef = 29B97316FDCFA39411CA2CEA; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + ); + }; + }; + 29B9732DFDCFA39411CA2CEA = { + buildActionMask = 2147483647; + files = ( + 1058C7A3FEA54F0111CA2CBB, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; +//290 +//291 +//292 +//293 +//294 +//4A0 +//4A1 +//4A2 +//4A3 +//4A4 + 4A9504CCFFE6A4B311CA0CBA = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = NO; + OPTIMIZATION_CFLAGS = "-O0"; + }; + isa = PBXBuildStyle; + name = Development; + }; + 4A9504CDFFE6A4B311CA0CBA = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = YES; + }; + isa = PBXBuildStyle; + name = Deployment; + }; +//4A0 +//4A1 +//4A2 +//4A3 +//4A4 +//650 +//651 +//652 +//653 +//654 + 6515F1C002245DF2000001D2 = { + isa = PBXFileReference; + path = BrowserController.h; + refType = 4; + }; + 6515F1C102245DF2000001D2 = { + isa = PBXFileReference; + path = BrowserController.m; + refType = 4; + }; + 6515F1C202245DF2000001D2 = { + fileRef = 6515F1C002245DF2000001D2; + isa = PBXBuildFile; + settings = { + }; + }; + 6515F1C302245DF3000001D2 = { + fileRef = 6515F1C102245DF2000001D2; + isa = PBXBuildFile; + settings = { + }; + }; + 6515F1C5022460A1000001D2 = { + isa = PBXFileReference; + name = DNSServiceDiscovery.h; + path = /usr/include/DNSServiceDiscovery/DNSServiceDiscovery.h; + refType = 0; + }; + }; + rootObject = 29B97313FDCFA39411CA2CEA; +} diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..08c0f0c67367eba42629b7bb234c30cb8d7a5f25 GIT binary patch literal 612 zcmb`ENe=-*5QX2_ujn|6*skJ&SP~pe;wobs!WbD_`0@C7(3dBfds%D@iQ({UlX>;J*QrTxiSy literal 0 HcmV?d00001 diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib new file mode 100644 index 0000000..2668d5d --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib @@ -0,0 +1,37 @@ +{ + IBClasses = ( + { + ACTIONS = { + addNewService = id; + connect = id; + editDomains = id; + handleDomainClick = id; + handleNameClick = id; + handleTableClick = id; + handleTypeClick = id; + loadDomains = id; + removeSelected = id; + }; + CLASS = BrowserController; + LANGUAGE = ObjC; + OUTLETS = { + domainEditField = id; + domainField = id; + domainWindow = id; + ipAddressField = id; + nameColumn = id; + nameField = id; + portField = id; + serviceDisplayTable = id; + serviceNameField = id; + serviceTypeField = id; + textField = id; + typeColumn = id; + typeField = id; + }; + SUPERCLASS = NSObject; + }, + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; } + ); + IBVersion = 1; +} \ No newline at end of file diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib new file mode 100644 index 0000000..e31cf4c --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib @@ -0,0 +1,22 @@ + + + + + IBDocumentLocation + 14 102 356 240 0 0 1152 746 + IBEditorPositions + + 29 + 22 474 271 44 0 0 1152 746 + + IBFramework Version + 273.0 + IBOpenObjects + + 220 + 201 + + IBSystem Version + 6C35 + + diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib new file mode 100644 index 0000000000000000000000000000000000000000..b330188906e31390fc577041682f1d3332a25082 GIT binary patch literal 9876 zcmb_i4RBP~b-qtp3H_lTB!fwCvT@B&$OI1C}_N8|jUal@2v+-BNc1c1na*oYW=FUqdn_=}XdhOjEbc&?Ze?;@bTY z{m#Ab?Y`BY<(Z;k-`ji7Irp4%&)+?F1C7~ZX*H3_8ft8qSO3IRx=qt;jzs%<_a1&o zjc0eovN6uCn`T`kDo0qMC_%!xOjaM3gB!=ps%}DUiA3+uWn+hvYBXzTsUbyRy6y4a^2$s;OLGRvqTbn`5*{tsLU)Z^q1k*4^FB%qLFQ z?$=TYeZ-PXxQq1|M(h|f%@v?7UdJY?ixWlj$>OBB5}18`gx=k$b?3j#7&A|wX4cs} zYh^5d;?}KOtN?1Qk*E}DK+WhmBd%^7vjGT_eZ#RK)xtYT2Oh&m)cC{0G2>y&Dwuz0 z{}E<@Of&auqiQ0Wjb&9#K~wm%1Q%y$%~idml3-6V8O-Xz-G-jZR&wzNg+KPNXu0MG zB2g)AhKO61pi+0gmePi`M+Ha9u;mxSM_`ha+8BxMqs{oJrjESdH0vQE7B%$bMxk9A z#v7m@b6QU&RD%i4V>7}@Elu;C&(c;jMWWF-@RBsd*^e`vpPLvp6Fm%@u=# z-QDqc9J?oY539*!56n)}@&RQQqXAkPTC3ToWs^{jfZ7dlZyTGq%dGj>J=^1lYO&Zm z@PGCe`!LI~IMY~y#n=a#fzK4)RS;4JSDH$kT~IEt3$@_Mnr>nY8#y8PTv5D83V}cL z=CWBm#V!=5C-UzhDZqY#EB74atQZ9U_$*Gl+y}IHyqhV+J5(t#NrEo%y(do2K;PTG z^o$TEU$=v?KZ7(a1M0ATR1HSeB-~L=SmuipcTFuvj`J(QKgke)4A4`ju*WNAeI}y` zZ|LUiYipIfmG8ajzB`=s1{ePZxjj_1*=!TxFCby66;CZR!6hDH8q)7 zD>mV-(k8ISKg8M7ZL|qbw|Sc&mKPyvpNB^(fB)B9j##%;d6UaYFcKT~v2)F=j#CH{ zQ6{V>b@VguvQn0 zm??w4zyMoMlvE!>NB>KB&@?($8_T&ZoXG_|&IAKF%J4g!H)_8E?!eKmI34jADluGrVWa@H+=6=V+hR@de=Q$e9h&Rq79L4 zBQwWm@iob5aSFKyA=f`eLfoYPW{n7Edv>0L zP!?+e6D@6?e1d`I1Xu3l*h?@%PqCq2024ng{v5mTTJa6Dh7yI=(9qtVH=$n(l9j}t z5<;{=GVB9=3gT11pStq8U{1zkv0oCxg`+j_NA6xQ8{wXmIR-mBwVQ7s&r}JST*boJ zbHgcAFLji(NHs9s1~OSWV;R3pRngp?ps=xyES@9rP?Ro(3_^VyA-d5G(M=Fm2ED=z zqlodssSli^a8cT*mLtLj%E1C2YJ!E1EwkN%1?AqdlTFRAS6;Fd22&`Sx4up?Q7m(1 zIu%~K>!nk@zxO-OlWbKBi6J6iz{gWfZRKhW(O4=Nju}Zkv*5B;b=4~+-s^`9%^!45 zbz!p+c-jhud~f$j0=RZ%?Qv0q*)JqD%UPB+rsD_0NY@mXw;|zhU z?K!OHvcZJ_&w7=euOuoXHuoFqplYb8xSH9#dGn%Gn8e7#DB0PHdT~Ljb#Bbd%%f*E zXL5&!QNzhhFH^CQ^NG`CEC`1zjGPaT^GM0U&nmW;nZJFHmQWWI8o0_4YmtJmOrTK^ z*qc3~8uMB83eYlmUG#`PLRITLu_|GgtESt3J*O?aX~h2{i}5c~D>OsL2i2{ImtL~N zz6OI5_8$&GQte%`X;F{-WNc!nKLAbZ8=vB2kur&0M+T zNHb$cnu{T6+M@DUG((cJby_73cKR~p_PMk^qKq9`|NM)kY(E!!a`F=`jD4bI0lZSCmj#CMC};n}*^|T^J%z21W+TOZ zso}paCq}BTv1NC8Cf3-**(Za9`pIC0kqZ?TU-I{*g$oeQASc>9PJ2-m=ywYcd zZafF{xLpHQH9C03lEJ>zpl%F{o`i3mLrbiq^d~qwFV;D~@H*akVs&&lh9kc`AOz_| z8l*H{_V@>vTqg4WwM=};W$xze+#1r_xit%FZQ0Tc`j-)ATQ(!*>867)+k;CR?Fuw0 zQaJ6FO;uamR7IIs&#O==-UIfJ{P;> z?ep;}yuA&+{<7=q^b`ud7i;OoM~d_;_&Uu7nlT z-D3D{#OlqZn7SJKMI1t#Uc}vIZL{Mc`|7kELTQZFmuyn)Q-{+@w7$6V#u&P8nkep3 zext0et3o$~E59$dC!c0$vn*EOoj#4;ir3_O;&gzHJyxiom;oY)Ee@RIRW~juF9>3N zMG$Hk5k0{N(t1WXw1xAOeZyZ&*4-b|Qle-;Dyl9Xr|RZ}s3E<6;@tA?o1zCXi2~Y# z4D3;p>3KDfgri!H=GlWUub_1xnMPAVZ~2*P^8k@)wmmjVO0YjDzBB3p3ea zov;st?6n>C#t!@9v>ooUw}Q_Ab9-Zd^+t4%M1Z=7Ue|{ zrA1ImfrZuO5NMZuCAub%%BQ;$V*8=Ruw@svZ18o_G@2B+ZdgLQoc)!EhQd#-2)#CX zXr6967h6#7hQw+XHzGj~S8)0(%RzicC1S8A6Z}m*7ZiuJxv|bOO_G%UpRl!%ePZ11 zIBs8>vA1ovClPw=cXrtKg{X_*28`Vr?gHkLnrmQmT~G=b-w}$aMoU`Aky=^~3B6)( zT1|N>)0czMNEpEZHLfmj2~@#f1ti$SL5jBd@6Y5<$~4*_TsbJR+gwRYN`vvNMmCWP z?$YDAVJz%kD~TM(*>}z?$8n{=HDPpqWVjhwj<|tn?5G6KEC(FzCER)Rh~5mDeRMfU zwFBx=)j)b!r1kUmBwX2#OFwvDHkbA`wtqQX)b-<%FYBSC7ekR2j9hoZesvmY@3C=v zc8ad-kbZX=u@Ti!o+}n!;L69j5|EFNyALza*Ecw*u$C6LE*rPkbl7{s_UO1heaYS* zvb#bP8|`gSod5pMvJYVdce`}>5Vs~zUybpK7awgU?l%`j8)u%TK0W% zs3ZayXuuGOS{5IuPl=l~EM($E!N3VT%N40T3M8)#wJ+ie)-H=BSo{=M8bRQ3l+pG$ zu$w5ZZO8=oj-W4Z*|QSKw=C*kjQKYVbOtcw+w1H2TDb-L=i|`XL#$2rg9=341egF! z2lV#fmGo4W*0Qf+tWv#?VPuuB;Tm*{duqgkLGSkhY8LvzeKK5*;gC8ls z;BJdVBidoNd~}b8y207E#jh3Q6df6LqiyW+L;cjyMLXaptP z|ECf$BO#j8{jrpqEP<{chb~qhLH|~CUwyxXK*Pjk?oNWKA(+z827#7K(IEP;^UzGQ zL+EF34maPp!4s!vp&qWh=W>^b1SxpYhty-@G$mHrdKvaaQ}tO~T8MxkSr8NCN%bjo zCVdmZ%iDm0iPj^KFsV|S#?J)e50@AXSIkpC&p*HQ^0=I@OW-zpNc>8IHbg8)j@q7) zcfh-}3{H~Agk5=Rxgt?p3~?XsU5A%e#=)34YmOxn$lUIsq%=DvXWLx9g}wS17YvX$ zw7LwCWIn;gJo%G1Pm)#4y9+cV@Ozo4`1OdVOLt}=k;!WtNcnD{EN=^ikp|NK+?kt~ zFw+>vF$(FsaxZUE7->q!aI2AZJ*-6V=7M?;ULKG!cSTko8cKThCZwg<+pMMCZeE3~ zphXeAKCK%vk9eYDf^vtrM)qL@FKcr772sR+b%0Qq_?TJMt(~iJCe}Dy*~r0CjTqLp(6~J?F$7ctfY}`_3R&R^WXDXTOWb0j|_J zhmbQJyj44wIQwsm{?Z0edyKOm;k}=;H=P@t{f9G)AF?*pt&j-5^y zet6)-xl-$FMu@=#^fc>?D|ovPl@}gcaBU2d)rW0=w&faja z^)KTc-2cMaiRuII>mS9(F3$eV0imnTcFw+mfxFSwaT+-L2fV)*A>O$Yr1Ac1_{ofO ze24QcRJQoK8@uJe#jZHh=ovesoPFK7h+k(p8X`32LCwMAL9R66fx@8yEFxum*V)gN zD(9VO0bsnBv!CJdVb1>30cef07KeEN!bbkrS;yHa2h#qR1AW>6`wgy;*zaDCkB@SN z0ni+BCU3_x-|oE1*=r8=ZX91f$Jto`eSotYcyx32QwL7@igS&#pF2+-#MnO0ZaMJd ef1KdT$_Z9P*lg|V4eN0|w%dTQkLdK1s{aF>>GoIv literal 0 HcmV?d00001 diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/main.m b/mDNSMacOSX/Applications/DNSServiceBrowser/main.m new file mode 100644 index 0000000..c340509 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceBrowser/main.m @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: main.m,v $ +Revision 1.4 2003/08/12 19:55:07 cheshire +Update to APSL 2.0 + + */ + +#import + +int main(int argc, const char *argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj b/mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj new file mode 100644 index 0000000..9487a76 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj @@ -0,0 +1,377 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 38; + objects = { + 080E96DCFE201CFB7F000001 = { + fileRef = 29B97318FDCFA39411CA2CEA; + isa = PBXBuildFile; + settings = { + }; + }; + 080E96DDFE201D6D7F000001 = { + children = ( + 654EC28D0226D54A006533C2, + 654EC28C0226D54A006533C2, + ); + isa = PBXGroup; + name = Classes; + refType = 4; + }; + 089C165CFE840E0CC02AAC07 = { + children = ( + 089C165DFE840E0CC02AAC07, + ); + isa = PBXVariantGroup; + name = InfoPlist.strings; + refType = 4; + }; + 089C165DFE840E0CC02AAC07 = { + fileEncoding = 10; + isa = PBXFileReference; + name = English; + path = English.lproj/InfoPlist.strings; + refType = 4; + }; + 089C165EFE840E0CC02AAC07 = { + fileRef = 089C165CFE840E0CC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; +//080 +//081 +//082 +//083 +//084 +//100 +//101 +//102 +//103 +//104 + 1058C7A0FEA54F0111CA2CBB = { + children = ( + 1058C7A1FEA54F0111CA2CBB, + ); + isa = PBXGroup; + name = "Linked Frameworks"; + refType = 4; + }; + 1058C7A1FEA54F0111CA2CBB = { + isa = PBXFrameworkReference; + name = Cocoa.framework; + path = /System/Library/Frameworks/Cocoa.framework; + refType = 0; + }; + 1058C7A2FEA54F0111CA2CBB = { + children = ( + 29B97325FDCFA39411CA2CEA, + 29B97324FDCFA39411CA2CEA, + 65DD378E028194AE000001D1, + ); + isa = PBXGroup; + name = "Other Frameworks"; + refType = 4; + }; + 1058C7A3FEA54F0111CA2CBB = { + fileRef = 1058C7A1FEA54F0111CA2CBB; + isa = PBXBuildFile; + settings = { + }; + }; +//100 +//101 +//102 +//103 +//104 +//170 +//171 +//172 +//173 +//174 + 17587328FF379C6511CA2CBB = { + isa = PBXApplicationReference; + path = "DNS Service Registration.app"; + refType = 3; + }; +//170 +//171 +//172 +//173 +//174 +//190 +//191 +//192 +//193 +//194 + 19C28FACFE9D520D11CA2CBB = { + children = ( + 17587328FF379C6511CA2CBB, + ); + isa = PBXGroup; + name = Products; + refType = 4; + }; +//190 +//191 +//192 +//193 +//194 +//290 +//291 +//292 +//293 +//294 + 29B97313FDCFA39411CA2CEA = { + buildStyles = ( + 4A9504CCFFE6A4B311CA0CBA, + 4A9504CDFFE6A4B311CA0CBA, + ); + isa = PBXProject; + mainGroup = 29B97314FDCFA39411CA2CEA; + projectDirPath = ""; + targets = ( + 29B97326FDCFA39411CA2CEA, + ); + }; + 29B97314FDCFA39411CA2CEA = { + children = ( + 080E96DDFE201D6D7F000001, + 29B97315FDCFA39411CA2CEA, + 29B97317FDCFA39411CA2CEA, + 29B97323FDCFA39411CA2CEA, + 19C28FACFE9D520D11CA2CBB, + ); + isa = PBXGroup; + name = "DNS Service Registration"; + path = ""; + refType = 4; + }; + 29B97315FDCFA39411CA2CEA = { + children = ( + 29B97316FDCFA39411CA2CEA, + ); + isa = PBXGroup; + name = "Other Sources"; + path = ""; + refType = 4; + }; + 29B97316FDCFA39411CA2CEA = { + isa = PBXFileReference; + path = main.m; + refType = 4; + }; + 29B97317FDCFA39411CA2CEA = { + children = ( + 29B97318FDCFA39411CA2CEA, + 089C165CFE840E0CC02AAC07, + ); + isa = PBXGroup; + name = Resources; + path = ""; + refType = 4; + }; + 29B97318FDCFA39411CA2CEA = { + children = ( + 29B97319FDCFA39411CA2CEA, + ); + isa = PBXVariantGroup; + name = MainMenu.nib; + path = ""; + refType = 4; + }; + 29B97319FDCFA39411CA2CEA = { + isa = PBXFileReference; + name = English; + path = English.lproj/MainMenu.nib; + refType = 4; + }; + 29B97323FDCFA39411CA2CEA = { + children = ( + 1058C7A0FEA54F0111CA2CBB, + 1058C7A2FEA54F0111CA2CBB, + ); + isa = PBXGroup; + name = Frameworks; + path = ""; + refType = 4; + }; + 29B97324FDCFA39411CA2CEA = { + isa = PBXFrameworkReference; + name = AppKit.framework; + path = /System/Library/Frameworks/AppKit.framework; + refType = 0; + }; + 29B97325FDCFA39411CA2CEA = { + isa = PBXFrameworkReference; + name = Foundation.framework; + path = /System/Library/Frameworks/Foundation.framework; + refType = 0; + }; + 29B97326FDCFA39411CA2CEA = { + buildPhases = ( + 29B97327FDCFA39411CA2CEA, + 29B97328FDCFA39411CA2CEA, + 29B9732BFDCFA39411CA2CEA, + 29B9732DFDCFA39411CA2CEA, + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = "$(HOME)/Applications"; + LIBRARY_SEARCH_PATHS = ""; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "DNS Service Registration"; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + WRAPPER_EXTENSION = app; + }; + dependencies = ( + ); + isa = PBXApplicationTarget; + name = "DNS Service Registration"; + productInstallPath = "$(HOME)/Applications"; + productName = "DNS Service Registration"; + productReference = 17587328FF379C6511CA2CBB; + productSettingsXML = " + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + DNS Service Registration + CFBundleIconFile + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 0.1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + +"; + shouldUseHeadermap = 1; + }; + 29B97327FDCFA39411CA2CEA = { + buildActionMask = 2147483647; + files = ( + 654EC28E0226D54A006533C2, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 29B97328FDCFA39411CA2CEA = { + buildActionMask = 2147483647; + files = ( + 080E96DCFE201CFB7F000001, + 089C165EFE840E0CC02AAC07, + ); + isa = PBXResourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 29B9732BFDCFA39411CA2CEA = { + buildActionMask = 2147483647; + files = ( + 29B9732CFDCFA39411CA2CEA, + 654EC28F0226D54A006533C2, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 29B9732CFDCFA39411CA2CEA = { + fileRef = 29B97316FDCFA39411CA2CEA; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + ); + }; + }; + 29B9732DFDCFA39411CA2CEA = { + buildActionMask = 2147483647; + files = ( + 1058C7A3FEA54F0111CA2CBB, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; +//290 +//291 +//292 +//293 +//294 +//4A0 +//4A1 +//4A2 +//4A3 +//4A4 + 4A9504CCFFE6A4B311CA0CBA = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = NO; + OPTIMIZATION_CFLAGS = "-O0"; + }; + isa = PBXBuildStyle; + name = Development; + }; + 4A9504CDFFE6A4B311CA0CBA = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = YES; + }; + isa = PBXBuildStyle; + name = Deployment; + }; +//4A0 +//4A1 +//4A2 +//4A3 +//4A4 +//650 +//651 +//652 +//653 +//654 + 654EC28C0226D54A006533C2 = { + isa = PBXFileReference; + path = RegistrationController.m; + refType = 4; + }; + 654EC28D0226D54A006533C2 = { + isa = PBXFileReference; + path = RegistrationController.h; + refType = 4; + }; + 654EC28E0226D54A006533C2 = { + fileRef = 654EC28D0226D54A006533C2; + isa = PBXBuildFile; + settings = { + }; + }; + 654EC28F0226D54A006533C2 = { + fileRef = 654EC28C0226D54A006533C2; + isa = PBXBuildFile; + settings = { + }; + }; + 65DD378E028194AE000001D1 = { + isa = PBXFrameworkReference; + name = CoreFoundation.framework; + path = /System/Library/Frameworks/CoreFoundation.framework; + refType = 0; + }; + }; + rootObject = 29B97313FDCFA39411CA2CEA; +} diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..2aadfdc937f9b10aa5703db7e702c32f21be366b GIT binary patch literal 642 zcmcJMOAEp<5QOLKuLvGRd|kzhiVsi^7Q9N;T1Bf;YoY#l^-Fvlq$fc_vdQjbXLt5? zS5+k$jdavfzV_Pcsz57zDK^#_&rt`g!LB)nyw=<+IywAV3Emy(%%Mu;EU-H2sjUm> z6}UhX@<&*s7Q3h}9*>*ncv5(lE;{85dD*-%f09=PMKyD>dfHG+3~J``yzBMc?=9@% z&y3$17}e;yPVIS>^x;oF>im + + + + IBDocumentLocation + 32 111 356 240 0 0 1152 746 + IBEditorPositions + + 29 + 103 609 252 44 0 0 1152 746 + + IBFramework Version + 273.0 + IBOpenObjects + + 243 + 21 + + IBSystem Version + 6C30 + + diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib new file mode 100644 index 0000000000000000000000000000000000000000..705e77d75023c87108c7ca36a155b098c343ca3e GIT binary patch literal 9113 zcmbVSeQ+Dcb>9U+iX!+;NwqYR3ndaQ+o5ZTt*X#*1&Xq4)-lBjbS*c{xDarpE(UP) z;h+!qk7}vIxtUH=7en4yFg>GjCdtH#T_x$Ht(8nziM33PCh0Vze7H{Hp4-ONcsye_ zZLL-n`+K|iID!HVRTOcD-S^(Uef!?q-@auqWF4Q>5*f?XVkyz^j?E*sZMTm^hxhD1 z^f@hV?TuM6!QQg%=8>psVY$2_8OmlXBc&SeoVEiIV(l1-K9aR!hmu;4- zX`A2OHWD42oJ{KRn57$OC+FB)-|2$01)N$sJ8Wqw!LLozAWf=Cu*=hSP>)0+%zo}v z(_?x%VH|ao62-v=O*3|!+4d?(m#@=>VBUT{KWn#vZ+MuPBLg}FoI2I~h@RF{`qP?& zFFfT;u};RO{%CP=k>wz?b0n%}c0kJ*Su?KPIqiZFl7~~V3CTp0gsMY_wfK{%nE9mR z>n3jlIF-t=i42B;^s&kqYXdFr!88!ORC8K{S_ti3Wp+euihb$Q$ zOeVvY5#DDSX^T{6wRows{fUvNGE{~Xa-7psbBwXGXZ^GFjWqvC39;daBnaA05fjN%-*ZH&b0n&z%*ogCG-jILQv76;M@pv3u8^EQ-^;9gW6dX;) z;@V*&nb1s4maDu&+LkRRs>!bk1-MWk{EneJo^Rlz3WW8@RVcE6@oAE zGwcliTJy_|;;~LYh4}8G@H(-YQ2HL}9-nX+8HP|Ye(@0D!9$0k(P?(?Ta+!#*yeAv zuQ8^cbD5&V0CeVr)jnZK+k?N#IBkq9it5DFAMLs~L9h|tva zsUW|B)*uay*)@*oRuV2KsrF$aYo`la?8eXZ?~H%834?9G?~O(FX_jSirn3Z#u?Lxn z&otgO2;$>yHjlG&{2V*i1f86PR>t7v1*vCi-u}D%LSYJR?3++HC)lfxN4pq{cI6j} zx(4-lJi<6>ig+s9mQ=o9y2+sQ68hft_urEGPq7GNe+BhJ!|8G9qLf1H9~8FyO+8A# z+6HAw;UGC|Kp&8idBtvsQ)ZFLW5`Gv@ThZ5c7rlhQ}lQS8Hs$MHLez1(m3{;=Njz5 z`1r})cfE9m!-?NK*L41;AnPoW(Tpmj7vM#Xlh4_F0I6WU+CJ^3U|(5>^~7h%Lf5(S$l#-UN zQu}?krp47c{x9z=5!GH<@bBCwIHxJ$vz{rvyG)qhdjxw{u$SRM^jR&Ae7o!$!7=P; zPT5lL&9Q%ctE8jcjP_(L%Sf}=$U!%k_2oTHh@>zL@b&PL0i-i3CUVBzKQ20B^DgC# z9fOHPctkrIj%wx+J?uyBU?bxNFZVj|S z)IEd?G~ZO>vV~xy+$ggrz;wAzlI->BWx)-LUS!Q_+|C7@70o;rD4^Rz)4!7B0YHV zri=X{){e3_g~XEVeW{%1D<032=1v69&YOqMi3nWp>>J-GW`p^Pjofz^T;Lx2g%=2E z+_rZlT5`+q0c}DD&{Mbzn@I~HjL>PKyM6GGk+s4#sn3@1kqmO}sm;Z3$}E#-w`a14Qb09ohfrx;&iJ`g6)wyWVRq_N2ar%nl!8J| z-Z%A459tZ5_RJxGA9iYhri!v5DP_NPSTmRFTgG%{dpAc98%GJyEt}g?+InrZ>36dF z3d={6c&gSU);M1oJME3f(6ZRB9v&oGFHq`Xu!aT+H_srr2Bw zNTHV?9i3FD`~KJR_bTjy94liq(3L@?;xm75Mf;o`iV$|g@4$c#8Gy`E#zR^RkdGv7 z1kT&ObgZ2ul9Mpn!0?&xgUFrqYd1>3R^H6l712ZWe*SZuBLE>V1DYSiQ5vbp+oe>@ z_Y91Ji{W@C=L4XSO{H zrncN|%jL@5u%_JoWoS~ZqF{f#Ojv2_`hSQ!In-&td2-L6u6~u)%|G)c@cGsxI}L$f zlow?6gsJ=S=E>cRoYd*c<)(fZq4NO!$W4!TQ79DyioCy{e!o8;y}B5xDws6@gde;n z1E|VpyE-7CBO`+ca=(qia``^HVSIdmo!q_QgJBJyK6or0v{7>nmFhw6B?z3C-R4o9zbO`(s@pAY3b>i znulT-SZ2g6c$eYY-Z~a$Y;5IR*!(~Od7SppvXdlM$bnmBURf;yzb)85lWz1RlhY4! zMFrimzpOVMcR!joB~Z9oLf%?inO^smS+^(T_98=|3ZiNcV_Ir5i7JTTA5NnZqe~kh zfg+zY2OblAUa34ac??HNoUoQc6qIU8f(S1MDZ4toIc^6@4(1y?Ap%#=@e6`o5lrNB zSTT2Pk2{auNh1BdX6&eD^4Ie@ki3rn7k2EpAOZ&kf7T8T508&?_*2nZGw#8DmoK?etF?0%L;xlRB1{l_l6;0v5E1qCzwpOKZuBu9tKCjpb^B zv}N!>nG;ISxDgEVd_E_^Snwe&Ik^lNEXZ>op`53Yt5HgMKL3Z3yaz`QUqvk}xiJRc z?bLuRbciSd#O6nUi{+*dp#c&q*oo7uwHm^y(L?a+A_WF072Q^P zR5|H(hkSG0r=tvUa|uO67nCGWFQf+lyqt9dW=#X|bgfzXsaNfv)Y24I#9`FK5h4yi z=D3Dp+lu-vp)4&8a>VJW6Nr5b*fNyVVrE!cxmr>Q?vY~aU@JA?UL&4OVc=rvu4who ze?$q-KjFGcjB+iNIfK#I5mk}Q)MK3%A`cHrv`;3D*2C0vKs%!0oLK8HA=s_Vux%8@ zSj3-|oh&WvXgyjqkD@SXl~^Wgu~3X46x>Ufea=eRd1Yq^N|K7R{)LL@M^UkszV~#k z@zmlK$q#5n^56R?@~JuaSk0oYUueTnR#uJqv5%5TJ{ue|CXY*vb?Wlk`XG zqL(4D%#@{Q@ax^8&e!s=M6MS5DEal91eqnBSf)fJ(bAqHNwA&($CUXl$@N@CttA&4 zUs)l=v6?tgBs!+nV4@zLHpzpt^%333W~6Zw_3*8GG*hZdpgbBzp){;!0qbe?2HQ%+ksjG`nlTW zJxsX7D-CsDJ&YTcG9sUaA2+gLc`T@zsAUmH<%a7EZhxQK9&(W=X51?a?!qNE(d#ZQ zxWNUtuh)Hh!3~Gp_ZQG_Q!d#Ya^G8UxAnTSXmD+pu9epLAy4r}A*q=#=!~11aXU%) zNn&0faxYu%)dlzVP~qclLDmn^_l#gy-76482RFEvyr0<7@3_~i+jcnaLA2p+&v(Gv z?zr!w5tlM$=3jT*`Kl&}yILh~J2;^h)TlrRG`tKl-$kC|zK;gnm8kM5cxJ%SN;~=; znehIjIHEdkCrBE$??C5fyal)8pX2u9)qg&B+^v2|m`oqOW73rh0JvszZV=7Tpg17f zI2vfuW#f?eh+_6x!f~&m&2I>du~^jv&i9I(6>*OHHro96Lu8tF1p7BPg^m(TaufLC zH^nHv)6EB!Ak=Yt2ZprJigZdi(%pu4Kwvc&$2|_)aj0ObW`%AYcWk*FEZD=#Teky9 z$YOTshE}t1?Sb1V?4oS?YdoIbkBA@N9~#%Y*>eP7-Vt1i{nHGTfY z95m~h)J(dRq1)A%l{Im}IzYv0!8^{sLIfQ#+{~sYwBfY8!Yp@gCZA}*Wn)TDRQ6hKah0WWarDIuSDyvQ{ZAR3_axgW0Y-j7pv?!TI2DrcAS*Xc~#8+;qES%ciLj z#kiZYZNpN6v9ok2D&lr2!?xE*GwsjfG7Wbt6)Oqec^-`9#XT8yEwES5;4E}pF1NDF zW+EX^Y)PXmuUx>_Lo3)q*YnGI$a7jI4U?{OP^IzlkT0|*WVL%)A4NaREH8nUa*_Xb z7S*KcjZ7jwsT2mKe#(y3{gf|C{Z#y@?x*C@T{44Iu9||9P)R~LQk4cB=igUL&>l-9 zfcpIlAo%9<{i?SmfxlFU$_x9F=Ea`o1S<3s@-AL^;~Bc&Z7aH~yo)b-#OCwxa9Zsu z7PM0&w@$`TD6?H{k8WCT}w; zNg%mRusj-G7VL}Ob-`|UxL{^p=#$=7!S6!9id!0D+dp)4?EW!TWTPxTUdb{ty#|FWk^T6YJL8$EE_U-3h zZ@_z{5q;YQ@9|#z6}-U?t$5&~ay=e>f_Hgu3-+Pcf~zkyd|dDl9`|E(Jnlv!!($Nb zc>E?-zzYjD + +@interface RegistrationController : NSObject +{ + IBOutlet NSTableColumn *typeColumn; + IBOutlet NSTableColumn *nameColumn; + IBOutlet NSTableColumn *portColumn; + IBOutlet NSTableColumn *domainColumn; + IBOutlet NSTableColumn *textColumn; + + IBOutlet NSTableView *serviceDisplayTable; + + IBOutlet NSTextField *serviceTypeField; + IBOutlet NSTextField *serviceNameField; + IBOutlet NSTextField *servicePortField; + IBOutlet NSTextField *serviceDomainField; + IBOutlet NSTextField *serviceTextField; + + NSMutableArray *srvtypeKeys; + NSMutableArray *srvnameKeys; + NSMutableArray *srvportKeys; + NSMutableArray *srvdomainKeys; + NSMutableArray *srvtextKeys; + + NSMutableDictionary *registeredDict; +} + +- (IBAction)registerService:(id)sender; +- (IBAction)unregisterService:(id)sender; + +- (IBAction)addNewService:(id)sender; +- (IBAction)removeSelected:(id)sender; + +@end diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m b/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m new file mode 100644 index 0000000..6255179 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: RegistrationController.m,v $ +Revision 1.13 2003/08/12 19:55:07 cheshire +Update to APSL 2.0 + + */ + +#import "RegistrationController.h" + +#include + +void reg_reply ( + int errorCode, + void *context + ) +{ + // registration reply + printf("Got a reply from the server with error %d\n", errorCode); + return; +} + +void +MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info ) +{ + DNSServiceDiscovery_handleReply(msg); +} + +@implementation RegistrationController + +- (void)registerDefaults +{ + NSMutableDictionary *regDict = [NSMutableDictionary dictionary]; + + NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_ssh._tcp.", @"_tftp._tcp.", @"_http._tcp.", @"_printer._tcp.", @"_afpovertcp._tcp.", nil]; + NSArray *nameArray = [NSArray arrayWithObjects:@"My ftp Server", @"My Computer", @"Testing Boot Image", @"A Web Server", @"SteveÕs Printer", @"Company AppleShare Server", nil]; + NSArray *portArray = [NSArray arrayWithObjects:@"21", @"22", @"69", @"80", @"515", @"548", nil]; + NSArray *domainArray = [NSArray arrayWithObjects:@"", @"", @"", @"", @"", @"", nil]; + NSArray *textArray = [NSArray arrayWithObjects:@"", @"", @"image=mybootimage", @"path=/index.html", @"rn=lpt1", @"Vol=Public", nil]; + + [regDict setObject:typeArray forKey:@"SrvTypeKeys"]; + [regDict setObject:nameArray forKey:@"SrvNameKeys"]; + [regDict setObject:portArray forKey:@"SrvPortKeys"]; + [regDict setObject:domainArray forKey:@"SrvDomainKeys"]; + [regDict setObject:textArray forKey:@"SrvTextKeys"]; + + [[NSUserDefaults standardUserDefaults] registerDefaults:regDict]; +} + +- (id)init +{ + srvtypeKeys = [[NSMutableArray array] retain]; //Define arrays for Type, Domain, and Name + srvnameKeys = [[NSMutableArray array] retain]; + srvportKeys = [[NSMutableArray array] retain]; + srvdomainKeys = [[NSMutableArray array] retain]; + srvtextKeys = [[NSMutableArray array] retain]; + + registeredDict = [[NSMutableDictionary alloc] init]; + + [self registerDefaults]; + return [super init]; +} + +- (void)awakeFromNib //BrowserController startup procedure +{ + [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]]; + [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]]; + [srvportKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvPortKeys"]]; + [srvdomainKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvDomainKeys"]]; + [srvtextKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTextKeys"]]; + + [serviceDisplayTable reloadData]; //Reload (redraw) data in fields + +} + + + + - (IBAction)registerService:(id)sender +{ + int selectedRow = [serviceDisplayTable selectedRow]; + CFRunLoopSourceRef rls; + uint16_t registerPort; + CFMachPortRef cfMachPort; + CFMachPortContext context; + Boolean shouldFreeInfo; + dns_service_discovery_ref dns_client; + mach_port_t port; + + if (selectedRow < 0) { + return; + } + + context.version = 1; + context.info = 0; + context.retain = NULL; + context.release = NULL; + context.copyDescription = NULL; + + registerPort = [[srvportKeys objectAtIndex:selectedRow] intValue]; + + dns_client = DNSServiceRegistrationCreate + ( + [[srvnameKeys objectAtIndex:selectedRow] UTF8String], + [[srvtypeKeys objectAtIndex:selectedRow] UTF8String], + [[srvdomainKeys objectAtIndex:selectedRow] UTF8String], + registerPort, + [[srvtextKeys objectAtIndex:selectedRow] UTF8String], + reg_reply, + nil + ); + + port = DNSServiceDiscoveryMachPort(dns_client); + + if (port) { + + //printf("port is %d\n", port); + + cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo ); + + rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + [registeredDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)dns_client] forKey:[srvtypeKeys objectAtIndex:selectedRow]]; + } else { + printf("Could not obtain client port\n"); + } + +} + +- (IBAction)unregisterService:(id)sender +{ + int selectedRow = [serviceDisplayTable selectedRow]; + NSString *key = [srvtypeKeys objectAtIndex:selectedRow]; + + NSNumber *refPtr = [registeredDict objectForKey:key]; + dns_service_discovery_ref ref = (dns_service_discovery_ref)[refPtr unsignedIntValue]; + + if (ref) { + DNSServiceDiscoveryDeallocate(ref); + [registeredDict removeObjectForKey:key]; + } +} + +-(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row +{ + if (row<0) return; +} + +- (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods +{ + return [srvtypeKeys count]; +} + +- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex +{ + if (theColumn == typeColumn) { + return [srvtypeKeys objectAtIndex:rowIndex]; + } + if (theColumn == nameColumn) { + return [srvnameKeys objectAtIndex:rowIndex]; + } + if (theColumn == portColumn) { + return [srvportKeys objectAtIndex:rowIndex]; + } + if (theColumn == domainColumn) { + return [srvdomainKeys objectAtIndex:rowIndex]; + } + if (theColumn == textColumn) { + return [srvtextKeys objectAtIndex:rowIndex]; + } + + return(0); +} //End of mandatory TableView methods + +- (IBAction)removeSelected:(id)sender +{ + // remove the selected row and force a refresh + + int selectedRow = [serviceDisplayTable selectedRow]; + + if (selectedRow) { + + [srvtypeKeys removeObjectAtIndex:selectedRow]; + [srvnameKeys removeObjectAtIndex:selectedRow]; + [srvportKeys removeObjectAtIndex:selectedRow]; + [srvdomainKeys removeObjectAtIndex:selectedRow]; + [srvtextKeys removeObjectAtIndex:selectedRow]; + + [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; + + [serviceDisplayTable reloadData]; + } +} + +- (IBAction)addNewService:(id)sender +{ + // add new entries from the edit fields to the arrays for the defaults + + if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length] && [[serviceDomainField stringValue] length]&& [[servicePortField stringValue] length]) { + [srvtypeKeys addObject:[serviceTypeField stringValue]]; + [srvnameKeys addObject:[serviceNameField stringValue]]; + [srvportKeys addObject:[servicePortField stringValue]]; + [srvdomainKeys addObject:[serviceDomainField stringValue]]; + [srvtextKeys addObject:[serviceTextField stringValue]]; + + [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; + [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; + + [serviceDisplayTable reloadData]; + } else { + NSBeep(); + } + +} + + + +@end diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/main.m b/mDNSMacOSX/Applications/DNSServiceRegistration/main.m new file mode 100644 index 0000000..c340509 --- /dev/null +++ b/mDNSMacOSX/Applications/DNSServiceRegistration/main.m @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: main.m,v $ +Revision 1.4 2003/08/12 19:55:07 cheshire +Update to APSL 2.0 + + */ + +#import + +int main(int argc, const char *argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h new file mode 100644 index 0000000..7bb3e90 --- /dev/null +++ b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: HAAutomounter.h,v $ +Revision 1.4 2003/08/12 19:55:08 cheshire +Update to APSL 2.0 + + */ + +#import + + +@interface HAAutomounter : NSObject { + + NSNetServiceBrowser *browser; + NSNetService *resolver; +} + +@end diff --git a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m new file mode 100644 index 0000000..4aae5ed --- /dev/null +++ b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: HAAutomounter.m,v $ +Revision 1.4 2003/08/12 19:55:08 cheshire +Update to APSL 2.0 + + */ + +#import "HAAutomounter.h" + +#import + +#include +#include "arpa/inet.h" +#include + +@implementation HAAutomounter + +- (id)init +{ + self = [super init]; + + browser = [[NSNetServiceBrowser alloc] init]; + + [browser setDelegate:self]; + + [browser searchForServicesOfType:@"_mountme._tcp." inDomain:@""]; + + [browser scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode]; + + return self; +} + +- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing +{ + if (resolver) { + [resolver release]; + } + + resolver = [[NSNetService alloc] initWithDomain:[aNetService domain] type:[aNetService type] name:[aNetService name]]; + [resolver setDelegate:self]; + [resolver scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode]; + + [resolver resolve]; +} + + +- (void)netServiceDidResolveAddress:(NSNetService *)sender; +{ + if ([[sender addresses] count]) { + + // URL mount the volume + NSData *addr = [[sender addresses] objectAtIndex:0]; + struct sockaddr_in *address = CFDataGetBytePtr((CFDataRef)addr); + NSString *ipAddr = [NSString stringWithCString:inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))]; + int port = ((struct sockaddr_in *)address)->sin_port; + NSArray *txtArray = [[sender protocolSpecificInformation] componentsSeparatedByString:@","]; + + if ([txtArray count] == 3) { + NSString *user = [txtArray objectAtIndex:0]; + NSString *password = [txtArray objectAtIndex:1]; + NSString *share = [txtArray objectAtIndex:2]; + + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%@@%@:%d/%@", user, password, ipAddr, port, share]]]; + } else { + NSLog(@"incompatible format for txt record, s/b user,password,share"); + } + + } else { + NSLog(@"No address %@", sender); + } +} + +- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing +{ + // unmount the volume +} + + +@end diff --git a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj new file mode 100644 index 0000000..7d654f8 --- /dev/null +++ b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj @@ -0,0 +1,296 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 38; + objects = { + 014CEA4F0018CE4811CA2923 = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = NO; + OPTIMIZATION_CFLAGS = "-O0"; + }; + isa = PBXBuildStyle; + name = Development; + }; + 014CEA500018CE4811CA2923 = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = YES; + }; + isa = PBXBuildStyle; + name = Deployment; + }; +//010 +//011 +//012 +//013 +//014 +//030 +//031 +//032 +//033 +//034 + 034768E6FF38A76511DB9C8B = { + isa = PBXExecutableFileReference; + path = HAAutomounter; + refType = 3; + }; +//030 +//031 +//032 +//033 +//034 +//080 +//081 +//082 +//083 +//084 + 08FB7793FE84155DC02AAC07 = { + buildStyles = ( + 014CEA4F0018CE4811CA2923, + 014CEA500018CE4811CA2923, + ); + isa = PBXProject; + mainGroup = 08FB7794FE84155DC02AAC07; + projectDirPath = ""; + targets = ( + 08FB779FFE84155DC02AAC07, + ); + }; + 08FB7794FE84155DC02AAC07 = { + children = ( + 08FB7795FE84155DC02AAC07, + 08FB779DFE84155DC02AAC07, + 1AB674ADFE9D54B511CA2CBB, + ); + isa = PBXGroup; + name = HAAutomounter; + refType = 4; + }; + 08FB7795FE84155DC02AAC07 = { + children = ( + 65CA1A4902808474000001D1, + 65CA1A4802808474000001D1, + 08FB7796FE84155DC02AAC07, + ); + isa = PBXGroup; + name = Source; + refType = 4; + }; + 08FB7796FE84155DC02AAC07 = { + isa = PBXFileReference; + path = main.m; + refType = 4; + }; + 08FB779DFE84155DC02AAC07 = { + children = ( + 08FB779EFE84155DC02AAC07, + 65CA1A4F0280888E000001D1, + 65CA1DCD028088B2000001D1, + 65CA1E9D02809D68000001D1, + 65CA1EB302809DA3000001D1, + 6547B9AF0282024900CE36C6, + ); + isa = PBXGroup; + name = "External Frameworks and Libraries"; + refType = 4; + }; + 08FB779EFE84155DC02AAC07 = { + isa = PBXFrameworkReference; + name = Foundation.framework; + path = /System/Library/Frameworks/Foundation.framework; + refType = 0; + }; + 08FB779FFE84155DC02AAC07 = { + buildPhases = ( + 08FB77A0FE84155DC02AAC07, + 08FB77A1FE84155DC02AAC07, + 08FB77A3FE84155DC02AAC07, + 08FB77A5FE84155DC02AAC07, + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\""; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = "$(HOME)/bin"; + LIBRARY_SEARCH_PATHS = ""; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = HAAutomounter; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = HAAutomounter; + productInstallPath = "$(HOME)/bin"; + productName = HAAutomounter; + productReference = 034768E6FF38A76511DB9C8B; + shouldUseHeadermap = 1; + }; + 08FB77A0FE84155DC02AAC07 = { + buildActionMask = 2147483647; + files = ( + 65CA1A4A02808474000001D1, + ); + isa = PBXHeadersBuildPhase; + }; + 08FB77A1FE84155DC02AAC07 = { + buildActionMask = 2147483647; + files = ( + 08FB77A2FE84155DC02AAC07, + 65CA1A4B02808474000001D1, + ); + isa = PBXSourcesBuildPhase; + }; + 08FB77A2FE84155DC02AAC07 = { + fileRef = 08FB7796FE84155DC02AAC07; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + ); + }; + }; + 08FB77A3FE84155DC02AAC07 = { + buildActionMask = 2147483647; + files = ( + 08FB77A4FE84155DC02AAC07, + 65CA1DCC02808890000001D1, + 65CA1E9C028088B3000001D1, + 65CA1EB202809D68000001D1, + 65CA1EBC02809DA3000001D1, + 6547BBFD0282024900CE36C6, + ); + isa = PBXFrameworksBuildPhase; + }; + 08FB77A4FE84155DC02AAC07 = { + fileRef = 08FB779EFE84155DC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; + 08FB77A5FE84155DC02AAC07 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + }; +//080 +//081 +//082 +//083 +//084 +//1A0 +//1A1 +//1A2 +//1A3 +//1A4 + 1AB674ADFE9D54B511CA2CBB = { + children = ( + 034768E6FF38A76511DB9C8B, + ); + isa = PBXGroup; + name = Products; + refType = 4; + }; +//1A0 +//1A1 +//1A2 +//1A3 +//1A4 +//650 +//651 +//652 +//653 +//654 + 6547B9AF0282024900CE36C6 = { + isa = PBXFrameworkReference; + name = AppKit.framework; + path = /System/Library/Frameworks/AppKit.framework; + refType = 0; + }; + 6547BBFD0282024900CE36C6 = { + fileRef = 6547B9AF0282024900CE36C6; + isa = PBXBuildFile; + settings = { + }; + }; + 65CA1A4802808474000001D1 = { + isa = PBXFileReference; + path = HAAutomounter.h; + refType = 4; + }; + 65CA1A4902808474000001D1 = { + isa = PBXFileReference; + path = HAAutomounter.m; + refType = 4; + }; + 65CA1A4A02808474000001D1 = { + fileRef = 65CA1A4802808474000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + 65CA1A4B02808474000001D1 = { + fileRef = 65CA1A4902808474000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + 65CA1A4F0280888E000001D1 = { + isa = PBXFrameworkReference; + name = CoreFoundation.framework; + path = /System/Library/Frameworks/CoreFoundation.framework; + refType = 0; + }; + 65CA1DCC02808890000001D1 = { + fileRef = 65CA1A4F0280888E000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + 65CA1DCD028088B2000001D1 = { + isa = PBXFrameworkReference; + name = CoreServices.framework; + path = /System/Library/Frameworks/CoreServices.framework; + refType = 0; + }; + 65CA1E9C028088B3000001D1 = { + fileRef = 65CA1DCD028088B2000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + 65CA1E9D02809D68000001D1 = { + isa = PBXFrameworkReference; + name = AppleShareClientCore.framework; + path = /System/Library/Frameworks/AppleShareClientCore.framework; + refType = 0; + }; + 65CA1EB202809D68000001D1 = { + fileRef = 65CA1E9D02809D68000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + 65CA1EB302809DA3000001D1 = { + isa = PBXFrameworkReference; + name = URLMount.framework; + path = /System/Library/PrivateFrameworks/URLMount.framework; + refType = 0; + }; + 65CA1EBC02809DA3000001D1 = { + fileRef = 65CA1EB302809DA3000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + }; + rootObject = 08FB7793FE84155DC02AAC07; +} diff --git a/mDNSMacOSX/Applications/HAAutomounter/main.m b/mDNSMacOSX/Applications/HAAutomounter/main.m new file mode 100644 index 0000000..6ad4471 --- /dev/null +++ b/mDNSMacOSX/Applications/HAAutomounter/main.m @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: main.m,v $ +Revision 1.4 2003/08/12 19:55:08 cheshire +Update to APSL 2.0 + + */ + +#import + +#import "HAAutomounter.h" + +int main (int argc, const char * argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + [[HAAutomounter alloc] init]; + + [[NSRunLoop currentRunLoop] run]; + + [pool release]; + return 0; +} diff --git a/mDNSMacOSX/CFSocket.c b/mDNSMacOSX/CFSocket.c new file mode 100644 index 0000000..519a899 --- /dev/null +++ b/mDNSMacOSX/CFSocket.c @@ -0,0 +1,1464 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: CFSocket.c,v $ +Revision 1.115 2003/09/10 00:45:55 cheshire + Don't log "sendto failed" errors during the first two minutes of startup + +Revision 1.114 2003/08/27 02:55:13 cheshire +: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down) + +Revision 1.113 2003/08/19 22:20:00 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.112 2003/08/19 03:04:43 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured + +Revision 1.111 2003/08/18 22:53:37 cheshire + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.110 2003/08/16 03:39:00 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.109 2003/08/15 02:19:49 cheshire + syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 +Also limit number of messages to at most 100 + +Revision 1.108 2003/08/12 22:24:52 cheshire + syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 +This message indicates a kernel bug, but still we don't want to flood syslog. +Do a sleep(1) after writing this log message, to limit the rate. + +Revision 1.107 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + +Revision 1.106 2003/08/12 13:48:32 cheshire +Add comment explaining clockdivisor calculation + +Revision 1.105 2003/08/12 13:44:14 cheshire + mDNSResponder *VERY* unhappy if time goes backwards +Use mach_absolute_time() (which is guaranteed to always go forwards, resetting only on reboot) +instead of gettimeofday() (which can jump back if the user manually changes their time/date) + +Revision 1.104 2003/08/12 13:12:07 cheshire +Textual search/replace: Indicate local functions using "mDNSlocal" instead of "static" + +Revision 1.103 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.102 2003/08/06 00:14:52 cheshire + Need to check IP TTL on responses +Also add corresponding checks in the IPv6 code path + +Revision 1.101 2003/08/05 22:20:16 cheshire + Need to check IP TTL on responses + +Revision 1.100 2003/08/05 21:18:50 cheshire + mDNSResponder should ignore 6to4 +Only use interfaces that are marked as multicast-capable (IFF_MULTICAST) + +Revision 1.99 2003/08/05 20:13:52 cheshire + mDNSResponder using IPv6 interfaces before they are ready +Ignore interfaces with the IN6_IFF_NOTREADY flag set + +Revision 1.98 2003/07/20 03:38:51 ksekar +Bug #: 3320722 +Completed support for Unix-domain socket based API. + +Revision 1.97 2003/07/19 03:15:16 cheshire +Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, +and add the obvious trivial implementations to each platform support layer + +Revision 1.96 2003/07/18 00:30:00 cheshire + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.95 2003/07/12 03:15:20 cheshire + After SCDynamicStore notification, mDNSResponder updates +m->hostlabel even if user hasn't actually actually changed their dot-local hostname + +Revision 1.94 2003/07/03 00:51:54 cheshire + When select() and recvmgs() disagree, get more info from kernel about the socket state + +Revision 1.93 2003/07/03 00:09:14 cheshire + New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call +Additional refinement suggested by Josh: Use info->scope_id instead of if_nametoindex(info->ifa_name); + +Revision 1.92 2003/07/02 21:19:51 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.91 2003/06/24 01:53:51 cheshire +Minor update to comments + +Revision 1.90 2003/06/24 01:51:47 cheshire + Oops: Double-dispose of sockets +Don't need to close sockets: CFSocketInvalidate() does that for us + +Revision 1.89 2003/06/21 18:12:47 cheshire + Rendezvous cannot handle interfaces whose total name is >3 chars +One-line change: should say "IF_NAMESIZE", not sizeof(ifname) + +Revision 1.88 2003/06/12 23:38:37 cheshire + mDNSResponder doesn't detect some configuration changes +Also check that scope_id matches before concluding that two interfaces are the same + +Revision 1.87 2003/06/10 01:14:11 cheshire + New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call + +Revision 1.86 2003/05/28 02:41:52 cheshire + Time to remove Mac OS 9 UDP Port 53 legacy support + +Revision 1.85 2003/05/28 02:39:47 cheshire +Minor change to debugging messages + +Revision 1.84 2003/05/27 22:29:40 cheshire +Remove out-dated comment + +Revision 1.83 2003/05/26 03:21:29 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.82 2003/05/26 03:01:27 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.81 2003/05/24 02:06:42 cheshire + IPv6 Multicast Loopback doesn't work +Tried setting IPV6_MULTICAST_LOOP; it doesn't help. +However, it is probably wise to have the code explicitly set this socket +option anyway, in case the default changes in later versions of Unix. + +Revision 1.80 2003/05/24 02:02:24 cheshire + if_indextoname consumes a lot of CPU +Fix error in myIfIndexToName; was returning prematurely + +Revision 1.79 2003/05/23 23:07:44 cheshire + Must not write to stderr when running as daemon + +Revision 1.78 2003/05/23 01:19:04 cheshire + mDNSResponder needs to signal type of service to AirPort +Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate + +Revision 1.77 2003/05/23 01:12:05 cheshire +Minor code tidying + +Revision 1.76 2003/05/22 01:26:01 cheshire +Tidy up log messages + +Revision 1.75 2003/05/22 00:07:09 cheshire + myCFSocketCallBack recvfrom(5) error 1, errno 35 +Extra logging to determine whether there is a bug in CFSocket + +Revision 1.74 2003/05/21 20:20:12 cheshire +Fix warnings (mainly printf format string warnings, like using "%d" where +it should say "%lu", etc.) and improve error logging (use strerror() +to include textual error message as well as numeric error in log messages). + +Revision 1.73 2003/05/21 17:56:29 ksekar +Bug #: : mDNSResponder doesn't watch for IPv6 address changes + +Revision 1.72 2003/05/14 18:48:41 cheshire + mDNSResponder should be smarter about reconfigurations +More minor refinements: +CFSocket.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory +mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away + +Revision 1.71 2003/05/14 07:08:37 cheshire + mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.70 2003/05/07 18:30:24 cheshire +Fix signed/unsigned comparison warning + +Revision 1.69 2003/05/06 20:14:44 cheshire +Change "tp" to "tv" + +Revision 1.68 2003/05/06 00:00:49 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.67 2003/04/29 00:43:44 cheshire +Fix compiler warnings + +Revision 1.66 2003/04/26 02:41:58 cheshire + Change timenow from a local variable to a structure member + +Revision 1.65 2003/04/26 02:34:01 cheshire +Add missing mDNSexport + +Revision 1.64 2003/04/15 16:48:06 jgraessl +Bug #: 3228833 +Modified code in CFSocket notifier function to read all packets on the socket +instead of reading only one packet every time the notifier was called. + +Revision 1.63 2003/04/15 16:33:50 jgraessl +Bug #: 3221880 +Switched to our own copy of if_indextoname to improve performance. + +Revision 1.62 2003/03/28 01:55:44 cheshire +Minor improvements to debugging messages + +Revision 1.61 2003/03/27 03:30:56 cheshire + Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.60 2003/03/15 04:40:38 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.59 2003/03/11 01:23:26 cheshire +Bug #: 3194246 mDNSResponder socket problems + +Revision 1.58 2003/03/06 01:43:04 cheshire +Bug #: 3189097 Additional debugging code in mDNSResponder +Improve "LIST_ALL_INTERFACES" output + +Revision 1.57 2003/03/05 22:36:27 cheshire +Bug #: 3186338 Loopback doesn't work with mDNSResponder-27 +Temporary workaround: Skip loopback interface *only* if we found at least one v4 interface to use + +Revision 1.56 2003/03/05 01:50:38 cheshire +Bug #: 3189097 Additional debugging code in mDNSResponder + +Revision 1.55 2003/02/21 01:54:09 cheshire +Bug #: 3099194 mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.54 2003/02/20 06:48:35 cheshire +Bug #: 3169535 Xserve RAID needs to do interface-specific registrations +Reviewed by: Josh Graessley, Bob Bradley + +Revision 1.53 2003/01/29 02:21:23 cheshire +Return mStatus_Invalid if can't send packet because socket not available + +Revision 1.52 2003/01/28 19:39:43 jgraessl +Enabling AAAA over IPv4 support. + +Revision 1.51 2003/01/28 05:11:23 cheshire +Fixed backwards comparison in SearchForInterfaceByName + +Revision 1.50 2003/01/13 23:49:44 jgraessl +Merged changes for the following fixes in to top of tree: +3086540 computer name changes not handled properly +3124348 service name changes are not properly handled +3124352 announcements sent in pairs, failing chattiness test + +Revision 1.49 2002/12/23 22:13:30 jgraessl +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.48 2002/11/22 01:37:52 cheshire +Bug #: 3108426 mDNSResponder is monitoring ServiceEntities instead of InterfaceEntities + +Revision 1.47 2002/09/21 20:44:51 zarzycki +Added APSL info + +Revision 1.46 2002/09/19 21:25:35 cheshire +mDNS_snprintf() doesn't need to be in a separate file + +Revision 1.45 2002/09/17 01:45:13 cheshire +Add LIST_ALL_INTERFACES symbol for debugging + +Revision 1.44 2002/09/17 01:36:23 cheshire +Move Puma support to CFSocketPuma.c + +Revision 1.43 2002/09/17 01:05:28 cheshire +Change mDNS_AdvertiseLocalAddresses to be an Init parameter instead of a global + +Revision 1.42 2002/09/16 23:13:50 cheshire +Minor code tidying + + */ + +// *************************************************************************** +// mDNS-CFSocket.c: +// Supporting routines to run mDNS on a CFRunLoop platform +// *************************************************************************** + +// Open Transport 2.7.x on Mac OS 9 used to send Multicast DNS queries to UDP port 53, +// before the Multicast DNS port was changed to 5353. For this reason, the mDNSResponder +// in earlier versions of Mac OS X 10.2 Jaguar used to set mDNS_AllowPort53 to 1 to allow +// it to also listen and answer queries on UDP port 53. Now that Transport 2.8 (included in +// the Classic subsystem of Mac OS X 10.2 Jaguar) has been corrected to issue Multicast DNS +// queries on UDP port 5353, this backwards-compatibility legacy support is no longer needed. +#define mDNS_AllowPort53 0 + +// For debugging, set LIST_ALL_INTERFACES to 1 to display all found interfaces, +// including ones that mDNSResponder chooses not to use. +#define LIST_ALL_INTERFACES 0 + +// For enabling AAAA records over IPv4. Setting this to 0 sends only +// A records over IPv4 and AAAA over IPv6. Setting this to 1 sends both +// AAAA and A records over both IPv4 and IPv6. +#define AAAA_OVER_V4 1 + +#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above +#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below +#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform + +#include +#include // For select() and close() +#include // For va_list support +#include +#include +#include +#include +#include +#include +#include +#include + +#include // For IP_RECVTTL +#ifndef IP_RECVTTL +#define IP_RECVTTL 24 /* bool; receive reception TTL w/dgram */ +#endif + +#include // For n_long, required by below +#include // For IPTOS_LOWDELAY etc. +#include // For IN6_IFF_NOTREADY etc. + +// Code contributed by Dave Heller: +// Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will +// work on Mac OS X 10.1, which does not have the getifaddrs call. +#define RUN_ON_PUMA_WITHOUT_IFADDRS 0 +#if RUN_ON_PUMA_WITHOUT_IFADDRS +#include "CFSocketPuma.c" +#else +#include +#endif + +#include +#include +#include + +// *************************************************************************** +// Globals + +static mDNSu32 clockdivisor = 0; + +// *************************************************************************** +// Macros + +#define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger) +#define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3]) + +#define mDNSAddressIsAllDNSLinkGroup(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup )) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) ) + +// *************************************************************************** +// Functions + +// Note, this uses mDNS_vsnprintf instead of standard "vsnprintf", because mDNS_vsnprintf knows +// how to print special data types like IP addresses and length-prefixed domain names +#if MDNS_DEBUGMSGS +mDNSexport void debugf_(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } +#endif + +#if MDNS_DEBUGMSGS > 1 +mDNSexport void verbosedebugf_(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } +#endif + +mDNSexport void LogMsg(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + + extern int debug_mode; + if (debug_mode) // In debug_mode we write to stderr + { + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } + else // else, in production mode, we write to syslog + { + openlog("mDNSResponder", LOG_CONS | LOG_PERROR | LOG_PID, LOG_DAEMON); + syslog(LOG_ERR, "%s", buffer); + closelog(); + } + } + +mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh) + { + static struct ifaddrs *ifa = NULL; + + if (refresh && ifa) + { + freeifaddrs(ifa); + ifa = NULL; + } + + if (ifa == NULL) getifaddrs(&ifa); + + return ifa; + } + +mDNSlocal int myIfIndexToName(u_short index, char* name) + { + struct ifaddrs *ifa; + for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK) + if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == index) + { strncpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; } + return -1; + } + +mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index) + { + NetworkInterfaceInfoOSX *i; + if (index == (uint32_t)~0) return((mDNSInterfaceID)~0); + if (index) + for (i = m->p->InterfaceList; i; i = i->next) + if (i->scope_id == index) + return(i->ifinfo.InterfaceID); + return(mDNSNULL); + } + +mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id) + { + NetworkInterfaceInfoOSX *i; + if (id == (mDNSInterfaceID)~0) return((mDNSu32)~0); + if (id) + for (i = m->p->InterfaceList; i; i = i->next) + if (i->ifinfo.InterfaceID == id) + return i->scope_id; + return 0; + } + +mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, + mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort) + { + #pragma unused(m) + NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID; + struct sockaddr_storage to; + int s, err; + + if (!InterfaceID) { LogMsg("mDNSPlatformSendUDP ERROR! Cannot send from zero InterfaceID"); return mStatus_BadParamErr; } + + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in* sin_to = (struct sockaddr_in*)&to; + sin_to->sin_len = sizeof(*sin_to); + sin_to->sin_family = AF_INET; + sin_to->sin_port = dstPort.NotAnInteger; + sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + } + else if (dst->type == mDNSAddrType_IPv6) + { + struct sockaddr_in6* sin6_to = (struct sockaddr_in6*)&to; + sin6_to->sin6_len = sizeof(*sin6_to); + sin6_to->sin6_family = AF_INET6; + sin6_to->sin6_port = dstPort.NotAnInteger; + sin6_to->sin6_flowinfo = 0; + sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6; + sin6_to->sin6_scope_id = info->scope_id; + } + else + { + LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!\n"); + return mStatus_BadParamErr; + } + + if (srcPort.NotAnInteger == MulticastDNSPort.NotAnInteger) + { + if (dst->type == mDNSAddrType_IPv4) s = info->sktv4; + else if (dst->type == mDNSAddrType_IPv6) s = info->sktv6; + else s = -1; + } +#if mDNS_AllowPort53 + else if (srcPort.NotAnInteger == UnicastDNSPort.NotAnInteger && dst->type == mDNSAddrType_IPv4) + s = info->skt53; +#endif + else { LogMsg("Source port %d not allowed", (mDNSu16)srcPort.b[0]<<8 | srcPort.b[1]); return(-1); } + + if (s >= 0) + verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %X %s/%d to %#a:%d skt %d", + InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1], s); + else + verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %X %s/%d (socket of this type not available)", + InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1]); + + // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet + // If we don't have the corresponding type of socket available, then return mStatus_Invalid + if (s < 0) return(mStatus_Invalid); + + err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len); + if (err < 0) + { + // Don't report EHOSTDOWN (i.e. ARP failure) to unicast destinations + if (errno == EHOSTDOWN && !mDNSAddressIsAllDNSLinkGroup(dst)) return(err); + // Don't report EHOSTUNREACH in the first two minutes after boot + // This is because mDNSResponder intentionally starts up early in the boot process (See ) + // but this means that sometimes it starts before configd has finished setting up the multicast routing entries. + if (errno == EHOSTUNREACH && (mDNSu32)(m->timenow) < (mDNSu32)(mDNSPlatformOneSecond * 120)) return(err); + LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %s/%ld to %#a:%d skt %d error %d errno %d (%s)", + InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1], s, err, errno, strerror(errno)); + return(err); + } + + return(mStatus_NoError); + } + +mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, + struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) + { + static unsigned int numLogMessages = 0; + struct iovec databuffers = { (char *)buffer, max }; + struct msghdr msg; + ssize_t n; + struct cmsghdr *cmPtr; + char ancillary[1024]; + + *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be + + // Set up the message + msg.msg_name = (caddr_t)from; + msg.msg_namelen = *fromlen; + msg.msg_iov = &databuffers; + msg.msg_iovlen = 1; + msg.msg_control = (caddr_t)&ancillary; + msg.msg_controllen = sizeof(ancillary); + msg.msg_flags = 0; + + // Receive the data + n = recvmsg(s, &msg, 0); + if (n<0) + { + if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) returned error %d errno %d", s, n, errno); + return(-1); + } + if (msg.msg_controllen < (int)sizeof(struct cmsghdr)) + { + if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", + s, msg.msg_controllen, sizeof(struct cmsghdr)); + return(-1); + } + if (msg.msg_flags & MSG_CTRUNC) + { + if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); + return(-1); + } + + *fromlen = msg.msg_namelen; + + // Parse each option out of the ancillary data. + for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) + { + // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) + { + dstaddr->type = mDNSAddrType_IPv4; + dstaddr->ip.v4.NotAnInteger = *(u_int32_t*)CMSG_DATA(cmPtr); + } + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) + { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); + if (sdl->sdl_nlen < IF_NAMESIZE) + { + mDNSPlatformMemCopy(sdl->sdl_data, ifname, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = 0; + // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); + } + } + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL) + { + *ttl = *(u_char*)CMSG_DATA(cmPtr); + } + if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO) + { + struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr); + dstaddr->type = mDNSAddrType_IPv6; + dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr; + myIfIndexToName(ip6_info->ipi6_ifindex, ifname); + } + if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT) + { + *ttl = *(int*)CMSG_DATA(cmPtr); + } + } + + return(n); + } + +mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context) + { + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort, destPort = MulticastDNSPort; + NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)context; + mDNS *const m = info->m; + DNSMessage packet; + struct sockaddr_storage from; + size_t fromlen = sizeof(from); + char packetifname[IF_NAMESIZE] = ""; + int err, s1 = -1, skt = CFSocketGetNative(cfs); + int count = 0; + + (void)address; // Parameter not used + (void)data; // Parameter not used + + if (CallBackType != kCFSocketReadCallBack) LogMsg("myCFSocketCallBack: Why is CallBackType %d not kCFSocketReadCallBack?", CallBackType); + +#if mDNS_AllowPort53 + if (cfs == info->cfs53) { s1 = info->skt53; destPort = UnicastDNSPort; } + else +#endif + if (cfs == info->cfsv4) s1 = info->sktv4; + else if (cfs == info->cfsv6) s1 = info->sktv6; + + if (s1 < 0 || s1 != skt) + { + LogMsg("myCFSocketCallBack: s1 %d native socket %d, cfs %p", s1, skt, cfs); +#if mDNS_AllowPort53 + LogMsg("myCFSocketCallBack: cfs53 %p, skt53 %d", info->cfs53, info->skt53); +#endif + LogMsg("myCFSocketCallBack: cfsv4 %p, sktv4 %d", info->cfsv4, info->sktv4); + LogMsg("myCFSocketCallBack: cfsv6 %p, sktv6 %d", info->cfsv6, info->sktv6); + } + + mDNSu8 ttl; + while ((err = myrecvfrom(s1, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl)) >= 0) + { + count++; + if (from.ss_family == AF_INET) + { + struct sockaddr_in *sin = (struct sockaddr_in*)&from; + senderAddr.type = mDNSAddrType_IPv4; + senderAddr.ip.v4.NotAnInteger = sin->sin_addr.s_addr; + senderPort.NotAnInteger = sin->sin_port; + } + else if (from.ss_family == AF_INET6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; + senderAddr.type = mDNSAddrType_IPv6; + senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + senderPort.NotAnInteger = sin6->sin6_port; + } + else + { + LogMsg("myCFSocketCallBack from is unknown address family %d", from.ss_family); + return; + } + + // Even though we indicated a specific interface in the IP_ADD_MEMBERSHIP call, a weirdness of the + // sockets API means that even though this socket has only officially joined the multicast group + // on one specific interface, the kernel will still deliver multicast packets to it no matter which + // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug. + // To work around this weirdness, we use the IP_RECVIF option to find the name of the interface + // on which the packet arrived, and ignore the packet if it really arrived on some other interface. + if (strcmp(info->ifa_name, packetifname)) + { + verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s (Ignored -- really arrived on interface %s)", + &senderAddr, &destAddr, &info->ifinfo.ip, info->ifa_name, packetifname); + return; + } + else + verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s", + &senderAddr, &destAddr, &info->ifinfo.ip, info->ifa_name); + + if (err < (int)sizeof(DNSMessageHeader)) { debugf("myCFSocketCallBack packet length (%d) too short", err); return; } + + mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, &senderAddr, senderPort, &destAddr, destPort, info->ifinfo.InterfaceID, ttl); + } + + if (err < 0 && (errno != EWOULDBLOCK || count == 0)) + { + // Something is busted here. + // CFSocket says there is a packet, but myrecvfrom says there is not. + // Try calling select() to get another opinion. + // Find out about other socket parameter that can help understand why select() says the socket is ready for read + // All of this is racy, as data may have arrived after the call to select() + int save_errno = errno; + int so_error = -1; + int so_nread = -1; + int fionread = -1; + int solen = sizeof(int); + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(s1, &readfds); + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + int selectresult = select(s1+1, &readfds, NULL, NULL, &timeout); + if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1) + LogMsg("myCFSocketCallBack getsockopt(SO_ERROR) error %d", errno); + if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1) + LogMsg("myCFSocketCallBack getsockopt(SO_NREAD) error %d", errno); + if (ioctl(s1, FIONREAD, &fionread) == -1) + LogMsg("myCFSocketCallBack ioctl(FIONREAD) error %d", errno); + static unsigned int numLogMessages = 0; + if (numLogMessages++ < 100) + LogMsg("myCFSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d", + s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count); + sleep(1); // After logging this error, rate limit so we don't flood syslog + } + } + +// This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel +mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) + { + CFStringEncoding encoding = kCFStringEncodingUTF8; + CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); + if (cfs) + { + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); + } + } + +// This gets the text of the field currently labelled "Rendezvous Name" in the Sharing Prefs Control Panel +mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) + { + CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); + if (cfs) + { + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); + } + } + +mDNSlocal mStatus SetupSocket(NetworkInterfaceInfoOSX *i, mDNSIPPort port, int *s, CFSocketRef *c) + { + const int on = 1; + const int twofivefive = 255; + + if (*s >= 0) { LogMsg("SetupSocket ERROR: socket %d is already set", *s); return(-1); } + if (*c) { LogMsg("SetupSocket ERROR: CFSocketRef %p is already set", *c); return(-1); } + + // Open the socket... + int skt = socket(i->sa_family, SOCK_DGRAM, IPPROTO_UDP); + if (skt < 0) { LogMsg("socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } + + // ... with a shared UDP port + mStatus err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + if (err < 0) { LogMsg("setsockopt - SO_REUSEPORT error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + if (i->sa_family == AF_INET) + { + // We want to receive destination addresses + err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); + if (err < 0) { LogMsg("setsockopt - IP_RECVDSTADDR error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // We want to receive interface identifiers + err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err < 0) { LogMsg("setsockopt - IP_RECVIF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // We want to receive packet TTL value so we can check it + err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); + // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it + + // Add multicast group membership on this interface + struct in_addr addr = { i->ifinfo.ip.ip.v4.NotAnInteger }; + struct ip_mreq imr; + imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; + imr.imr_interface = addr; + err = setsockopt(skt, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); + if (err < 0) { LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // Specify outgoing interface too + err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)); + if (err < 0) { LogMsg("setsockopt - IP_MULTICAST_IF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // Send unicast packets with TTL 255 + err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); + if (err < 0) { LogMsg("setsockopt - IP_TTL error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // And multicast packets with TTL 255 too + err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); + if (err < 0) { LogMsg("setsockopt - IP_MULTICAST_TTL error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate + const int ip_tosbits = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; + err = setsockopt(skt, IPPROTO_IP, IP_TOS, &ip_tosbits, sizeof(ip_tosbits)); + if (err < 0) { LogMsg("setsockopt - IP_TOS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // And start listening for packets + struct sockaddr_in listening_sockaddr; + listening_sockaddr.sin_family = AF_INET; + listening_sockaddr.sin_port = port.NotAnInteger; + listening_sockaddr.sin_addr.s_addr = 0; // Want to receive multicasts AND unicasts on this socket + err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); + if (err) + { + // If we fail to bind to port 53 (because we're not root), that's okay, just tidy up and silently continue + if (port.NotAnInteger == UnicastDNSPort.NotAnInteger) { close(skt); err = 0; } + else LogMsg("bind error %ld errno %d (%s)", err, errno, strerror(errno)); + return(err); + } + } + else if (i->sa_family == AF_INET6) + { + // We want to receive destination addresses and receive interface identifiers + err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + if (err < 0) { LogMsg("setsockopt - IPV6_PKTINFO error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // We want to receive packet hop count value so we can check it + err = setsockopt(skt, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)); + if (err < 0) { LogMsg("setsockopt - IPV6_HOPLIMIT error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // We want to receive only IPv6 packets, without this option, we may + // get IPv4 addresses as mapped addresses. + err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (err < 0) { LogMsg("setsockopt - IPV6_V6ONLY error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // Add multicast group membership on this interface + int interface_id = if_nametoindex(i->ifa_name); + struct ipv6_mreq i6mr; + i6mr.ipv6mr_interface = interface_id; + i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroupv6; + err = setsockopt(skt, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); + if (err < 0) { LogMsg("setsockopt - IPV6_JOIN_GROUP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // Specify outgoing interface too + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interface_id, sizeof(interface_id)); + if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_IF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // Send unicast packets with TTL 255 + err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive)); + if (err < 0) { LogMsg("setsockopt - IPV6_UNICAST_HOPS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // And multicast packets with TTL 255 too + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive)); + if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_HOPS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // Note: IPV6_TCLASS appears not to be implemented on OS X right now (or indeed on ANY version of Unix?) + #ifdef IPV6_TCLASS + // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate + int tclass = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; // This may not be right (since tclass is not implemented on OS X, I can't test it) + err = setsockopt(skt, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass)); + if (err < 0) { LogMsg("setsockopt - IPV6_TCLASS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + #endif + + // Want to receive our own packets + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); + if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_LOOP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + + // And start listening for packets + struct sockaddr_in6 listening_sockaddr6; + bzero(&listening_sockaddr6, sizeof(listening_sockaddr6)); + listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6); + listening_sockaddr6.sin6_family = AF_INET6; + listening_sockaddr6.sin6_port = port.NotAnInteger; + listening_sockaddr6.sin6_flowinfo = 0; +// listening_sockaddr6.sin6_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket + listening_sockaddr6.sin6_scope_id = 0; + err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6)); + if (err) { LogMsg("bind error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } + } + + fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking + *s = skt; + CFSocketContext myCFSocketContext = { 0, i->ifinfo.InterfaceID, NULL, NULL, NULL }; + *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext); + CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + + return(err); + } + +mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) + { + if (sa->sa_family == AF_INET) + { + struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa; + ip->type = mDNSAddrType_IPv4; + ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr; + return(0); + } + else if (sa->sa_family == AF_INET6) + { + struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; + ip->type = mDNSAddrType_IPv6; + if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0; + ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; + return(0); + } + else + { + LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); + return(-1); + } + } + +mDNSlocal mStatus AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa) + { + mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); + mDNSAddr ip; + SetupAddr(&ip, ifa->ifa_addr); + NetworkInterfaceInfoOSX **p; + for (p = &m->p->InterfaceList; *p; p = &(*p)->next) + if (scope_id == (*p)->scope_id && mDNSSameAddress(&ip, &(*p)->ifinfo.ip)) + { + debugf("AddInterfaceToList: Found existing interface %u with address %#a", scope_id, &ip); + (*p)->CurrentlyActive = mDNStrue; + return(0); + } + + debugf("AddInterfaceToList: Making new interface %u with address %#a", scope_id, &ip); + NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i)); + if (!i) return(-1); + i->ifa_name = (char *)mallocL("NetworkInterfaceInfoOSX name", strlen(ifa->ifa_name) + 1); + if (!i->ifa_name) { freeL("NetworkInterfaceInfoOSX", i); return(-1); } + strcpy(i->ifa_name, ifa->ifa_name); + + i->ifinfo.InterfaceID = mDNSNULL; + i->ifinfo.ip = ip; + i->ifinfo.Advertise = m->AdvertiseLocalAddresses; + i->ifinfo.TxAndRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList + + i->next = mDNSNULL; + i->m = m; + i->scope_id = scope_id; + i->CurrentlyActive = mDNStrue; + i->sa_family = ifa->ifa_addr->sa_family; + #if mDNS_AllowPort53 + i->skt53 = -1; + i->cfs53 = NULL; + #endif + i->sktv4 = -1; + i->cfsv4 = NULL; + i->sktv6 = -1; + i->cfsv6 = NULL; + + if (!i->ifa_name) return(-1); + *p = i; + return(0); + } + +mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id) + { + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->CurrentlyActive && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4) + if (!(i->ifinfo.ip.ip.v4.b[0] == 169 && i->ifinfo.ip.ip.v4.b[1] == 254)) + return(i); + return(mDNSNULL); + } + +mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) + { + mDNSBool foundav4 = mDNSfalse; + struct ifaddrs *ifa = myGetIfAddrs(1); + struct ifaddrs *theLoopback = NULL; + int err = (ifa != NULL) ? 0 : (errno != 0 ? errno : -1); + int InfoSocket = err ? -1 : socket(AF_INET6, SOCK_DGRAM, 0); + if (err) return(err); + + // Set up the nice label + m->nicelabel.c[0] = 0; + GetUserSpecifiedFriendlyComputerName(&m->nicelabel); + if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); + + // Set up the RFC 1034-compliant label + domainlabel hostlabel; + hostlabel.c[0] = 0; + GetUserSpecifiedRFC1034ComputerName(&hostlabel); + if (hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&hostlabel, "Macintosh"); + // If the user has changed their dot-local host name since the last time we checked, then update our local copy. + // If the user has not changed their dot-local host name, then leave ours alone (m->hostlabel may have gone through + // repeated conflict resolution to get to its current value, and if we reset it, we'll have to go through all that again.) + if (SameDomainLabel(m->p->userhostlabel.c, hostlabel.c)) + debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c); + else + { + debugf("Updating m->hostlabel to %#s", hostlabel.c); + m->p->userhostlabel = m->hostlabel = hostlabel; + mDNS_GenerateFQDN(m); + } + + while (ifa) + { +#if LIST_ALL_INTERFACES + if (ifa->ifa_addr->sa_family == AF_APPLETALK) + debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_APPLETALK", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + else if (ifa->ifa_addr->sa_family == AF_LINK) + debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_LINK", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6) + debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (!(ifa->ifa_flags & IFF_UP)) + debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface not IFF_UP", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (ifa->ifa_flags & IFF_POINTOPOINT) + debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (ifa->ifa_flags & IFF_LOOPBACK) + debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); +#endif + if ((ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) && + (ifa->ifa_flags & IFF_MULTICAST) && + (ifa->ifa_flags & IFF_UP) && !(ifa->ifa_flags & IFF_POINTOPOINT)) + { + int ifru_flags6 = 0; + if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + struct in6_ifreq ifr6; + bzero((char *)&ifr6, sizeof(ifr6)); + strncpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name)); + ifr6.ifr_addr = *sin6; + if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1) + ifru_flags6 = ifr6.ifr_ifru.ifru_flags6; + verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6); + } + if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) + { + if (ifa->ifa_flags & IFF_LOOPBACK) + theLoopback = ifa; + else + { + AddInterfaceToList(m, ifa); + if (ifa->ifa_addr->sa_family == AF_INET) + foundav4 = mDNStrue; + } + } + } + ifa = ifa->ifa_next; + } + +// Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. +// In the interim, we skip loopback interface only if we found at least one v4 interface to use + if (!foundav4 && theLoopback) + AddInterfaceToList(m, theLoopback); + + // Now the list is complete, set the TxAndRx setting for each interface. + // We always send and receive using IPv4. + // To reduce traffic, we send and receive using IPv6 only on interfaces that have no routable IPv4 address. + // Having a routable IPv4 address assigned is a reasonable indicator of being on a large configured network, + // which means there's a good chance that most or all the other devices on that network should also have v4. + // By doing this we lose the ability to talk to true v6-only devices on that link, but we cut the packet rate in half. + // At this time, reducing the packet rate is more important than v6-only devices on a large configured network, + // so we are willing to make that sacrifice. + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->CurrentlyActive) + { + mDNSBool txrx = ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); + if (i->ifinfo.TxAndRx != txrx) + { + i->ifinfo.TxAndRx = txrx; + i->CurrentlyActive = 2; // State change; need to deregister and reregister this interface + } + } + + if (InfoSocket >= 0) close(InfoSocket); + return(err); + } + +mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, char *ifname, int type) + { + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (!strcmp(i->ifa_name, ifname) && + ((AAAA_OVER_V4 ) || + (type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) || + (type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6) )) return(i); + return(NULL); + } + +mDNSlocal void SetupActiveInterfaces(mDNS *const m) + { + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + { + mStatus err = 0; + NetworkInterfaceInfo *n = &i->ifinfo; + NetworkInterfaceInfoOSX *alias = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); + if (!alias) alias = i; + + if (n->InterfaceID && n->InterfaceID != (mDNSInterfaceID)alias) + { + LogMsg("SetupActiveInterfaces ERROR! n->InterfaceID %p != alias %p", n->InterfaceID, alias); + n->InterfaceID = mDNSNULL; + } + + if (!n->InterfaceID) + { + n->InterfaceID = (mDNSInterfaceID)alias; + mDNS_RegisterInterface(m, n); + debugf("SetupActiveInterfaces: Registered %s(%lu) InterfaceID %p %#a%s", + i->ifa_name, i->scope_id, alias, &n->ip, n->InterfaceActive ? " (Primary)" : ""); + } + + if (!n->TxAndRx) + debugf("SetupActiveInterfaces: No TX/Rx on %s(%lu) InterfaceID %p %#a", i->ifa_name, i->scope_id, alias, &n->ip); + else + { + if (i->sa_family == AF_INET && alias->sktv4 == -1) + { + #if mDNS_AllowPort53 + err = SetupSocket(i, UnicastDNSPort, &alias->skt53, &alias->cfs53); + #endif + if (!err) err = SetupSocket(i, MulticastDNSPort, &alias->sktv4, &alias->cfsv4); + if (err == 0) debugf("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a", alias->sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); + else LogMsg("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a FAILED", alias->sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); + } + + if (i->sa_family == AF_INET6 && alias->sktv6 == -1) + { + err = SetupSocket(i, MulticastDNSPort, &alias->sktv6, &alias->cfsv6); + if (err == 0) debugf("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a", alias->sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); + else LogMsg("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a FAILED", alias->sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); + } + } + } + } + +mDNSlocal void MarkAllInterfacesInactive(mDNS *const m) + { + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + i->CurrentlyActive = mDNSfalse; + } + +mDNSlocal void ClearInactiveInterfaces(mDNS *const m) + { + // First pass: + // If an interface is going away, then deregister this from the mDNSCore. + // We also have to deregister it if the alias interface that it's using for its InterfaceID is going away. + // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory + // it refers to has gone away we'll crash. + // Don't actually close the sockets or free the memory yet: When the last representative of an interface goes away + // mDNSCore may want to send goodbye packets on that interface. (Not yet implemented, but a good idea anyway.) + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + { + // 1. If this interface is no longer active, or it's InterfaceID is changing, deregister it + NetworkInterfaceInfoOSX *alias = (NetworkInterfaceInfoOSX *)(i->ifinfo.InterfaceID); + if (i->ifinfo.InterfaceID && (!i->CurrentlyActive || (alias && !alias->CurrentlyActive) || i->CurrentlyActive == 2)) + { + debugf("ClearInactiveInterfaces: Deregistering %#a", &i->ifinfo.ip); + mDNS_DeregisterInterface(m, &i->ifinfo); + i->ifinfo.InterfaceID = mDNSNULL; + } + } + + // Second pass: + // Now that everything that's going to deregister has done so, we can close sockets and free the memory + NetworkInterfaceInfoOSX **p = &m->p->InterfaceList; + while (*p) + { + i = *p; + // 2. Close all our CFSockets. We'll recreate them later as necessary. + // (We may have previously had both v4 and v6, and we may not need both any more.) + // Note: MUST NOT close the underlying native BSD sockets. + // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately, + // because it first has to unhook the sockets from its select() call, before it can safely close them. + #if mDNS_AllowPort53 + if (i->cfs53) { CFSocketInvalidate(i->cfs53); CFRelease(i->cfs53); } + i->skt53 = -1; + i->cfs53 = NULL; + #endif + if (i->cfsv4) { CFSocketInvalidate(i->cfsv4); CFRelease(i->cfsv4); } + if (i->cfsv6) { CFSocketInvalidate(i->cfsv6); CFRelease(i->cfsv6); } + i->sktv4 = i->sktv6 = -1; + i->cfsv4 = i->cfsv6 = NULL; + + // 3. If no longer active, delete interface from list and free memory + if (!i->CurrentlyActive) + { + debugf("ClearInactiveInterfaces: Deleting %#a", &i->ifinfo.ip); + *p = i->next; + if (i->ifa_name) freeL("NetworkInterfaceInfoOSX name", i->ifa_name); + freeL("NetworkInterfaceInfoOSX", i); + } + else + p = &i->next; + } + } + +mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) + { + (void)store; // Parameter not used + (void)changedKeys; // Parameter not used + debugf("*** Network Configuration Change ***"); + + mDNS *const m = (mDNS *const)context; + MarkAllInterfacesInactive(m); + UpdateInterfaceList(m); + ClearInactiveInterfaces(m); + SetupActiveInterfaces(m); + + if (m->MainCallback) + m->MainCallback(m, mStatus_ConfigChanged); + } + +mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) + { + mStatus err = -1; + SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder"), NetworkChanged, &context); + CFStringRef key1 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); + CFStringRef key2 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); + CFStringRef key3 = SCDynamicStoreKeyCreateComputerName(NULL); + CFStringRef key4 = SCDynamicStoreKeyCreateHostNames(NULL); + CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); + + CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + if (!store) { LogMsg("SCDynamicStoreCreate failed: %s\n", SCErrorString(SCError())); goto error; } + if (!key1 || !key2 || !key3 || !key4 || !keys || !pattern1 || !pattern2 || !patterns) goto error; + + CFArrayAppendValue(keys, key1); + CFArrayAppendValue(keys, key2); + CFArrayAppendValue(keys, key3); + CFArrayAppendValue(keys, key4); + CFArrayAppendValue(patterns, pattern1); + CFArrayAppendValue(patterns, pattern2); + if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) + { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError())); goto error; } + + m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); + if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s\n", SCErrorString(SCError())); goto error; } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); + m->p->Store = store; + err = 0; + goto exit; + +error: + if (store) CFRelease(store); + +exit: + if (key1) CFRelease(key1); + if (key2) CFRelease(key2); + if (key3) CFRelease(key3); + if (key4) CFRelease(key4); + if (pattern1) CFRelease(pattern1); + if (pattern2) CFRelease(pattern2); + if (keys) CFRelease(keys); + if (patterns) CFRelease(patterns); + + return(err); + } + +mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) + { + mDNS *const m = (mDNS *const)refcon; + (void)service; // Parameter not used + switch(messageType) + { + case kIOMessageCanSystemPowerOff: debugf("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 + case kIOMessageSystemWillPowerOff: debugf("PowerChanged kIOMessageSystemWillPowerOff"); mDNSCoreMachineSleep(m, true); break; // E0000250 + case kIOMessageSystemWillNotPowerOff: debugf("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 + case kIOMessageCanSystemSleep: debugf("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 + case kIOMessageSystemWillSleep: debugf("PowerChanged kIOMessageSystemWillSleep"); mDNSCoreMachineSleep(m, true); break; // E0000280 + case kIOMessageSystemWillNotSleep: debugf("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 + case kIOMessageSystemHasPoweredOn: debugf("PowerChanged kIOMessageSystemHasPoweredOn"); mDNSCoreMachineSleep(m, false); break; // E0000300 + default: debugf("PowerChanged unknown message %X", messageType); break; + } + IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); + } + +mDNSlocal mStatus WatchForPowerChanges(mDNS *const m) + { + IONotificationPortRef thePortRef; + m->p->PowerConnection = IORegisterForSystemPower(m, &thePortRef, PowerChanged, &m->p->PowerNotifier); + if (m->p->PowerConnection) + { + m->p->PowerRLS = IONotificationPortGetRunLoopSource(thePortRef); + CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); + return(mStatus_NoError); + } + return(-1); + } + +CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); +CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; +CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; +CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; + +mDNSexport mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) + { + int major = 0, minor = 0; + char letter = 0, prodname[256]="Mac OS X", prodvers[256]="", buildver[256]="?"; + CFDictionaryRef vers = _CFCopySystemVersionDictionary(); + if (vers) + { + CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey); + CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey); + CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); + if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); + if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); + if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8); + sscanf(buildver, "%d%c%d", &major, &letter, &minor); + CFRelease(vers); + } + if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, mDNSResponderVersionString); + return(major); + } + +mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) + { + mStatus err; + + m->hostlabel.c[0] = 0; + + char *HINFO_HWstring = "Macintosh"; + char HINFO_HWstring_buffer[256]; + int get_model[2] = { CTL_HW, HW_MODEL }; + size_t len_model = sizeof(HINFO_HWstring_buffer); + if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0) + HINFO_HWstring = HINFO_HWstring_buffer; + + char HINFO_SWstring[256] = ""; + if (mDNSMacOSXSystemBuildNumber(HINFO_SWstring) < 7) m->KnownBugs = mDNS_KnownBug_PhantomInterfaces; + + mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); + mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); + if (hlen + slen < 254) + { + m->HIHardware.c[0] = hlen; + m->HISoftware.c[0] = slen; + mDNSPlatformMemCopy(HINFO_HWstring, &m->HIHardware.c[1], hlen); + mDNSPlatformMemCopy(HINFO_SWstring, &m->HISoftware.c[1], slen); + } + + m->p->InterfaceList = mDNSNULL; + m->p->userhostlabel.c[0] = 0; + UpdateInterfaceList(m); + SetupActiveInterfaces(m); + + err = WatchForNetworkChanges(m); + if (err) return(err); + + err = WatchForPowerChanges(m); + return(err); + } + +mDNSexport mStatus mDNSPlatformInit(mDNS *const m) + { + mStatus result = mDNSPlatformInit_setup(m); + // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already + // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately + if (result == mStatus_NoError) mDNSCoreInitComplete(m, mStatus_NoError); + return(result); + } + +mDNSexport void mDNSPlatformClose(mDNS *const m) + { + if (m->p->PowerConnection) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PowerRLS, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(m->p->PowerRLS); + CFRelease(m->p->PowerRLS); + IODeregisterForSystemPower(&m->p->PowerNotifier); + m->p->PowerConnection = NULL; + m->p->PowerNotifier = NULL; + m->p->PowerRLS = NULL; + } + + if (m->p->Store) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(m->p->StoreRLS); + CFRelease(m->p->StoreRLS); + CFRelease(m->p->Store); + m->p->Store = NULL; + m->p->StoreRLS = NULL; + } + + MarkAllInterfacesInactive(m); + ClearInactiveInterfaces(m); + } + +mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; + +mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) + { + // Notes: Typical values for mach_timebase_info: + // tbi.numer = 1000 million + // tbi.denom = 33 million + // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds; + // numer / denom = nanoseconds per hardware clock tick (e.g. 30); + // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033) + // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333) + // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds + // + // Arithmetic notes: + // tbi.denom is at least 1, and not more than 2^32-1. + // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t. + // tbi.denom is at least 1, and not more than 2^32-1. + // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9. + // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz, + // which is unlikely on any current or future Macintosh. + // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz. + // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code. + struct mach_timebase_info tbi; + kern_return_t result = mach_timebase_info(&tbi); + if (result != KERN_SUCCESS) return(result); + clockdivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer; + *timenow = mDNSPlatformTimeNow(); + return(mStatus_NoError); + } + +mDNSexport mDNSs32 mDNSPlatformTimeNow(void) + { + if (clockdivisor == 0) { LogMsg("mDNSPlatformTimeNow called before mDNSPlatformTimeInit"); return(0); } + return((mDNSs32)(mach_absolute_time() / clockdivisor)); + } + +// Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves +mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; } +mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; } +mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { strcpy((char *)dst, (char *)src); } +mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) { return(strlen((char*)src)); } +mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len) { memcpy(dst, src, len); } +mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len) { return(memcmp(dst, src, len) == 0); } +mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) { bzero(dst, len); } +mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); } +mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); } diff --git a/mDNSMacOSX/CFSocketPuma.c b/mDNSMacOSX/CFSocketPuma.c new file mode 100644 index 0000000..e0cf87c --- /dev/null +++ b/mDNSMacOSX/CFSocketPuma.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * This file is not normally used. + * It can be conditionally compiled in by defining RUN_ON_PUMA_WITHOUT_IFADDRS + * in CFSocket.c. It is included mainly as sample code for people building + * for other platforms that (like Puma) lack the getifaddrs() call. + * NOTE: YOU CANNOT use this code to build an mDNSResponder daemon for Puma + * that works just like the Jaguar one, because Puma lacks other necessary + * functionality (like the LibInfo support to receive MIG messages from clients). + + Change History (most recent first): + +$Log: CFSocketPuma.c,v $ +Revision 1.4 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + +Revision 1.3 2003/07/02 21:19:51 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.2 2002/09/21 20:44:51 zarzycki +Added APSL info + +Revision 1.1 2002/09/17 01:36:23 cheshire +Move Puma support to CFSocketPuma.c + + */ + +#include +#include +#define ifaddrs ifa_info +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ +#endif +#include + +/* Our own header for the programs that need interface configuration info. + Include this file, instead of "unp.h". */ + +#define IFA_NAME 16 /* same as IFNAMSIZ in */ +#define IFA_HADDR 8 /* allow for 64-bit EUI-64 in future */ + +struct ifa_info { + char ifa_name[IFA_NAME]; /* interface name, null terminated */ + u_char ifa_haddr[IFA_HADDR]; /* hardware address */ + u_short ifa_hlen; /* #bytes in hardware address: 0, 6, 8 */ + short ifa_flags; /* IFF_xxx constants from */ + short ifa_myflags; /* our own IFI_xxx flags */ + struct sockaddr *ifa_addr; /* primary address */ + struct sockaddr *ifa_brdaddr;/* broadcast address */ + struct sockaddr *ifa_dstaddr;/* destination address */ + struct ifa_info *ifa_next; /* next of these structures */ +}; + +#define IFI_ALIAS 1 /* ifa_addr is an alias */ + + /* function prototypes */ +struct ifa_info *get_ifa_info(int, int); +struct ifa_info *Get_ifa_info(int, int); +void free_ifa_info(struct ifa_info *); + +#define HAVE_SOCKADDR_SA_LEN 1 + +struct ifa_info * +get_ifa_info(int family, int doaliases) +{ + struct ifa_info *ifi, *ifihead, **ifipnext; + int sockfd, len, lastlen, flags, myflags; + char *ptr, *buf, lastname[IFNAMSIZ], *cptr; + struct ifconf ifc; + struct ifreq *ifr, ifrcopy; + struct sockaddr_in *sinptr; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + lastlen = 0; + len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ + for ( ; ; ) { + buf = (char *) malloc(len); + ifc.ifc_len = len; + ifc.ifc_buf = buf; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { + if (errno != EINVAL || lastlen != 0) + debugf("ioctl error"); + } else { + if (ifc.ifc_len == lastlen) + break; /* success, len has not changed */ + lastlen = ifc.ifc_len; + } + len += 10 * sizeof(struct ifreq); /* increment */ + free(buf); + } + ifihead = NULL; + ifipnext = &ifihead; + lastname[0] = 0; +/* end get_ifa_info1 */ + +/* include get_ifa_info2 */ + for (ptr = buf; ptr < buf + ifc.ifc_len; ) { + ifr = (struct ifreq *) ptr; + +#ifdef HAVE_SOCKADDR_SA_LEN + len = MAX(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); +#else + switch (ifr->ifr_addr.sa_family) { +#ifdef IPV6 + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; +#endif + case AF_INET: + default: + len = sizeof(struct sockaddr); + break; + } +#endif /* HAVE_SOCKADDR_SA_LEN */ + ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ + + if (ifr->ifr_addr.sa_family != family) + continue; /* ignore if not desired address family */ + + myflags = 0; + if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) + *cptr = 0; /* replace colon will null */ + if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { + if (doaliases == 0) + continue; /* already processed this interface */ + myflags = IFI_ALIAS; + } + memcpy(lastname, ifr->ifr_name, IFNAMSIZ); + + ifrcopy = *ifr; + ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); + flags = ifrcopy.ifr_flags; + if ((flags & IFF_UP) == 0) + continue; /* ignore if interface not up */ + + ifi = (struct ifa_info *) calloc(1, sizeof(struct ifa_info)); + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifa_next; /* pointer to next one goes here */ + + ifi->ifa_flags = flags; /* IFF_xxx values */ + ifi->ifa_myflags = myflags; /* IFI_xxx values */ + memcpy(ifi->ifa_name, ifr->ifr_name, IFA_NAME); + ifi->ifa_name[IFA_NAME-1] = '\0'; +/* end get_ifa_info2 */ +/* include get_ifa_info3 */ + switch (ifr->ifr_addr.sa_family) { + case AF_INET: + sinptr = (struct sockaddr_in *) &ifr->ifr_addr; + if (ifi->ifa_addr == NULL) { + ifi->ifa_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); + memcpy(ifi->ifa_addr, sinptr, sizeof(struct sockaddr_in)); + +#ifdef SIOCGIFBRDADDR + if (flags & IFF_BROADCAST) { + ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; + ifi->ifa_brdaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); + memcpy(ifi->ifa_brdaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + +#ifdef SIOCGIFDSTADDR + if (flags & IFF_POINTOPOINT) { + ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; + ifi->ifa_dstaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); + memcpy(ifi->ifa_dstaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + } + break; + + default: + break; + } + } + free(buf); + return(ifihead); /* pointer to first structure in linked list */ +} +/* end get_ifa_info3 */ + +/* include free_ifa_info */ +mDNSlocal void freeifaddrs(struct ifa_info *ifihead) +{ + struct ifa_info *ifi, *ifinext; + + for (ifi = ifihead; ifi != NULL; ifi = ifinext) { + if (ifi->ifa_addr != NULL) + free(ifi->ifa_addr); + if (ifi->ifa_brdaddr != NULL) + free(ifi->ifa_brdaddr); + if (ifi->ifa_dstaddr != NULL) + free(ifi->ifa_dstaddr); + ifinext = ifi->ifa_next; /* can't fetch ifa_next after free() */ + free(ifi); /* the ifa_info{} itself */ + } +} +/* end free_ifa_info */ + +struct ifa_info * +Get_ifa_info(int family, int doaliases) +{ + struct ifa_info *ifi; + + if ( (ifi = get_ifa_info(family, doaliases)) == NULL) + debugf("get_ifa_info error"); + return(ifi); +} + +mDNSlocal int getifaddrs(struct ifa_info **ifalist) + { + *ifalist = get_ifa_info(PF_INET, false); + if( ifalist == nil ) + return -1; + else + return(0); + } diff --git a/mDNSMacOSX/DNSServiceDiscoveryDefines.h b/mDNSMacOSX/DNSServiceDiscoveryDefines.h new file mode 100644 index 0000000..b6d3a9a --- /dev/null +++ b/mDNSMacOSX/DNSServiceDiscoveryDefines.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSServiceDiscoveryDefines.h,v $ +Revision 1.5 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#ifndef __DNS_SERVICE_DISCOVERY_DEFINES_H +#define __DNS_SERVICE_DISCOVERY_DEFINES_H + +#include + +#define DNS_SERVICE_DISCOVERY_SERVER "DNSServiceDiscoveryServer" + +typedef char DNSCString[1024]; +typedef char sockaddr_t[128]; + +typedef const char * record_data_t; + +#endif /* __DNS_SERVICE_DISCOVERY_DEFINES_H */ diff --git a/mDNSMacOSX/DNSServiceDiscoveryReply.defs b/mDNSMacOSX/DNSServiceDiscoveryReply.defs new file mode 100644 index 0000000..942fb6b --- /dev/null +++ b/mDNSMacOSX/DNSServiceDiscoveryReply.defs @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +subsystem + DNSServiceDiscoveryReply 7250; + +ServerPrefix internal_; + +#include +#include + +import "DNSServiceDiscoveryDefines.h"; + +type DNSCString = c_string[*:1024]; +type sockaddr_t = array[128] of char; + +simpleroutine DNSServiceDomainEnumerationReply_rpc( + reply: mach_port_t; + in resultType: int; + in replyDomain: DNSCString; + in flags: int; + SendTime to: natural_t); + +simpleroutine DNSServiceBrowserReply_rpc( + reply: mach_port_t; + in resultType: int; + in replyName: DNSCString; + in replyType: DNSCString; + in replyDomain: DNSCString; + in flags: int; + SendTime to: natural_t); + + +simpleroutine DNSServiceRegistrationReply_rpc( + reply: mach_port_t; + in resultType: int; + SendTime to: natural_t); + + +simpleroutine DNSServiceResolverReply_rpc( + reply: mach_port_t; + in interface: sockaddr_t; + in address: sockaddr_t; + in txtRecord: DNSCString; + in flags: int; + SendTime to: natural_t); diff --git a/mDNSMacOSX/DNSServiceDiscoveryRequest.defs b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs new file mode 100644 index 0000000..eb400b1 --- /dev/null +++ b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +subsystem + DNSServiceDiscoveryRequest 7200; + +ServerPrefix provide_; + +#include +#include + +import "DNSServiceDiscoveryDefines.h"; + +type DNSCString = c_string[*:1024]; +type record_data = ^ array [] of MACH_MSG_TYPE_BYTE + ctype: record_data_t; + +simpleroutine DNSServiceBrowserCreate_rpc( + server: mach_port_t; + in client: mach_port_t; + in regtype: DNSCString; + in domain: DNSCString); + + +simpleroutine DNSServiceDomainEnumerationCreate_rpc( + server: mach_port_t; + in client: mach_port_t; + in registrationDomains: int); + +simpleroutine DNSServiceRegistrationCreate_rpc( + server: mach_port_t; + in client: mach_port_t; + in name: DNSCString; + in regtype: DNSCString; + in domain: DNSCString; + in port: int; + in txtRecord: DNSCString); + + +simpleroutine DNSServiceResolverResolve_rpc( + server: mach_port_t; + in client: mach_port_t; + in name: DNSCString; + in regtype: DNSCString; + in domain: DNSCString); + +routine DNSServiceRegistrationAddRecord_rpc( + server: mach_port_t; + in client: mach_port_t; + in record_type: int; + in record_data: record_data; + in ttl: uint32_t; + out record_reference: natural_t); + +simpleroutine DNSServiceRegistrationUpdateRecord_rpc( + server: mach_port_t; + in client: mach_port_t; + in record_reference: natural_t; + in record_data: record_data; + in ttl: uint32_t); + +simpleroutine DNSServiceRegistrationRemoveRecord_rpc( + server: mach_port_t; + in client: mach_port_t; + in record_reference: natural_t); diff --git a/mDNSMacOSX/SampleUDSClient.c b/mDNSMacOSX/SampleUDSClient.c new file mode 100755 index 0000000..7838fc1 --- /dev/null +++ b/mDNSMacOSX/SampleUDSClient.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SampleUDSClient.c,v $ +Revision 1.7 2003/08/18 18:50:15 cheshire +Can now give "-lo" as first parameter, to test "local only" mode + +Revision 1.6 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#include +#include +#include // include Mach API to ensure no conflicts exist +#include +#include +#include +#include +#include +#define BIND_8_COMPAT 1 +#include +// T_SRV is not defined in older versions of nameser.h +#ifndef T_SRV +#define T_SRV 33 +#endif + +// constants +#define MAX_DOMAIN_LABEL 63 +#define MAX_DOMAIN_NAME 255 +#define MAX_CSTRING 2044 + + +// data structure defs +typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; + +typedef struct { u_char c[ 64]; } domainlabel; +typedef struct { u_char c[256]; } domainname; + + +typedef struct + { + uint16_t priority; + uint16_t weight; + uint16_t port; + domainname target; + } srv_rdata; + + +// private function prototypes +static void sighdlr(int signo); +static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc); +static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc); +//static void MyCallbackWrapper(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *i, void *context); +static void print_rdata(int type, int len, const u_char *rdata); +static void query_cb(const DNSServiceRef DNSServiceRef, const DNSServiceFlags flags, const u_int32_t interfaceIndex, const DNSServiceErrorType errorCode, const char *name, const u_int16_t rrtype, const u_int16_t rrclass, const u_int16_t rdlen, const void *rdata, const u_int32_t ttl, void *context); +static void resolve_cb(const DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context); +static void my_enum_cb( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context); +static void my_regecordcb(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context); +static void browse_cb(DNSServiceRef sdr, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err, const char *serviceName, const char *regtype, const char *domain, void *context); + + +// globals +static DNSServiceRef sdr = NULL; +static uint32_t InterfaceIndex = 0; + +static void regservice_cb(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) + { + #pragma unused (sdRef, flags, errorCode, context) + printf("regservice_cb %s %s %s\n", name, regtype, domain); + } + +int main (int argc, char * argv[]) { + int err, t, i; + char *name, *type, *domain; + DNSServiceFlags flags; + DNSRecordRef recordrefs[10]; + char host[256]; + int ipaddr = 12345; // random IP address + + char full[1024]; + + // First parameter "-lo" means "local only" + if (!strcmp(argv[1], "-lo")) { InterfaceIndex = -1; argv++; argc--; } + + if (signal(SIGINT, sighdlr) == SIG_ERR) fprintf(stderr, "ERROR - can't catch interupt!\n"); + if (argc < 2) exit(1); + + if (!strcmp(argv[1], "-regrecord")) + { + err = DNSServiceCreateConnection(&sdr); + if (err) + { + printf("DNSServiceCreateConnection returned %d\n", err); + exit(1); + } + printf("registering 10 address records...\n"); + for (i = 0; i < 10; i++) + { + sprintf(host, "testhost-%d.local.", i); + ipaddr++; + err = DNSServiceRegisterRecord(sdr, &recordrefs[i], kDNSServiceFlagsUnique, InterfaceIndex, + host, 1, 1, 4, &ipaddr, 60, my_regecordcb, NULL); + if (err) + { + printf("DNSServiceRegisterRecord returned error %d\n", err); + exit(1); + } + } + printf("processing results...\n"); + for (i = 0; i < 10; i++) DNSServiceProcessResult(sdr); + printf("deregistering half of the records\n"); + for (i = 0; i < 10; i++) + { + if (i % 2) + { + err = DNSServiceRemoveRecord(sdr, recordrefs[i], 0); + if (err) + { + printf("DNSServiceRemoveRecord returned error %d\n" ,err); + exit(1); + } + } + } + printf("sleeping 10...\n"); + sleep(10); + printf("deregistering all remaining records\n");; + DNSServiceRefDeallocate(sdr); + printf("done. sleeping 10..\n"); + sleep(10); + exit(1); + } + + if (!strcmp(argv[1], "-browse")) + { + if (argc < 3) exit(1); + err = DNSServiceBrowse(&sdr, 0, InterfaceIndex, argv[2], NULL /*"local."*/, browse_cb, NULL); + if (err) + { + printf("DNSServiceBrowse returned error %d\n", err); + exit(1); + } + while(1) DNSServiceProcessResult(sdr); + } + + if (!strcmp(argv[1], "-enum")) + { + if (!strcmp(argv[2], "browse")) flags = kDNSServiceFlagsBrowseDomains; + else if (!strcmp(argv[2], "register")) flags = kDNSServiceFlagsRegistrationDomains; + else exit(1); + + err = DNSServiceEnumerateDomains(&sdr, flags, InterfaceIndex, my_enum_cb, NULL); + if (err) + { + printf("EnumerateDomains returned error %d\n", err); + exit(1); + } + while(1) DNSServiceProcessResult(sdr); + } + if (!strcmp(argv[1], "-query")) + { + t = atol(argv[5]); + err = DNSServiceConstructFullName(full, argv[2], argv[3], argv[4]); + if (err) exit(1); + printf("resolving fullname %s type %d\n", full, t); + err = DNSServiceQueryRecord(&sdr, 0, 0, full, t, 1, query_cb, NULL); + while (1) DNSServiceProcessResult(sdr); + } + + if (!strcmp(argv[1], "-regservice")) + { + char *regtype = "_http._tcp"; + char txtstring[] = "\x0DMy Txt Record"; + if (argc > 2) name = argv[2]; + else name = NULL; + if (argc > 3) regtype = argv[3]; + uint16_t PortAsNumber = 123; + if (argc > 4) PortAsNumber = atoi(argv[4]); + Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; + err = DNSServiceRegister(&sdr, 0, InterfaceIndex, name, regtype, "local.", NULL, registerPort.NotAnInteger, sizeof(txtstring)-1, txtstring, regservice_cb, NULL); + if (err) + { + printf("DNSServiceRegister returned error %d\n", err); + exit(1); + } + while (1) DNSServiceProcessResult(sdr); + } + if (!strcmp(argv[1], "-resolve")) + { + name = argv[2]; + type = argv[3]; + domain = argv[4]; + err = DNSServiceResolve(&sdr, 0, InterfaceIndex, name, type, domain, resolve_cb, NULL); + if (err) + { + printf("DNSServiceResolve returned error %d\n", err); + exit(1); + } + while(1) DNSServiceProcessResult(sdr); + } + exit(1); + } + + + +// callbacks + +// wrapper to make callbacks fit CFRunLoop callback signature +/* +static void MyCallbackWrapper(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *i, void *context) + { + (void)sr; + (void)t; + (void)dr; + (void)i; + + DNSServiceRef *sdr = context; + DNSServiceDiscoveryProcessResult(*sdr); + } +*/ + +static void browse_cb(DNSServiceRef sdr, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err, const char *serviceName, const char *regtype, const char *domain, void *context) + { + #pragma unused(sdr, ifi, context) + + if (err) + { + printf("Callback: error %d\n", err); + return; + } + printf("BrowseCB: %s %s %s %s (%s)\n", serviceName, regtype, domain, (flags & kDNSServiceFlagsMoreComing ? "(more coming)" : ""), flags & kDNSServiceFlagsAdd ? "(ADD)" : "(REMOVE)"); + + } + +static void my_enum_cb( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context) + { + #pragma unused(sdRef, context) + char *type; + if (flags == kDNSServiceFlagsAdd) type = "add"; + else if (flags == kDNSServiceFlagsRemove) type = "remove"; + else if (flags == (kDNSServiceFlagsAdd | kDNSServiceFlagsDefault)) type = "add default"; + else type = "unknown"; + + + if (errorCode) printf("EnumerateDomainsCB: error code %d\n", errorCode); + else printf("%s domain %s on interface %d\n", type, replyDomain, interfaceIndex); + } + +static void query_cb(const DNSServiceRef DNSServiceRef, const DNSServiceFlags flags, const u_int32_t interfaceIndex, const DNSServiceErrorType errorCode, const char *name, const u_int16_t rrtype, const u_int16_t rrclass, const u_int16_t rdlen, const void *rdata, const u_int32_t ttl, void *context) + { + (void)DNSServiceRef; + (void)flags; + (void)interfaceIndex; + (void)rrclass; + (void)ttl; + (void)context; + + if (errorCode) + { + printf("query callback: error==%d\n", errorCode); + return; + } + printf("query callback - name = %s, rdata=\n", name); + print_rdata(rrtype, rdlen, rdata); + } + +static void resolve_cb(const DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context) + { + int i; + + #pragma unused(sdRef, flags, interfaceIndex, errorCode, context, txtRecord) + printf("Resolved %s to %s:%d (%d bytes txt data)\n", fullname, hosttarget, port, txtLen); + printf("TXT Data:\n"); + for (i = 0; i < txtLen; i++) + if (txtRecord[i] >= ' ') printf("%c", txtRecord[i]); + } + + + +static void my_regecordcb(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context) + { + #pragma unused (sdRef, RecordRef, flags, context) + if (errorCode) printf("regrecord CB received error %d\n", errorCode); + else printf("regrecord callback - no errors\n"); + } + + +// resource record data interpretation routines +static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) + { + const u_char * src = label->c; // Domain label we're reading + const u_char len = *src++; // Read length of this (non-null) label + const u_char *const end = src + len; // Work out where the label ends + if (len > MAX_DOMAIN_LABEL) return(NULL); // If illegal label, abort + while (src < end) // While we have characters in the label + { + u_char c = *src++; + if (esc) + { + if (c == '.') // If character is a dot, + *ptr++ = esc; // Output escape character + else if (c <= ' ') // If non-printing ascii, + { // Output decimal escape sequence + *ptr++ = esc; + *ptr++ = (char) ('0' + (c / 100) ); + *ptr++ = (char) ('0' + (c / 10) % 10); + c = (u_char)('0' + (c ) % 10); + } + } + *ptr++ = (char)c; // Copy the character + } + *ptr = 0; // Null-terminate the string + return(ptr); // and return + } + +static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) + { + const u_char *src = name->c; // Domain name we're reading + const u_char *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid + + if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot + + while (*src) // While more characters in the domain name + { + if (src + 1 + *src >= max) return(NULL); + ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); + if (!ptr) return(NULL); + src += 1 + *src; + *ptr++ = '.'; // Write the dot after the label + } + + *ptr++ = 0; // Null-terminate the string + return(ptr); // and return + } + +// print arbitrary rdata in a readable manned +static void print_rdata(int type, int len, const u_char *rdata) + { + int i; + srv_rdata *srv; + char targetstr[MAX_CSTRING]; + struct in_addr in; + + switch (type) + { + case T_TXT: + // print all the alphanumeric and punctuation characters + for (i = 0; i < len; i++) + if (rdata[i] >= 32 && rdata[i] <= 127) printf("%c", rdata[i]); + printf("\n"); + return; + case T_SRV: + srv = (srv_rdata *)rdata; + ConvertDomainNameToCString_withescape(&srv->target, targetstr, 0); + printf("pri=%d, w=%d, port=%d, target=%s\n", srv->priority, srv->weight, srv->port, targetstr); + return; + case T_A: + assert(len == 4); + memcpy(&in, rdata, sizeof(in)); + printf("%s\n", inet_ntoa(in)); + return; + case T_PTR: + ConvertDomainNameToCString_withescape((domainname *)rdata, targetstr, 0); + printf("%s\n", targetstr); + return; + default: + printf("ERROR: I dont know how to print RData of type %d\n", type); + return; + } + } + + + + +// signal handlers, setup/teardown, etc. +static void sighdlr(int signo) + { + assert(signo == SIGINT); + fprintf(stderr, "Received sigint - deallocating serviceref and exiting\n"); + if (sdr) + DNSServiceRefDeallocate(sdr); + exit(1); + } + + + + + + + diff --git a/SamplemDNSClient.c b/mDNSMacOSX/SamplemDNSClient.c similarity index 64% rename from SamplemDNSClient.c rename to mDNSMacOSX/SamplemDNSClient.c index 8f14f2d..f905bd7 100644 --- a/SamplemDNSClient.c +++ b/mDNSMacOSX/SamplemDNSClient.c @@ -1,26 +1,25 @@ /* - * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * * @APPLE_LICENSE_HEADER_END@ - */ - -/* + * * Formatting notes: * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion * on C indentation can be found on the web, such as , @@ -33,10 +32,34 @@ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't * understand why variable y is not of type "char*" just proves the point that poor code * layout leads people to unfortunate misunderstandings about how the C language really works.) + + Change History (most recent first): + +$Log: SamplemDNSClient.c,v $ +Revision 1.39 2003/08/18 19:05:45 cheshire + UpdateRecord not working right +Added "newrdlength" field to hold new length of updated rdata + +Revision 1.38 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + +Revision 1.37 2003/08/05 20:39:25 cheshire + mDNS buffered std out makes it impossible to use from another tool +Added "setlinebuf(stdout);" + +Revision 1.36 2003/07/19 03:23:13 cheshire + mDNSResponder needs to receive and cache larger records + +Revision 1.35 2003/07/11 01:57:18 cheshire +Add checkin history header + */ #include +#define BIND_8_COMPAT #include +#include +#include #include #include @@ -47,11 +70,12 @@ typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; static char operation; static dns_service_discovery_ref client = NULL; +static int num_printed; static char addtest = 0; static DNSRecordReference record; static char myhinfo9[11] = "\003Mac\006OS 9.2"; static char myhinfoX[ 9] = "\003Mac\004OS X"; -static char updatetest[2] = "\001A"; +static char updatetest[3] = "\002AA"; static char bigNULL[4096]; //************************************************************************************************************* @@ -71,6 +95,9 @@ static char bigNULL[4096]; static void MyHandleMachMessage(CFMachPortRef port, void *msg, CFIndex size, void *info) { + (void)port; // Unused + (void)size; // Unused + (void)info; // Unused DNSServiceDiscovery_handleReply(msg); } @@ -94,6 +121,15 @@ static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref client) //************************************************************************************************************* // Sample callback functions for each of the operation types +static void printtimestamp(void) + { + struct timeval tv; + struct tm tm; + gettimeofday(&tv, NULL); + localtime_r((time_t*)&tv.tv_sec, &tm); + printf("%d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec/1000); + } + #define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \ (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \ (X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown") @@ -101,6 +137,8 @@ static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref client) static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context) { + (void)context; // Unused + printtimestamp(); printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(resultType)); if (flags) printf(" Flags: %X", flags); printf("\n"); @@ -109,6 +147,8 @@ static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context) { + (void)context; // Unused + printtimestamp(); printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(resultType)); if (flags) printf(" Flags: %X", flags); printf("\n"); @@ -117,35 +157,74 @@ static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultTyp static void browse_reply(DNSServiceBrowserReplyResultType resultType, const char *replyName, const char *replyType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context) { - char *op = (resultType == DNSServiceBrowserReplyAddInstance) ? "Found" : "Removed"; - printf("Service \"%s\", type \"%s\", domain \"%s\" %s", replyName, replyType, replyDomain, op); - if (flags) printf(" Flags: %X", flags); - printf("\n"); + char *op = (resultType == DNSServiceBrowserReplyAddInstance) ? "Add" : "Rmv"; + (void)context; // Unused + if (num_printed++ == 0) printf("A/R Flags %-8s %-20s %s\n", "Domain", "Service Type", "Instance Name"); + printtimestamp(); + printf("%s%6X %-8s %-20s %s\n", op, flags, replyDomain, replyType, replyName); } static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context) { - if (address->sa_family != AF_INET) + (void)interface; // Unused + (void)context; // Unused + if (address->sa_family != AF_INET && address->sa_family != AF_INET6) printf("Unknown address family %d\n", address->sa_family); else { - struct sockaddr_in *ip = (struct sockaddr_in *)address; - union { uint32_t l; u_char b[4]; } addr = { ip->sin_addr.s_addr }; - union { uint16_t s; u_char b[2]; } port = { ip->sin_port }; - uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; const char *src = txtRecord; - printf("Service can be reached at %d.%d.%d.%d:%u", addr.b[0], addr.b[1], addr.b[2], addr.b[3], PortAsNumber); - while (*src) + printtimestamp(); + + if (address->sa_family == AF_INET) { - char txtInfo[256]; + struct sockaddr_in *ip = (struct sockaddr_in *)address; + union { uint32_t l; u_char b[4]; } addr = { ip->sin_addr.s_addr }; + union { uint16_t s; u_char b[2]; } port = { ip->sin_port }; + uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; + char ipstring[16]; + sprintf(ipstring, "%d.%d.%d.%d", addr.b[0], addr.b[1], addr.b[2], addr.b[3]); + printf("Service can be reached at %-15s:%u", ipstring, PortAsNumber); + } + else if (address->sa_family == AF_INET6) + { + struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)address; + u_int16_t *w = ip6->sin6_addr.__u6_addr.__u6_addr16; + union { uint16_t s; u_char b[2]; } port = { ip6->sin6_port }; + uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; + char ipstring[40]; + char ifname[IF_NAMESIZE + 1] = ""; + sprintf(ipstring, "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X", w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]); + if (ip6->sin6_scope_id) { ifname[0] = '%'; if_indextoname(ip6->sin6_scope_id, &ifname[1]); } + printf("%s%s:%u", ipstring, ifname, PortAsNumber); + } + if (flags) printf(" Flags: %X", flags); + if (*src) + { + char txtInfo[64]; // Display at most first 64 characters of TXT record char *dst = txtInfo; const char *const lim = &txtInfo[sizeof(txtInfo)]; - while (*src && *src != 1 && dst < lim-1) *dst++ = *src++; + while (*src && dst < lim-1) + { + if (*src == '\\') *dst++ = '\\'; // '\' displays as "\\" + if (*src >= ' ') *dst++ = *src++; // Display normal characters as-is + else + { + *dst++ = '\\'; // Display a backslash + if (*src == 1) *dst++ = ' '; // String boundary displayed as "\ " + else // Other chararacters displayed as "\0xHH" + { + static const char hexchars[16] = "0123456789ABCDEF"; + *dst++ = '0'; + *dst++ = 'x'; + *dst++ = hexchars[*src >> 4]; + *dst++ = hexchars[*src & 0xF]; + } + src++; + } + } *dst++ = 0; - printf(" TXT \"%s\"", txtInfo); - if (*src == 1) src++; + printf(" TXT %s", txtInfo); } - if (flags) printf(" Flags: %X", flags); printf("\n"); } } @@ -181,8 +260,10 @@ static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info) { if (updatetest[1] != 'Z') updatetest[1]++; else updatetest[1] = 'A'; + updatetest[0] = 3 - updatetest[0]; + updatetest[2] = updatetest[1]; printf("Updating Test TXT record to %c\n", updatetest[1]); - DNSServiceRegistrationUpdateRecord(client, 0, sizeof(updatetest), &updatetest[0], 120); + DNSServiceRegistrationUpdateRecord(client, 0, 1+updatetest[0], &updatetest[0], 120); } break; @@ -198,6 +279,7 @@ static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info) static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context) { + (void)context; // Unused printf("Got a reply from the server: "); switch (errorCode) { @@ -222,6 +304,7 @@ static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *cont int main(int argc, char **argv) { char *dom; + setlinebuf(stdout); // Want to see lines as they appear, not block buffered if (argc < 2) goto Fail; // Minimum command line is the command name and one argument operation = getopt(argc, (char * const *)argv, "EFBLRAUNTM"); @@ -258,9 +341,23 @@ int main(int argc, char **argv) char *dom = argv[optind+2]; uint16_t PortAsNumber = atoi(argv[optind+3]); Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; - char *txt = (argc > optind+4) ? argv[optind+4] : ""; + char txt[2048]; + char *ptr = txt; + int i; + if (nam[0] == '.' && nam[1] == 0) nam[0] = 0; // We allow '.' on the command line as a synonym for empty string if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string + + // Copy all the TXT strings into one C string separated by ASCII-1 delimiters + for (i = optind+4; i < argc; i++) + { + strcpy(ptr, argv[i]); + ptr += strlen(argv[i]); + *ptr++ = 1; + } + if (ptr > txt) ptr--; + *ptr = 0; + printf("Registering Service %s.%s%s port %s %s\n", nam, typ, dom, argv[optind+3], txt); client = DNSServiceRegistrationCreate(nam, typ, dom, registerPort.NotAnInteger, txt, reg_reply, nil); break; @@ -278,8 +375,8 @@ int main(int argc, char **argv) case 'T': { Opaque16 registerPort = { { 0x23, 0x45 } }; - char TXT[512]; - int i; + char TXT[1000]; + unsigned int i; for (i=0; i> 5); TXT[i] = 0; @@ -289,12 +386,14 @@ int main(int argc, char **argv) } case 'M': { - Opaque16 registerPort = { { 0x23, 0x45 } }; + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; static const char TXT1[] = "First String\001Second String\001Third String"; static const char TXT2[] = "\x0D" "Fourth String" "\x0C" "Fifth String" "\x0C" "Sixth String"; printf("Registering Service Test._testdualtxt._tcp.local.\n"); - client = DNSServiceRegistrationCreate("Test", "_testdualtxt._tcp.", "", registerPort.NotAnInteger, TXT1, reg_reply, nil); - record = DNSServiceRegistrationAddRecord(client, T_TXT, sizeof(TXT2), TXT2, 120); + client = DNSServiceRegistrationCreate("", "_testdualtxt._tcp.", "", registerPort.NotAnInteger, TXT1, reg_reply, nil); + // use "sizeof(TXT2)-1" because we don't wan't the C compiler's null byte on the end of the string + record = DNSServiceRegistrationAddRecord(client, T_TXT, sizeof(TXT2)-1, TXT2, 120); break; } @@ -315,15 +414,15 @@ Exit: return 0; Fail: - fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", argv[0]); - fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", argv[0]); - fprintf(stderr, "%s -B (Browse for services instances)\n", argv[0]); - fprintf(stderr, "%s -L (Look up a service instance)\n", argv[0]); - fprintf(stderr, "%s -R (Register a service)\n", argv[0]); - fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", argv[0]); - fprintf(stderr, "%s -U (Test updating a TXT record)\n", argv[0]); - fprintf(stderr, "%s -N (Test adding a large NULL record)\n", argv[0]); - fprintf(stderr, "%s -T (Test creating a large TXT record)\n", argv[0]); - fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", argv[0]); + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", argv[0]); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", argv[0]); + fprintf(stderr, "%s -B (Browse for services instances)\n", argv[0]); + fprintf(stderr, "%s -L (Look up a service instance)\n", argv[0]); + fprintf(stderr, "%s -R [...] (Register a service)\n", argv[0]); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", argv[0]); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", argv[0]); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", argv[0]); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", argv[0]); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", argv[0]); return 0; } diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c new file mode 100644 index 0000000..b6aa01e --- /dev/null +++ b/mDNSMacOSX/daemon.c @@ -0,0 +1,1701 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * Formatting notes: + * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion + * on C indentation can be found on the web, such as , + * but for the sake of brevity here I will say just this: Curly braces are not syntactially + * part of an "if" statement; they are the beginning and ending markers of a compound statement; + * therefore common sense dictates that if they are part of a compound statement then they + * should be indented to the same level as everything else in that compound statement. + * Indenting curly braces at the same level as the "if" implies that curly braces are + * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" + * thinking that variables x and y are both of type "char*" -- and anyone who doesn't + * understand why variable y is not of type "char*" just proves the point that poor code + * layout leads people to unfortunate misunderstandings about how the C language really works.) + + Change History (most recent first): + +$Log: daemon.c,v $ +Revision 1.134 2003/08/21 20:01:37 cheshire + Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog + +Revision 1.133 2003/08/20 23:39:31 cheshire + Review syslog messages, and remove as appropriate + +Revision 1.132 2003/08/20 01:44:56 cheshire +Fix errors in LogOperation() calls (only used for debugging) + +Revision 1.131 2003/08/19 05:39:43 cheshire + SIGINFO dump should include resolves started by DNSServiceQueryRecord + +Revision 1.130 2003/08/16 03:39:01 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.129 2003/08/15 20:16:03 cheshire + mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.128 2003/08/14 19:30:36 cheshire + Include list of cache records in SIGINFO output + +Revision 1.127 2003/08/14 02:18:21 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.126 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + +Revision 1.125 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.124 2003/07/25 18:28:23 cheshire +Minor fix to error messages in syslog: Display string parameters with quotes + +Revision 1.123 2003/07/23 17:45:28 cheshire + mDNSResponder leaks a bit +Don't allocate memory for the reply until after we've verified that the reply is valid + +Revision 1.122 2003/07/23 00:00:04 cheshire +Add comments + +Revision 1.121 2003/07/20 03:38:51 ksekar +Bug #: 3320722 +Completed support for Unix-domain socket based API. + +Revision 1.120 2003/07/18 00:30:00 cheshire + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.119 2003/07/17 19:08:58 cheshire + Remove calls to enable obsolete UDS code + +Revision 1.118 2003/07/15 21:12:28 cheshire +Added extra debugging checks in validatelists() (not used in final shipping version) + +Revision 1.117 2003/07/15 01:55:15 cheshire + Need to implement service registration with subtypes + +Revision 1.116 2003/07/02 21:19:51 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.115 2003/07/02 02:41:24 cheshire + mDNSResponder needs to start with a smaller cache and then grow it as needed + +Revision 1.114 2003/07/01 21:10:20 cheshire +Reinstate checkin 1.111, inadvertently overwritten by checkin 1.112 + +Revision 1.113 2003/06/28 17:27:43 vlubet + Redirect standard input, standard output, and +standard error file descriptors to /dev/null just like any other +well behaved daemon + +Revision 1.112 2003/06/25 23:42:19 ksekar +Bug #: : Feature: New Rendezvous APIs (#7875) +Reviewed by: Stuart Cheshire +Added files necessary to implement Unix domain sockets based enhanced +Rendezvous APIs, and integrated with existing Mach-port based daemon. + +Revision 1.111 2003/06/11 01:02:43 cheshire + mDNSResponder binary compatibility +Make single binary that can run on both Jaguar and Panther. + +Revision 1.110 2003/06/10 01:14:11 cheshire + New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call + +Revision 1.109 2003/06/06 19:53:43 cheshire +For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass +(Global search-and-replace; no functional change to code execution.) + +Revision 1.108 2003/06/06 14:08:06 cheshire +For clarity, pull body of main while() loop out into a separate function called mDNSDaemonIdle() + +Revision 1.107 2003/05/29 05:44:55 cheshire +Minor fixes to log messages + +Revision 1.106 2003/05/27 18:30:55 cheshire + Need a way to easily examine current mDNSResponder state +Dean Reece suggested SIGINFO is more appropriate than SIGHUP + +Revision 1.105 2003/05/26 03:21:29 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.104 2003/05/26 00:42:06 cheshire + Temporarily include mDNSResponder version in packets + +Revision 1.103 2003/05/23 23:07:44 cheshire + Must not write to stderr when running as daemon + +Revision 1.102 2003/05/22 01:32:31 cheshire +Fix typo in Log message format string + +Revision 1.101 2003/05/22 00:26:55 cheshire + DNSServiceRegistrationCreate() should return error on dup +Modify error message to explain that this is technically legal, but may indicate a bug. + +Revision 1.100 2003/05/21 21:02:24 ksekar +Bug #: : Service should be prefixed +Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main +Mach message port to "com.apple.mDNSResponder. + +Revision 1.99 2003/05/21 17:33:49 cheshire +Fix warnings (mainly printf format string warnings, like using "%d" where it should say "%lu", etc.) + +Revision 1.98 2003/05/20 00:33:07 cheshire + Need a way to easily examine current mDNSResponder state +SIGHUP now writes state summary to syslog + +Revision 1.97 2003/05/08 00:19:08 cheshire + Forgot to set "err = mStatus_BadParamErr" in a couple of places + +Revision 1.96 2003/05/07 22:10:46 cheshire + Add a few more error logging messages + +Revision 1.95 2003/05/07 19:20:17 cheshire + Add version number to mDNSResponder builds + +Revision 1.94 2003/05/07 00:28:18 cheshire + Need to make mDNSResponder more defensive against bad clients + +Revision 1.93 2003/05/06 00:00:49 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.92 2003/04/04 20:38:57 cheshire +Add $Log header + + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "DNSServiceDiscoveryRequestServer.h" +#include "DNSServiceDiscoveryReply.h" + +#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform + +#include + +#define ENABLE_UDS 1 + +//************************************************************************************************************* +// Macros + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) + +//************************************************************************************************************* +// Globals + +mDNSexport mDNS mDNSStorage; +static mDNS_PlatformSupport PlatformStorage; +#define RR_CACHE_SIZE 64 +static CacheRecord rrcachestorage[RR_CACHE_SIZE]; +static const char PID_FILE[] = "/var/run/mDNSResponder.pid"; + +static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart"; +static mach_port_t client_death_port = MACH_PORT_NULL; +static mach_port_t exit_m_port = MACH_PORT_NULL; +static mach_port_t info_m_port = MACH_PORT_NULL; +static mach_port_t server_priv_port = MACH_PORT_NULL; + +// mDNS Mach Message Timeout, in milliseconds. +// We need this to be short enough that we don't deadlock the mDNSResponder if a client +// fails to service its mach message queue, but long enough to give a well-written +// client a chance to service its mach message queue without getting cut off. +// Empirically, 50ms seems to work, so we set the timeout to 250ms to give +// even extra-slow clients a fair chance before we cut them off. +#define MDNS_MM_TIMEOUT 250 + +static int restarting_via_mach_init = 0; + +#if MDNS_DEBUGMSGS +int debug_mode = 1; +#else +int debug_mode = 0; +#endif + +//************************************************************************************************************* +// Active client list structures + +typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration; +struct DNSServiceDomainEnumeration_struct + { + DNSServiceDomainEnumeration *next; + mach_port_t ClientMachPort; + DNSQuestion dom; // Question asking for domains + DNSQuestion def; // Question asking for default domain + }; + +typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult; +struct DNSServiceBrowserResult_struct + { + DNSServiceBrowserResult *next; + int resultType; + char name[256], type[256], dom[256]; + }; + +typedef struct DNSServiceBrowser_struct DNSServiceBrowser; +struct DNSServiceBrowser_struct + { + DNSServiceBrowser *next; + mach_port_t ClientMachPort; + DNSQuestion q; + DNSServiceBrowserResult *results; + mDNSs32 lastsuccess; + }; + +typedef struct DNSServiceResolver_struct DNSServiceResolver; +struct DNSServiceResolver_struct + { + DNSServiceResolver *next; + mach_port_t ClientMachPort; + ServiceInfoQuery q; + ServiceInfo i; + mDNSs32 ReportTime; + }; + +typedef struct DNSServiceRegistration_struct DNSServiceRegistration; +struct DNSServiceRegistration_struct + { + DNSServiceRegistration *next; + mach_port_t ClientMachPort; + mDNSBool autoname; + mDNSBool autorename; + domainlabel name; + ServiceRecordSet s; + // Don't add any fields after ServiceRecordSet. + // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object + }; + +static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL; +static DNSServiceBrowser *DNSServiceBrowserList = NULL; +static DNSServiceResolver *DNSServiceResolverList = NULL; +static DNSServiceRegistration *DNSServiceRegistrationList = NULL; + +//************************************************************************************************************* +// General Utility Functions + +#if MACOSX_MDNS_MALLOC_DEBUGGING + +char _malloc_options[] = "AXZ"; + +static void validatelists(mDNS *const m) + { + DNSServiceDomainEnumeration *e; + DNSServiceBrowser *b; + DNSServiceResolver *l; + DNSServiceRegistration *r; + AuthRecord *rr; + CacheRecord *cr; + DNSQuestion *q; + mDNSs32 slot; + + for (e = DNSServiceDomainEnumerationList; e; e=e->next) + if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0) + LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e, e->ClientMachPort); + + for (b = DNSServiceBrowserList; b; b=b->next) + if (b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t)~0) + LogMsg("!!!! DNSServiceBrowserList: %p is garbage (%X) !!!!", b, b->ClientMachPort); + + for (l = DNSServiceResolverList; l; l=l->next) + if (l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t)~0) + LogMsg("!!!! DNSServiceResolverList: %p is garbage (%X) !!!!", l, l->ClientMachPort); + + for (r = DNSServiceRegistrationList; r; r=r->next) + if (r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t)~0) + LogMsg("!!!! DNSServiceRegistrationList: %p is garbage (%X) !!!!", r, r->ClientMachPort); + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->RecordType == 0 || rr->RecordType == 0xFF) + LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr, rr->RecordType); + + for (rr = m->DuplicateRecords; rr; rr=rr->next) + if (rr->RecordType == 0 || rr->RecordType == 0xFF) + LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr, rr->RecordType); + + for (q = m->Questions; q; q=q->next) + if (q->ThisQInterval == (mDNSs32)~0) + LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q, q->ThisQInterval); + + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for (cr = mDNSStorage.rrcache_hash[slot]; cr; cr=cr->next) + if (cr->RecordType == 0 || cr->RecordType == 0xFF) + LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->RecordType); + } + +void *mallocL(char *msg, unsigned int size) + { + unsigned long *mem = malloc(size+8); + if (!mem) + { + LogMsg("malloc( %s : %d ) failed", msg, size); + return(NULL); + } + else + { + LogMalloc("malloc( %s : %lu ) = %p", msg, size, &mem[2]); + mem[0] = 0xDEAD1234; + mem[1] = size; + //bzero(&mem[2], size); + memset(&mem[2], 0xFF, size); + validatelists(&mDNSStorage); + return(&mem[2]); + } + } + +void freeL(char *msg, void *x) + { + if (!x) + LogMsg("free( %s @ NULL )!", msg); + else + { + unsigned long *mem = ((unsigned long *)x) - 2; + if (mem[0] != 0xDEAD1234) + { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; } + if (mem[1] > 8000) + { LogMsg("free( %s : %ld @ %p) too big!", msg, mem[1], &mem[2]); return; } + LogMalloc("free( %s : %ld @ %p)", msg, mem[1], &mem[2]); + //bzero(mem, mem[1]+8); + memset(mem, 0xFF, mem[1]+8); + validatelists(&mDNSStorage); + free(mem); + } + } + +#endif + +//************************************************************************************************************* +// Client Death Detection + +mDNSlocal void FreeDNSServiceRegistration(DNSServiceRegistration *x) + { + while (x->s.Extras) + { + ExtraResourceRecord *extras = x->s.Extras; + x->s.Extras = x->s.Extras->next; + if (extras->r.resrec.rdata != &extras->r.rdatastorage) + freeL("Extra RData", extras->r.resrec.rdata); + freeL("ExtraResourceRecord", extras); + } + + if (x->s.RR_TXT.resrec.rdata != &x->s.RR_TXT.rdatastorage) + freeL("TXT RData", x->s.RR_TXT.resrec.rdata); + + if (x->s.SubTypes) freeL("ServiceSubTypes", x->s.SubTypes); + + freeL("DNSServiceRegistration", x); + } + +// AbortClient finds whatever client is identified by the given Mach port, +// stops whatever operation that client was doing, and frees its memory. +// In the case of a service registration, the actual freeing may be deferred +// until we get the mStatus_MemFree message, if necessary +mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) + { + DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList; + DNSServiceBrowser **b = &DNSServiceBrowserList; + DNSServiceResolver **l = &DNSServiceResolverList; + DNSServiceRegistration **r = &DNSServiceRegistrationList; + + while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next; + if (*e) + { + DNSServiceDomainEnumeration *x = *e; + *e = (*e)->next; + if (m && m != x) + LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x); + else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c); + mDNS_StopGetDomains(&mDNSStorage, &x->dom); + mDNS_StopGetDomains(&mDNSStorage, &x->def); + freeL("DNSServiceDomainEnumeration", x); + return; + } + + while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next; + if (*b) + { + DNSServiceBrowser *x = *b; + *b = (*b)->next; + if (m && m != x) + LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->q.qname.c, m, x); + else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, x->q.qname.c); + mDNS_StopBrowse(&mDNSStorage, &x->q); + while (x->results) + { + DNSServiceBrowserResult *r = x->results; + x->results = x->results->next; + freeL("DNSServiceBrowserResult", r); + } + freeL("DNSServiceBrowser", x); + return; + } + + while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next; + if (*l) + { + DNSServiceResolver *x = *l; + *l = (*l)->next; + if (m && m != x) + LogMsg("%5d: DNSServiceResolver(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x); + else LogOperation("%5d: DNSServiceResolver(%##s) STOP", ClientMachPort, x->i.name.c); + mDNS_StopResolveService(&mDNSStorage, &x->q); + freeL("DNSServiceResolver", x); + return; + } + + while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next; + if (*r) + { + DNSServiceRegistration *x = *r; + *r = (*r)->next; + x->autorename = mDNSfalse; + if (m && m != x) + LogMsg("%5d: DNSServiceRegistration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->s.RR_SRV.resrec.name.c, m, x); + else LogOperation("%5d: DNSServiceRegistration(%##s) STOP", ClientMachPort, x->s.RR_SRV.resrec.name.c); + // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list, + // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory. + // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from + // the list, so we should go ahead and free the memory right now + if (mDNS_DeregisterService(&mDNSStorage, &x->s) != mStatus_NoError) + FreeDNSServiceRegistration(x); + return; + } + + LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort); + } + +#define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M)) + +mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m) + { + DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList; + DNSServiceBrowser *b = DNSServiceBrowserList; + DNSServiceResolver *l = DNSServiceResolverList; + DNSServiceRegistration *r = DNSServiceRegistrationList; + while (e && e->ClientMachPort != c) e = e->next; + while (b && b->ClientMachPort != c) b = b->next; + while (l && l->ClientMachPort != c) l = l->next; + while (r && r->ClientMachPort != c) r = r->next; + if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg); + else if (b) LogMsg("%5d: Browser(%##s) %s%s", c, b->q.qname.c, reason, msg); + else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg); + else if (r) LogMsg("%5d: Registration(%##s) %s%s", c, r->s.RR_SRV.resrec.name.c, reason, msg); + else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg); + + AbortClient(c, m); + } + +mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c) + { + DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList; + DNSServiceBrowser *b = DNSServiceBrowserList; + DNSServiceResolver *l = DNSServiceResolverList; + DNSServiceRegistration *r = DNSServiceRegistrationList; + while (e && e->ClientMachPort != c) e = e->next; + while (b && b->ClientMachPort != c) b = b->next; + while (l && l->ClientMachPort != c) l = l->next; + while (r && r->ClientMachPort != c) r = r->next; + if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c); + if (b) LogMsg("%5d: Browser(%##s) already exists!", c, b->q.qname.c); + if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c); + if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->s.RR_SRV.resrec.name.c); + return(e || b || l || r); + } + +mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info) + { + mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg; + (void)unusedport; // Unused + (void)size; // Unused + (void)info; // Unused + if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME) + { + const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg; + AbortClient(deathMessage->not_port, NULL); + + /* Deallocate the send right that came in the dead name notification */ + mach_port_destroy( mach_task_self(), deathMessage->not_port ); + } + } + +mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m) + { + mach_port_t prev; + kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0, + client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev); + // If the port already died while we were thinking about it, then abort the operation right away + if (r != KERN_SUCCESS) + AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m); + } + +//************************************************************************************************************* +// Domain Enumeration + +mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + kern_return_t status; + #pragma unused(m) + char buffer[256]; + DNSServiceDomainEnumerationReplyResultType rt; + DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext; + + debugf("FoundDomain: %##s PTR %##s", answer->name.c, answer->rdata->u.name.c); + if (answer->rrtype != kDNSType_PTR) return; + if (!x) { debugf("FoundDomain: DNSServiceDomainEnumeration is NULL"); return; } + + if (AddRecord) + { + if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain; + else rt = DNSServiceDomainEnumerationReplyAddDomainDefault; + } + else + { + if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain; + else return; + } + + LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s", + x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c, + !AddRecord ? "RemoveDomain" : + question == &x->dom ? "AddDomain" : "AddDomainDefault"); + + ConvertDomainNameToCString(&answer->rdata->u.name, buffer); + status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(x->ClientMachPort, "enumeration", x); + } + +mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client, + int regDom) + { + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; + mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault; + const DNSServiceDomainEnumerationReplyResultType rt = DNSServiceDomainEnumerationReplyAddDomainDefault; + + // Allocate memory, and handle failure + DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object, and link into list + x->ClientMachPort = client; + x->next = DNSServiceDomainEnumerationList; + DNSServiceDomainEnumerationList = x; + + // Generate initial response + verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing"); + // We always give local. as the initial default browse domain, and then look for more + kern_return_t status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, "local.", 0, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + { AbortBlockedClient(x->ClientMachPort, "local enumeration", x); return(mStatus_UnknownErr); } + + // Do the operation + err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, mDNSInterface_Any, FoundDomain, x); + if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, mDNSInterface_Any, FoundDomain, x); + if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; } + + // Succeeded: Wrap up and return + LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c); + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); + +fail: + LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%ld)", client, regDom, errormsg, err); + return(err); + } + +//************************************************************************************************************* +// Browse for services + +mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + (void)m; // Unused + + if (answer->rrtype != kDNSType_PTR) + { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; } + + domainlabel name; + domainname type, domain; + if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain)) + { + LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer", + answer->name.c, answer->rdata->u.name.c); + return; + } + + DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x)); + if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; } + + verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c); + ConvertDomainLabelToCString_unescaped(&name, x->name); + ConvertDomainNameToCString(&type, x->type); + ConvertDomainNameToCString(&domain, x->dom); + if (AddRecord) + x->resultType = DNSServiceBrowserReplyAddInstance; + else x->resultType = DNSServiceBrowserReplyRemoveInstance; + x->next = NULL; + + DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext; + DNSServiceBrowserResult **p = &browser->results; + while (*p) p = &(*p)->next; + *p = x; + } + +mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client, + DNSCString regtype, DNSCString domain) + { + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + // Check other parameters + domainname t, d; + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; } + if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Illegal domain"; goto badparam; } + + // Allocate memory, and handle failure + DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object, and link into list + x->ClientMachPort = client; + x->results = NULL; + x->lastsuccess = 0; + x->next = DNSServiceBrowserList; + DNSServiceBrowserList = x; + + // Do the operation + LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, d.c); + err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSInterface_Any, FoundInstance, x); + if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; } + + // Succeeded: Wrap up and return + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); + +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client, regtype, domain, errormsg, err); + return(err); + } + +//************************************************************************************************************* +// Resolve Service Info + +mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) + { + kern_return_t status; + DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext; + NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)query->info->InterfaceID; + if (query->info->InterfaceID == (mDNSInterfaceID)~0) ifx = mDNSNULL; + struct sockaddr_storage interface; + struct sockaddr_storage address; + char cstring[1024]; + int i, pstrlen = query->info->TXTinfo[0]; + (void)m; // Unused + + //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name); + + if (query->info->TXTlen > sizeof(cstring)) return; + + bzero(&interface, sizeof(interface)); + bzero(&address, sizeof(address)); + + if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin = (struct sockaddr_in*)&interface; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = 0; + sin->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger; + } + else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_flowinfo = 0; + sin6->sin6_port = 0; + sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6; + sin6->sin6_scope_id = ifx->scope_id; + } + + if (query->info->ip.type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin = (struct sockaddr_in*)&address; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = query->info->port.NotAnInteger; + sin->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger; + } + else + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = query->info->port.NotAnInteger; + sin6->sin6_flowinfo = 0; + sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6; + sin6->sin6_scope_id = ifx ? ifx->scope_id : 0; + } + + // The OS X DNSServiceResolverResolve() API is defined using a C-string, + // but the mDNS_StartResolveService() call actually returns a packed block of P-strings. + // Hence we have to convert the P-string(s) to a C-string before returning the result to the client. + // ASCII-1 characters are used in the C-string as boundary markers, + // to indicate the boundaries between the original constituent P-strings. + for (i=1; iinfo->TXTlen; i++) + { + if (--pstrlen >= 0) + cstring[i-1] = query->info->TXTinfo[i]; + else + { + cstring[i-1] = 1; + pstrlen = query->info->TXTinfo[i]; + } + } + cstring[i-1] = 0; // Put the terminating NULL on the end + + LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%d", x->ClientMachPort, + x->i.name.c, &query->info->ip, (int)query->info->port.b[0] << 8 | query->info->port.b[1]); + status = DNSServiceResolverReply_rpc(x->ClientMachPort, + (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(x->ClientMachPort, "resolve", x); + } + +mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client, + DNSCString name, DNSCString regtype, DNSCString domain) + { + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + // Check other parameters + domainlabel n; + domainname t, d, srv; + if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; } + if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } + + // Allocate memory, and handle failure + DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object, and link into list + x->ClientMachPort = client; + x->i.InterfaceID = mDNSInterface_Any; + x->i.name = srv; + x->ReportTime = (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond) | 1; + // Don't report errors for old iChat ("_ichat._tcp") service. + // New iChat ("_presence._tcp") uses DNSServiceQueryRecord() (from /usr/include/dns_sd.h) instead, + // and so should other applications that have valid reasons to be doing ongoing record monitoring. + if (SameDomainLabel(t.c, (mDNSu8*)"\x6_ichat")) x->ReportTime = 0; + x->next = DNSServiceResolverList; + DNSServiceResolverList = x; + + // Do the operation + LogOperation("%5d: DNSServiceResolver(%##s) START", client, x->i.name.c); + err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x); + if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; } + + // Succeeded: Wrap up and return + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); + +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client, name, regtype, domain, errormsg, err); + return(err); + } + +//************************************************************************************************************* +// Registration + +mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) + { + DNSServiceRegistration *x = (DNSServiceRegistration*)sr->ServiceContext; + + if (result == mStatus_NoError) + { + kern_return_t status; + LogOperation("%5d: DNSServiceRegistration(%##s) Name Registered", x->ClientMachPort, sr->RR_SRV.resrec.name.c); + status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(x->ClientMachPort, "registration success", x); + } + + else if (result == mStatus_NameConflict) + { + LogOperation("%5d: DNSServiceRegistration(%##s) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c); + // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered + // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well. + if (x->autoname) + mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + else + { + // If we get a name conflict, we tell the client about it, and then they are expected to dispose + // of their registration in the usual way (which we will catch via client death notification). + // If the Mach queue is full, we forcibly abort the client immediately. + kern_return_t status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(x->ClientMachPort, "registration conflict", x); + } + } + + else if (result == mStatus_MemFree) + { + if (x->autorename) + { + debugf("RegCallback renaming %#s to %#s", x->name.c, mDNSStorage.nicelabel.c); + x->autorename = mDNSfalse; + x->name = mDNSStorage.nicelabel; + mDNS_RenameAndReregisterService(m, &x->s, &x->name); + } + else + { + DNSServiceRegistration **r = &DNSServiceRegistrationList; + while (*r && *r != x) r = &(*r)->next; + if (*r) + { + LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.resrec.name.c); + *r = (*r)->next; + } + LogOperation("%5d: DNSServiceRegistration(%##s) Memory Free", x->ClientMachPort, sr->RR_SRV.resrec.name.c); + FreeDNSServiceRegistration(x); + } + } + + else + LogMsg("%5d: DNSServiceRegistration(%##s) Unknown Result %ld", + x->ClientMachPort, sr->RR_SRV.resrec.name.c, result); + } + +mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainname *srv, mDNSIPPort port) + { + int count = 1; // Start with the one we're planning to register, then see if there are any more + AuthRecord *rr; + for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && + rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger && + SameDomainName(&rr->resrec.name, srv)) + count++; + + if (count > 1) + LogMsg("%5d: Client application registered %d identical instances of service %##s port %d.", + x->ClientMachPort, count, srv->c, (int)port.b[0] << 8 | port.b[1]); + } + +mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client, + DNSCString name, DNSCString regtype, DNSCString domain, int notAnIntPort, DNSCString txtRecord) + { + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + // Check for sub-types after the service type + AuthRecord *SubTypes = mDNSNULL; + mDNSu32 i, NumSubTypes = 0; + char *comma = regtype; + while (*comma && *comma != ',') comma++; + if (*comma) // If we found a comma... + { + *comma = 0; // Overwrite the first comma with a nul + char *p = comma + 1; // Start scanning from the next character + while (*p) + { + if ( !(*p && *p != ',')) { errormsg = "Bad Service SubType"; goto badparam; } + while (*p && *p != ',') p++; + if (*p) *p++ = 0; + NumSubTypes++; + } + } + + // Check other parameters + domainlabel n; + domainname t, d; + domainname srv; + if (!name[0]) n = mDNSStorage.nicelabel; + else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; } + if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } + + mDNSIPPort port; + port.NotAnInteger = notAnIntPort; + + unsigned char txtinfo[1024] = ""; + unsigned int data_len = 0; + unsigned int size = sizeof(RDataBody); + unsigned char *pstring = &txtinfo[data_len]; + char *ptr = txtRecord; + + // The OS X DNSServiceRegistrationCreate() API is defined using a C-string, + // but the mDNS_RegisterService() call actually requires a packed block of P-strings. + // Hence we have to convert the C-string to a P-string. + // ASCII-1 characters are allowed in the C-string as boundary markers, + // so that a single C-string can be used to represent one or more P-strings. + while (*ptr) + { + if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; } + if (*ptr == 1) // If this is our boundary marker, start a new P-string + { + pstring = &txtinfo[data_len]; + pstring[0] = 0; + ptr++; + } + else + { + if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; } + pstring[++pstring[0]] = *ptr++; + } + } + + data_len++; + if (size < data_len) + size = data_len; + + // Allocate memory, and handle failure + DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + if (NumSubTypes) + { + SubTypes = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); + if (!SubTypes) { freeL("DNSServiceRegistration", x); err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + for (i = 0; i < NumSubTypes; i++) + { + comma++; // Advance over the nul character + MakeDomainNameFromDNSNameString(&SubTypes[i].resrec.name, comma); + while (*comma) comma++; // Advance comma to point to the next terminating nul + } + } + + // Set up object, and link into list + x->ClientMachPort = client; + x->autoname = (!name[0]); + x->autorename = mDNSfalse; + x->name = n; + x->next = DNSServiceRegistrationList; + DNSServiceRegistrationList = x; + + // Do the operation + LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\") START", x->ClientMachPort, name, regtype, domain); + // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with + // a port number of zero. When two instances of the protected client are allowed to run on one + // machine, we don't want to see misleading "Bogus client" messages in syslog and the console. + if (port.NotAnInteger) CheckForDuplicateRegistrations(x, &srv, port); + + err = mDNS_RegisterService(&mDNSStorage, &x->s, + &x->name, &t, &d, // Name, type, domain + mDNSNULL, port, // Host and port + txtinfo, data_len, // TXT data, length + SubTypes, NumSubTypes, // Subtypes + mDNSInterface_Any, // Interace ID + RegCallback, x); // Callback and context + + if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } + + // Succeeded: Wrap up and return + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); + +badtxt: + LogMsg("%5d: TXT record: %.100s...", client, txtRecord); +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)", + client, name, regtype, domain, notAnIntPort, errormsg, err); + return(err); + } + +mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) + { + (void)m; // Unused + if (result == mStatus_ConfigChanged) + { + DNSServiceRegistration *r; + for (r = DNSServiceRegistrationList; r; r=r->next) + if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c)) + { + debugf("NetworkChanged renaming %#s to %#s", r->name.c, mDNSStorage.nicelabel.c); + r->autorename = mDNStrue; + mDNS_DeregisterService(&mDNSStorage, &r->s); + } + } + else if (result == mStatus_GrowCache) + { + // If we've run out of cache space, then double the total cache size and give the memory to mDNSCore + mDNSu32 numrecords = m->rrcache_size; + CacheRecord *storage = mallocL("mStatus_GrowCache", sizeof(CacheRecord) * numrecords); + if (storage) mDNS_GrowCache(&mDNSStorage, storage, numrecords); + } + } + +//************************************************************************************************************* +// Add / Update / Remove records from existing Registration + +mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client, + int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference) + { + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + domainname *name = (domainname *)""; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + DNSServiceRegistration *x = DNSServiceRegistrationList; + while (x && x->ClientMachPort != client) x = x->next; + if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } + name = &x->s.RR_SRV.resrec.name; + + // Check other parameters + if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; } + unsigned int size = sizeof(RDataBody); + if (size < data_len) + size = data_len; + + // Allocate memory, and handle failure + ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); + if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Fill in type, length, and data of new record + extra->r.resrec.rrtype = type; + extra->r.rdatastorage.MaxRDLength = size; + extra->r.resrec.rdlength = data_len; + memcpy(&extra->r.rdatastorage.u.data, data, data_len); + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p", + client, x->s.RR_SRV.resrec.name.c, type, data_len, extra); + err = mDNS_AddRecordToService(&mDNSStorage, &x->s, extra, &extra->r.rdatastorage, ttl); + *reference = (natural_t)extra; + if (err) { errormsg = "mDNS_AddRecordToService"; goto fail; } + + // Succeeded: Wrap up and return + return(mStatus_NoError); + +fail: + LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, name->c, type, data_len, errormsg, err); + return(err); + } + +mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData) + { + (void)m; // Unused + if (OldRData != &rr->rdatastorage) + freeL("Old RData", OldRData); + } + +mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client, + natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) + { + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + domainname *name = (domainname *)""; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + DNSServiceRegistration *x = DNSServiceRegistrationList; + while (x && x->ClientMachPort != client) x = x->next; + if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } + name = &x->s.RR_SRV.resrec.name; + + // Check other parameters + if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; } + unsigned int size = sizeof(RDataBody); + if (size < data_len) + size = data_len; + + // Find the record we're updating. NULL reference means update the primary TXT record + AuthRecord *rr = &x->s.RR_TXT; + if (reference) // Scan our list to make sure we're updating a valid record that was previously added + { + ExtraResourceRecord *e = x->s.Extras; + while (e && e != (ExtraResourceRecord*)reference) e = e->next; + if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; } + rr = &e->r; + } + + // Allocate memory, and handle failure + RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size); + if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Fill in new length, and data + newrdata->MaxRDLength = size; + memcpy(&newrdata->u, data, data_len); + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, new length %d)", + client, x->s.RR_SRV.resrec.name.c, reference, data_len); + err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback); + if (err) { errormsg = "mDNS_Update"; goto fail; } + + // Succeeded: Wrap up and return + return(mStatus_NoError); + +fail: + LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err); + return(err); + } + +mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client, + natural_t reference) + { + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + domainname *name = (domainname *)""; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + DNSServiceRegistration *x = DNSServiceRegistrationList; + while (x && x->ClientMachPort != client) x = x->next; + if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } + name = &x->s.RR_SRV.resrec.name; + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X)", client, x->s.RR_SRV.resrec.name.c, reference); + ExtraResourceRecord *extra = (ExtraResourceRecord*)reference; + err = mDNS_RemoveRecordFromService(&mDNSStorage, &x->s, extra); + if (err) { errormsg = "mDNS_RemoveRecordFromService (No such record)"; goto fail; } + + // Succeeded: Wrap up and return + if (extra->r.resrec.rdata != &extra->r.rdatastorage) + freeL("Extra RData", extra->r.resrec.rdata); + freeL("ExtraResourceRecord", extra); + return(mStatus_NoError); + +fail: + LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s (%ld)", client, name->c, reference, errormsg, err); + return(err); + } + +//************************************************************************************************************* +// Support Code + +mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) + { + mig_reply_error_t *request = msg; + mig_reply_error_t *reply; + mach_msg_return_t mr; + int options; + (void)port; // Unused + (void)size; // Unused + (void)info; // Unused + + /* allocate a reply buffer */ + reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0); + + /* call the MiG server routine */ + (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head); + + if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS)) + { + if (reply->RetCode == MIG_NO_REPLY) + { + /* + * This return code is a little tricky -- it appears that the + * demux routine found an error of some sort, but since that + * error would not normally get returned either to the local + * user or the remote one, we pretend it's ok. + */ + CFAllocatorDeallocate(NULL, reply); + return; + } + + /* + * destroy any out-of-line data in the request buffer but don't destroy + * the reply port right (since we need that to send an error message). + */ + request->Head.msgh_remote_port = MACH_PORT_NULL; + mach_msg_destroy(&request->Head); + } + + if (reply->Head.msgh_remote_port == MACH_PORT_NULL) + { + /* no reply port, so destroy the reply */ + if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) + mach_msg_destroy(&reply->Head); + CFAllocatorDeallocate(NULL, reply); + return; + } + + /* + * send reply. + * + * We don't want to block indefinitely because the client + * isn't receiving messages from the reply port. + * If we have a send-once right for the reply port, then + * this isn't a concern because the send won't block. + * If we have a send right, we need to use MACH_SEND_TIMEOUT. + * To avoid falling off the kernel's fast RPC path unnecessarily, + * we only supply MACH_SEND_TIMEOUT when absolutely necessary. + */ + + options = MACH_SEND_MSG; + if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) + options |= MACH_SEND_TIMEOUT; + + mr = mach_msg(&reply->Head, /* msg */ + options, /* option */ + reply->Head.msgh_size, /* send_size */ + 0, /* rcv_size */ + MACH_PORT_NULL, /* rcv_name */ + MACH_MSG_TIMEOUT_NONE, /* timeout */ + MACH_PORT_NULL); /* notify */ + + /* Has a message error occurred? */ + switch (mr) + { + case MACH_SEND_INVALID_DEST: + case MACH_SEND_TIMED_OUT: + /* the reply can't be delivered, so destroy it */ + mach_msg_destroy(&reply->Head); + break; + + default : + /* Includes success case. */ + break; + } + + CFAllocatorDeallocate(NULL, reply); + } + +mDNSlocal kern_return_t registerBootstrapService() + { + kern_return_t status; + mach_port_t service_send_port, service_rcv_port; + + debugf("Registering Bootstrap Service"); + + /* + * See if our service name is already registered and if we have privilege to check in. + */ + status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port); + if (status == KERN_SUCCESS) + { + /* + * If so, we must be a followup instance of an already defined server. In that case, + * the bootstrap port we inherited from our parent is the server's privilege port, so set + * that in case we have to unregister later (which requires the privilege port). + */ + server_priv_port = bootstrap_port; + restarting_via_mach_init = TRUE; + } + else if (status == BOOTSTRAP_UNKNOWN_SERVICE) + { + status = bootstrap_create_server(bootstrap_port, "/usr/sbin/mDNSResponder", getuid(), + FALSE /* relaunch immediately, not on demand */, &server_priv_port); + if (status != KERN_SUCCESS) return status; + + status = bootstrap_create_service(server_priv_port, (char*)kmDNSBootstrapName, &service_send_port); + if (status != KERN_SUCCESS) + { + mach_port_deallocate(mach_task_self(), server_priv_port); + return status; + } + + status = bootstrap_check_in(server_priv_port, (char*)kmDNSBootstrapName, &service_rcv_port); + if (status != KERN_SUCCESS) + { + mach_port_deallocate(mach_task_self(), server_priv_port); + mach_port_deallocate(mach_task_self(), service_send_port); + return status; + } + assert(service_send_port == service_rcv_port); + } + + /* + * We have no intention of responding to requests on the service port. We are not otherwise + * a Mach port-based service. We are just using this mechanism for relaunch facilities. + * So, we can dispose of all the rights we have for the service port. We don't destroy the + * send right for the server's privileged bootstrap port - in case we have to unregister later. + */ + mach_port_destroy(mach_task_self(), service_rcv_port); + return status; + } + +mDNSlocal kern_return_t destroyBootstrapService() + { + debugf("Destroying Bootstrap Service"); + return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL); + } + +mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) + { + (void)port; // Unused + (void)msg; // Unused + (void)size; // Unused + (void)info; // Unused +/* + CacheRecord *rr; + int rrcache_active = 0; + for (rr = mDNSStorage.rrcache; rr; rr=rr->next) if (CacheRRActive(&mDNSStorage, rr)) rrcache_active++; + debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active); +*/ + + LogMsg("%s stopping", mDNSResponderVersionString); + + debugf("ExitCallback: destroyBootstrapService"); + if (!debug_mode) + destroyBootstrapService(); + + debugf("ExitCallback: Aborting MIG clients"); + while (DNSServiceDomainEnumerationList) + AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList); + while (DNSServiceBrowserList) + AbortClient(DNSServiceBrowserList ->ClientMachPort, DNSServiceBrowserList); + while (DNSServiceResolverList) + AbortClient(DNSServiceResolverList ->ClientMachPort, DNSServiceResolverList); + while (DNSServiceRegistrationList) + AbortClient(DNSServiceRegistrationList ->ClientMachPort, DNSServiceRegistrationList); + + debugf("ExitCallback: mDNS_Close"); + mDNS_Close(&mDNSStorage); +#if ENABLE_UDS + if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed"); +#endif + exit(0); + } + +// Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit +mDNSlocal void HandleSIGTERM(int signal) + { + (void)signal; // Unused + debugf(" "); + debugf("SIGINT/SIGTERM"); + mach_msg_header_t header; + header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + header.msgh_remote_port = exit_m_port; + header.msgh_local_port = MACH_PORT_NULL; + header.msgh_size = sizeof(header); + header.msgh_id = 0; + if (mach_msg_send(&header) != MACH_MSG_SUCCESS) + { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); } + } + +mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) + { + (void)port; // Unused + (void)msg; // Unused + (void)size; // Unused + (void)info; // Unused + DNSServiceDomainEnumeration *e; + DNSServiceBrowser *b; + DNSServiceResolver *l; + DNSServiceRegistration *r; + mDNSs32 slot; + CacheRecord *rr; + mDNSu32 CacheUsed = 0, CacheActive = 0; + + LogMsg("%s ---- BEGIN STATE LOG ----", mDNSResponderVersionString); + + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for (rr = mDNSStorage.rrcache_hash[slot]; rr; rr=rr->next) + { + CacheUsed++; + if (rr->CRActiveQuestion) CacheActive++; + LogMsg("%s %-5s%-6s%s", rr->CRActiveQuestion ? "Active: " : "Inactive:", DNSTypeName(rr->resrec.rrtype), + ((NetworkInterfaceInfoOSX *)rr->resrec.InterfaceID)->ifa_name, GetRRDisplayString(&mDNSStorage, rr)); + usleep(1000); // Limit rate a little so we don't flood syslog too fast + } + if (mDNSStorage.rrcache_totalused != CacheUsed) + LogMsg("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage.rrcache_totalused, CacheUsed); + if (mDNSStorage.rrcache_active != CacheActive) + LogMsg("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage.rrcache_active, CacheActive); + LogMsg("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive); + + for (e = DNSServiceDomainEnumerationList; e; e=e->next) + LogMsg("%5d: DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c); + + for (b = DNSServiceBrowserList; b; b=b->next) + LogMsg("%5d: ServiceBrowse %##s", b->ClientMachPort, b->q.qname.c); + + for (l = DNSServiceResolverList; l; l=l->next) + LogMsg("%5d: ServiceResolve %##s", l->ClientMachPort, l->i.name.c); + + for (r = DNSServiceRegistrationList; r; r=r->next) + LogMsg("%5d: ServiceRegistration %##s", r->ClientMachPort, r->s.RR_SRV.resrec.name.c); + + udsserver_info(); + + LogMsg("%s ---- END STATE LOG ----", mDNSResponderVersionString); + } + +mDNSlocal void HandleSIGINFO(int signal) + { + (void)signal; // Unused + mach_msg_header_t header; + header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + header.msgh_remote_port = info_m_port; + header.msgh_local_port = MACH_PORT_NULL; + header.msgh_size = sizeof(header); + header.msgh_id = 0; + if (mach_msg_send(&header) != MACH_MSG_SUCCESS) + LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated."); + } + +mDNSlocal kern_return_t mDNSDaemonInitialize(void) + { + mStatus err; + CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); + CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); + CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL); + CFMachPortRef i_port = CFMachPortCreate(NULL, INFOCallback, NULL, NULL); + mach_port_t m_port = CFMachPortGetPort(s_port); + char *MachServerName = mDNSMacOSXSystemBuildNumber(NULL) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder"; + kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port); + CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0); + CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); + CFRunLoopSourceRef e_rls = CFMachPortCreateRunLoopSource(NULL, e_port, 0); + CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0); + + if (status) + { + if (status == 1103) + LogMsg("Bootstrap_register failed(): A copy of the daemon is apparently already running"); + else + LogMsg("Bootstrap_register failed(): %s %d", mach_error_string(status), status); + return(status); + } + + err = mDNS_Init(&mDNSStorage, &PlatformStorage, + rrcachestorage, RR_CACHE_SIZE, + mDNS_Init_AdvertiseLocalAddresses, + mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); + if (err) { LogMsg("Daemon start: mDNS_Init failed %ld", err); return(err); } + + client_death_port = CFMachPortGetPort(d_port); + exit_m_port = CFMachPortGetPort(e_port); + info_m_port = CFMachPortGetPort(i_port); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode); + CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode); + CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode); + CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls, kCFRunLoopDefaultMode); + CFRelease(d_rls); + CFRelease(s_rls); + CFRelease(e_rls); + CFRelease(i_rls); + if (debug_mode) printf("Service registered with Mach Port %d\n", m_port); +#if ENABLE_UDS + err = udsserver_init(); + if (err) { LogMsg("Daemon start: udsserver_init failed"); return err; } + err = udsserver_add_rl_source(); + if (err) { LogMsg("Daemon start: udsserver_add_rl_source failed"); return err; } +#endif + return(err); + } + +mDNSlocal mDNSs32 mDNSDaemonIdle(void) + { + // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do + mDNSs32 nextevent = mDNS_Execute(&mDNSStorage); + + mDNSs32 now = mDNSPlatformTimeNow(); + + // 2. Deliver any waiting browse messages to clients + DNSServiceBrowser *b = DNSServiceBrowserList; + + while (b) + { + // NOTE: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the + // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient() + // and that will cause the DNSServiceBrowser object's memory to be freed before it returns + DNSServiceBrowser *x = b; + b = b->next; + if (x->results) // Try to deliver the list of results + { + while (x->results) + { + DNSServiceBrowserResult *const r = x->results; + DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0; + kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, r->name, r->type, r->dom, flags, 1); + // If we failed to send the mach message, try again in one second + if (status == MACH_SEND_TIMED_OUT) + { + if (nextevent - now > mDNSPlatformOneSecond) + nextevent = now + mDNSPlatformOneSecond; + break; + } + else + { + x->lastsuccess = now; + x->results = x->results->next; + freeL("DNSServiceBrowserResult", r); + } + } + // If this client hasn't read a single message in the last 60 seconds, abort it + if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond) + AbortBlockedClient(x->ClientMachPort, "browse", x); + } + } + + DNSServiceResolver *l; + for (l = DNSServiceResolverList; l; l=l->next) + if (l->ReportTime && now - l->ReportTime >= 0) + { + l->ReportTime = 0; + LogMsg("%5d: DNSServiceResolver(%##s) has remained active for over two minutes. " + "This places considerable burden on the network.", l->ClientMachPort, l->i.name.c); + } + + return(nextevent); + } + +mDNSexport int main(int argc, char **argv) + { + int i; + kern_return_t status; + FILE *fp; + + for (i=1; i 1 +mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; +#else +mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")"; +#endif diff --git a/mDNSMacOSX/dns_sd.h b/mDNSMacOSX/dns_sd.h new file mode 100755 index 0000000..eaed335 --- /dev/null +++ b/mDNSMacOSX/dns_sd.h @@ -0,0 +1,1006 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: dns_sd.h,v $ +Revision 1.3 2003/08/12 19:51:51 cheshire +Update to APSL 2.0 + + + */ + +#ifndef _DNS_SD_H +#define _DNS_SD_H + +#include +#include +#include +#include + + +/* DNSServiceRef, DNSRecordRef + * + * Opaque internal data types. + * Note: client is responsible for serializing access to these structures if + * they are shared between concurrent threads. + */ + +typedef struct _DNSServiceRef_t *DNSServiceRef; +typedef struct _DNSRecordRef_t *DNSRecordRef; + +/* General flags used in functions defined below */ +enum + { + kDNSServiceFlagsMoreComing = 1, + kDNSServiceFlagsFinished = 0, /* i.e. bit not set */ + /* MoreComing indicates to a Browse callback that another result is + * queued. Applications should not update their UI to display browse + * results when the MoreComing flag is set, instead deferring the update + * until the callback's flag is Finished. */ + + kDNSServiceFlagsAdd = 2, + kDNSServiceFlagsDefault = 4, + kDNSServiceFlagsRemove = 0, /* i.e. bit not set */ + /* Flags for domain enumeration and browse reply callbacks. + * "Default" applies only to enumeration and is only valid in + * conjuction with "Add" + */ + + kDNSServiceFlagsNoAutoRename = 8, + kDNSServiceFlagsAutoRename = 0, /* i.e. bit not set */ + /* Flag for specifying renaming behavior on name conflict when registering + * non-shared records. NoAutorename is only valid if a name is explicitly + * specified when registering a service (ie the default name is not used.) + */ + + + kDNSServiceFlagsShared = 16, + kDNSServiceFlagsUnique = 32, + /* Flag for registering individual records on a connected + * DNSServiceRef. Shared indicates that there may be multiple records + * with this name on the network (e.g. PTR records). Unique indicates that the + * record's name is to be unique on the network (e.g. SRV records). + */ + + kDNSServiceFlagsBrowseDomains = 64, + kDNSServiceFlagsRegistrationDomains = 128 + /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains. + * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains + * enumerates domains recommended for registration. + */ + }; + +/* possible error code values */ +enum + { + kDNSServiceErr_NoError = 0, + kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */ + kDNSServiceErr_NoSuchName = -65538, + kDNSServiceErr_NoMemory = -65539, + kDNSServiceErr_BadParam = -65540, + kDNSServiceErr_BadReference = -65541, + kDNSServiceErr_BadState = -65542, + kDNSServiceErr_BadFlags = -65543, + kDNSServiceErr_Unsupported = -65544, + kDNSServiceErr_NotInitialized = -65545, + kDNSServiceErr_AlreadyRegistered = -65547, + kDNSServiceErr_NameConflict = -65548, + kDNSServiceErr_Invalid = -65549, + kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */ + kDNSServiceErr_BadinterfaceIndex = -65552 + /* mDNS Error codes are in the range + * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ + }; + + +/* Maximum length, in bytes, of a domain name represented as an escaped C-String */ +#define kDNSServiceMaxDomainName 1005 + + +typedef uint32_t DNSServiceFlags; +typedef int32_t DNSServiceErrorType; + + +/********************************************************************************************* + * + * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions + * + *********************************************************************************************/ + + +/* DNSServiceRefSockFD() + * + * Access underlying Unix domain socket for an initialized DNSServiceRef. + * The DNS Service Discovery implmementation uses this socket to communicate between + * the client and the mDNSResponder daemon. The application MUST NOT directly read from + * or write to this socket. Access to the socket is provided so that it can be used as a + * run loop source, or in a select() loop: when data is available for reading on the socket, + * DNSServiceProcessResult() should be called, which will extract the daemon's reply from + * the socket, and pass it to the appropriate application callback. By using a run loop or + * select(), results from the daemon can be processed asynchronously. Without using these + * constructs, DNSServiceProcessResult() will block until the response from the daemon arrives. + * The client is responsible for ensuring that the data on the socket is processed in a timely + * fashion - the daemon may terminate its connection with a client that does not clear its + * socket buffer. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + * return value: The DNSServiceRef's underlying socket descriptor, or -1 on + * error. + */ + +int DNSServiceRefSockFD(DNSServiceRef sdRef); + +/* DNSServiceProcessResult() + * + * Read a reply from the daemon, calling the appropriate application callback. This call will + * block until the daemon's response is received. Use DNSServiceRefSockFD() in + * conjunction with a run loop or select() to determine the presence of a response from the + * server before calling this function to process the reply without blocking. Call this function + * at any point if it is acceptable to block until the daemon's response arrives. Note that the + * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is + * a reply from the daemon - the daemon may terminate its connection with a client that does not + * process the daemon's responses. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls + * that take a callback parameter. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred. + */ + +DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef); + +/* DNSServiceRefDeallocate() + * + * Terminate a connection with the daemon and free memory associated with the DNSServiceRef. + * Any services or records registered with this DNSServiceRef will be deregistered. Any + * Browse, Resolve, or Query operations called with this reference will be terminated. If the + * reference's underlying socket is used in a run loop or select() call, it should be removed BEFORE + * DNSServiceRefDeallocate() is called, as this function closes the reference's socket. + * + * Note: This call is to be used only with the DNSServiceRef defined by this API. It is + * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based + * DNSServiceDiscovery.h API. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + */ + +void DNSServiceRefDeallocate(DNSServiceRef sdRef); + + +/********************************************************************************************* + * + * Domain Enumeration + * + *********************************************************************************************/ + +/* DNSServiceEnumerateDomains() + * + * Asynchronously enumerate domains available for browsing and registration. + * Currently, the only domain returned is "local.", but other domains will be returned in future. + * + * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains + * are to be found. + * + * + * DNSServiceDomainEnumReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains(). + * + * flags: Possible values are: + * 1 (MoreComing) + * 2 (Add/Remove) + * 4 (Add Default) + * + * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given + * interface is determined via the if_nametoindex() family of calls.) + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates + * the failure that occurred (other parameters are undefined if errorCode is nonzero). + * + * replyDomain: The name of the domain. + * + * context: The context pointer passed to DNSServiceEnumerateDomains. + * + */ + +typedef void (*DNSServiceDomainEnumReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *replyDomain, + void *context + ); + +/* DNSServiceEnumerateDomains() Parameters: + * + * + * sdRef: A pointer to an uninitialized sdRef. May be passed to + * DNSServiceRefDeallocate() to cancel the enumeration. + * + * flags: Possible values are: + * 0 (BrowseDomains) to enumerate domains recommended for browsing. + * 32 (RegistrationDomains) to enumerate domains recommended for registration. + * + * interfaceIndex: If non-zero, specifies the interface on which to look for domains. + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to enumerate domains on + * all interfaces. + * + * callBack: The function to be called when a domain is found or the call asynchronously + * fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSServiceEnumerateDomains + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, + void *context /* may be NULL */ + ); + +/********************************************************************************************* + * + * Service Registration + * + *********************************************************************************************/ + +/* Register a service that is discovered via Browse() and Resolve() calls. + * + * + * DNSServiceRegisterReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceRegister(). + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts, if the + * kDNSServiceFlagsNoAutoRenameOnConflict flag was passed to the + * callout.) Other parameters are undefined if errorCode is nonzero. + * + * name: The service name registered (if the application did not specify a name in + * DNSServiceRegister(), this indicates what name was automatically chosen). + * + * regtype: The type of service registered, as it was passed to the callout. + * + * domain: The domain on which the service was registered (if the application did not + * specify a domain in DNSServiceRegister(), this indicates the default domain + * on which the service was registered). + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (*DNSServiceRegisterReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + void *context + ); + +/* DNSServiceRegister() Parameters: + * + * sdRef: A pointer to an uninitialized sdRef. If this call succeeds, the reference + * may be passed to + * DNSServiceRefDeallocate() to deregister the service. + * + * interfaceIndex: If non-zero, specifies the interface on which to register the service + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to register on all + * available interfaces. Pass -1 to register a service only on the local + * machine (service will not be visible to remote hosts.) + * + * flags: Indicates the renaming behavior on name conflict (most applications + * will pass 0). See flag definitions above for details. + * + * name: If non-NULL, specifies the service name to be registered. + * Most applications will not specify a name, in which case the + * computer name is used (this name is communicated to the client via + * the callback). + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * + * domain: If non-NULL, specifies the domain on which to advertise the service. + * Most applications will not specify a domain, instead automatically + * registering in the default domain(s). + * + * host: If non-NULL, specifies the SRV target host name. Most applications + * will not specify a host, instead automatically using the machine's + * default host name(s). Note that specifying a non-NULL host does NOT + * create an address record for that host - the application is responsible + * for ensuring that the appropriate address record exists, or creating it + * via DNSServiceRegisterRecord(). + * + * port: The port on which the service accepts connections. Pass 0 for a + * "placeholder" service (i.e. a service that will not be discovered by + * browsing, but will cause a name conflict if another client tries to + * register that same name.) Most clients will not use placeholder services. + * + * txtLen: The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL. + * + * txtRecord: The txt record rdata. May be NULL. Note that a non-NULL txtRecord + * MUST be a properly formatted DNS TXT record, i.e. + * ... + * + * callBack: The function to be called when the registration completes or asynchronously + * fails. The client MAY pass NULL for the callback - The client will NOT be notified + * of the default values picked on its behalf, and the client will NOT be notified of any + * asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration + * of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL. + * The client may still deregister the service at any time via DNSServiceRefDeallocate(). + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + * + */ + +DNSServiceErrorType DNSServiceRegister + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, /* may be NULL */ + const char *regtype, + const char *domain, /* may be NULL */ + const char *host, /* may be NULL */ + uint16_t port, + uint16_t txtLen, + const void *txtRecord, /* may be NULL */ + DNSServiceRegisterReply callBack, /* may be NULL */ + void *context /* may be NULL */ + ); + +/* DNSServiceAddRecord() + * + * Add a record to a registered service. The name of the record will be the same as the + * registered service's name. + * The record can later be updated or deregistered by passing the RecordRef initialized + * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * + * + * Parameters; + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * + * flags: Currently ignored, reserved for future use. + * + * rrtype: The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h. + * + * rdlen: The length, in bytes, of the rdata. + * + * rdata: The raw rdata to be contained in the added resource record. + * + * ttl: The time to live of the resource record, in seconds. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred (the RecordRef is not initialized). + */ + +DNSServiceErrorType DNSServiceAddRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ); + +/* DNSServiceUpdateRecord + * + * Update a registered resource record. The record must either be: + * - The primary txt record of a service registered via DNSServiceRegister() + * - A record added to a registered service via DNSServiceAddRecord() + * - An individual record registered by DNSServiceRegisterRecord() + * + * + * Parameters: + * + * sdRef: A DNSServiceRef that was initialized by DNSServiceRegister() + * or DNSServiceCreateConnection(). + * + * RecordRef: A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the + * service's primary txt record. + * + * flags: Currently ignored, reserved for future use. + * + * rdlen: The length, in bytes, of the new rdata. + * + * rdata: The new rdata to be contained in the updated resource record. + * + * ttl: The time to live of the updated resource record, in seconds. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSServiceErrorType DNSServiceUpdateRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, /* may be NULL */ + DNSServiceFlags flags, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ); + +/* DNSServiceRemoveRecord + * + * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister + * an record registered individually via DNSServiceRegisterRecord(). + * + * Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister() (if the + * record being removed was registered via DNSServiceAddRecord()) or by + * DNSServiceCreateConnection() (if the record being removed was registered via + * DNSServiceRegisterRecord()). + * + * recordRef: A DNSRecordRef initialized by a successful call to DNSServiceAddRecord() + * or DNSServiceRegisterRecord(). + * + * flags: Currently ignored, reserved for future use. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSServiceErrorType DNSServiceRemoveRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags + ); + + +/********************************************************************************************* + * + * Service Discovery + * + *********************************************************************************************/ + + +/* Browse for instances of a service. + * + * + * DNSServiceBrowseReply() Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceBrowse(). + * + * flags: Possible values are MoreComing and Add/Remove. See flag definitions + * for details. + * + * interfaceIndex: The interface on which the service is advertised. This index should + * be passed to DNSServiceResolve() when resolving the service. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * serviceName: The service name discovered. + * + * regtype: The service type, as passed in to DNSServiceBrowse(). + * + * domain: The domain on which the service was discovered (if the application did not + * specify a domain in DNSServicBrowse(), this indicates the domain on which the + * service was discovered.) + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (*DNSServiceBrowseReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *serviceName, + const char *regtype, + const char *replyDomain, + void *context + ); + +/* DNSServiceBrowse() Parameters: + * + * sdRef: A pointer to an uninitialized sdRef. May be passed to + * DNSServiceRefDeallocate() to terminate the browse. + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface on which to browse for services + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to browse on all available + * interfaces. Pass -1 to only browse for services provided on the local host. + * + * regtype: The service type being browsed for followed by the protocol, separated by a + * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * + * domain: If non-NULL, specifies the domain on which to browse for services. + * Most applications will not specify a domain, instead browsing on the + * default domain(s). + * + * callBack: The function to be called when an instance of the service being browsed for + * is found, or if the call asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSServiceBrowse + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *regtype, + const char *domain, /* may be NULL */ + DNSServiceBrowseReply callBack, + void *context /* may be NULL */ + ); + +/* DNSServiceResolve() + * + * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and + * txt record. + * + * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use + * DNSServiceQueryRecord() instead, as it is more efficient for this task. + * + * Note: When the desired results have been returned, the client MUST terminate the resolve by calling + * DNSServiceRefDeallocate(). + * + * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record and + * a single TXT record (the TXT record may be empty.) To resolve non-standard services with multiple + * SRV or TXT records, DNSServiceQueryRecord() should be used. + * + * DNSServiceResolveReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceResolve(). + * + * flags: Possible values are MoreComing and Add/Remove. See flag definitions + * for details. + * + * interfaceIndex: The interface on which the service was resolved. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * fullname: The full service domain name, in the form ... + * (Any literal dots (".") are escaped with a backslash ("\."), and literal + * backslashes are escaped with a second backslash ("\\"), e.g. a web server + * named "Dr. Pepper" would have the fullname "Dr\.\032Pepper._http._tcp.local."). + * This is the appropriate format to pass to standard system DNS APIs such as + * res_query(), or to the special-purpose functions included in this API that + * take fullname parameters. + * + * hosttarget: The target hostname of the machine providing the service. This name can + * be passed to functions like gethostbyname() to identify the host's IP address. + * + * port: The port number on which connections are accepted for this service. + * + * txtLen: The length of the txt record, in bytes. + * + * txtRecord: The service's primary txt record, in standard txt record format. + * + + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (*DNSServiceResolveReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + const char *hosttarget, + uint16_t port, + uint16_t txtLen, + const char *txtRecord, + void *context + ); + +/* DNSServiceResolve() Parameters + * + * sdRef: A pointer to an uninitialized sdRef. May be passed to + * DNSServiceRefDeallocate() to terminate the resolve. + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: The interface on which to resolve the service. The client should + * pass the interface on which the servicename was discovered, i.e. + * the interfaceIndex passed to the DNSServiceBrowseReply callback, + * or 0 to resolve the named service on all available interfaces. + * + * name: The servicename to be resolved. + * + * regtype: The service type being resolved followed by the protocol, separated by a + * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * + * domain: The domain on which the service is registered, i.e. the domain passed + * to the DNSServiceBrowseReply callback. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + + +DNSServiceErrorType DNSServiceResolve + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolveReply callBack, + void *context /* may be NULL */ + ); + + +/********************************************************************************************* + * + * Special Purpose Calls (most applications will not use these) + * + *********************************************************************************************/ + +/* DNS Naming Conventions: + * + * The following functions refer to resource records by their full domain name, unlike the above + * functions which divide the name into servicename/regtype/domain fields. In the above functions, + * a dot (".") is considered to be a literal dot in the servicename field (e.g. "Dr. Pepper") and + * a label separator in the regtype ("_ftp._tcp") or domain ("apple.com") fields. Literal dots in + * the domain field would be escaped with a backslash, and literal backslashes would be escaped with + * a second backslash (this is generally not an issue, as domain names on the Internet today almost + * never use characters other than letters, digits, or hyphens, and the dots are label separators.) + * Furthermore, this is transparent to the caller, so long as the fields are passed between functions + * without manipulation. However, the following, special-purpose calls use a single, full domain name. + * As such, all dots are considered to be label separators, unless escaped, and all backslashes are + * considered to be escape characters, unless preceded by a second backslash. For example, the name + * "Dr. Smith \ Dr. Johnson" could be passed literally as a service name parameter in the above calls, + * but in the special purpose call, the dots and backslash would have to be escaped + * (e.g. "Dr\. Smith \\ Dr\. Johnson._ftp._tcp.apple.com" for an ftp service on the apple.com domain.) + */ + +/* DNSServiceConstructFullName() + * + * Concatenate a three-part domain name (as returned by the above callbacks) into a properly-escaped + * full domain name. Note that callbacks in the above functions ALREADY ESCAPE strings where necessary. + * + * Parameters: + * + * fullName: A pointer to a buffer that where the resulting full domain name is to be written. + * The buffer must be kDNSServiceDiscoveryMaxDomainName (1005) bytes in length to + * accommodate the longest legal domain name without buffer overrun. + * + * service: The service name - any dots or slashes must NOT be escaped. + * May be NULL (to construct a PTR record name, e.g. + * "_ftp._tcp.apple.com"). + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). + * + * domain: The domain name, e.g. "apple.com". Any literal dots or backslashes + * must be escaped. + * + * return value: Returns 0 on success, -1 on error. + * + */ + +int DNSServiceConstructFullName + ( + char *fullName, + const char *service, /* may be NULL */ + const char *regtype, + const char *domain + ); + +/* DNSServiceCreateConnection() + * + * Create a connection to the daemon allowing efficient registration of + * multiple individual records. + * + * + * Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating + * the reference (via DNSServiceRefDeallocate()) severs the + * connection and deregisters all records registered on this connection. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred (in which + * case the DNSServiceRef is not initialized). + */ + +DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef); + + +/* DNSServiceRegisterRecord + * + * Register an individual resource record on a connected DNSServiceRef. + * + * Note that name conflicts occurring for records registered via this call must be handled + * by the client in the callback. + * + * + * DNSServiceRegisterRecordReply() parameters: + * + * sdRef: The connected DNSServiceRef initialized by + * DNSServiceDiscoveryConnect(). + * + * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts.) + * Other parameters are undefined if errorCode is nonzero. + * + * context: The context pointer that was passed to the callout. + * + */ + + typedef void (*DNSServiceRegisterRecordReply) + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + void *context + ); + + +/* DNSServiceRegisterRecord() Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceCreateConnection(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * (To deregister ALL records registered on a single connected DNSServiceRef + * and deallocate each of their corresponding DNSServiceRecordRefs, call + * DNSServiceRefDealloocate()). + * + * flags: Possible values are Shared/Unique (see flag type definitions for details). + * + * interfaceIndex: If non-zero, specifies the interface on which to register the record + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the record to be registered on all interfaces. + * Passing -1 causes the record to only be visible on the local host. + * + * fullname: The full domain name of the resource record. + * + * rrtype: The numerical type of the resource record (e.g. PTR, SRV, etc), as defined + * in nameser.h. + * + * rrclass: The class of the resource record, as defined in nameser.h (usually 1 for the + * Internet class). + * + * rdlen: Length, in bytes, of the rdata. + * + * rdata: A pointer to the raw rdata, as it is to appear in the DNS record. + * + * ttl: The time to live of the resource record, in seconds. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails (e.g. because of a name conflict.) + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSRecordRef is + * not initialized.) + */ + + +DNSServiceErrorType DNSServiceRegisterRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceQueryRecord + * + * Query for an arbitrary DNS record. + * + * + * DNSServiceQueryRecordReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord(). + * + * flags: Possible values are Finished/MoreComing. + * + * interfaceIndex: The interface on which the query was resolved (the index for a given + * interface is determined via the if_nametoindex() family of calls). + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * errorCode is nonzero. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. + * + * rrclass: The class of the resource record, as defined in nameser.h (usually 1). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + * ttl: The resource record's time to live, in seconds. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (*DNSServiceQueryRecordReply) + ( + DNSServiceRef DNSServiceRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + void *context + ); + +/* DNSServiceQueryRecord() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. + * + * flags: Currently unused, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface on which to issue the query + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the name to be queried for on all + * interfaces. Passing -1 causes the name to be queried for only on the + * local host. + * + * fullname: The full domain name of the resource record to be queried for. + * + * rrtype: The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc) + * as defined in nameser.h. + * + * rrclass: The class of the resource record, as defined in nameser.h + * (usually 1 for the Internet class). + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSServiceQueryRecord + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, + void *context /* may be NULL */ + ); + +/* DNSServiceReconfirmRecord + * + * Instruct the daemon to verify the validity of a resource record that appears to + * be out of date (e.g. because tcp connection to a service's target failed.) + * Causes the record to be flushed from the daemon's cache (as well as all other + * daemons' caches on the network) if the record is determined to be invalid. + * + * Parameters: + * + * flags: Currently unused, reserved for future use. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. + * + * rrclass: The class of the resource record, as defined in nameser.h (usually 1). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + */ + +void DNSServiceReconfirmRecord + ( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata + ); + + +#endif // _DNS_SD_H + diff --git a/mDNSMacOSX/dnssd_clientstub.c b/mDNSMacOSX/dnssd_clientstub.c new file mode 100755 index 0000000..7277eda --- /dev/null +++ b/mDNSMacOSX/dnssd_clientstub.c @@ -0,0 +1,998 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: dnssd_clientstub.c,v $ +Revision 1.9 2003/08/15 21:30:39 cheshire +Bring up to date with LibInfo version + +Revision 1.8 2003/08/13 23:54:52 ksekar +Bringing dnssd_clientstub.c up to date with Libinfo, per radar 3376640 + +Revision 1.7 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#include "dnssd_ipc.h" + +#define CTL_PATH_PREFIX "/tmp/dnssd_clippath." +// error socket (if needed) is named "dnssd_clipath.[pid].xxx:n" where xxx are the +// last 3 digits of the time (in seconds) and n is the 6-digit microsecond time + +// general utility functions +static DNSServiceRef connect_to_server(void); +DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd); +static ipc_msg_hdr *create_hdr(int op, int *len, char **data_start, int reuse_socket); +static int my_read(int sd, char *buf, int len); +static int my_write(int sd, char *buf, int len); +static int domain_ends_in_dot(const char *dom); +// server response handlers +static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *msg); +static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data); +static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data); +static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data); +static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data); +static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data); + +typedef struct _DNSServiceRef_t + { + int sockfd; // connected socket between client and daemon + int op; // request/reply_op_t + process_reply_callback process_reply; + void *app_callback; + void *app_context; + uint32_t max_index; //largest assigned record index - 0 if no additl. recs registered + } _DNSServiceRef_t; + +typedef struct _DNSRecordRef_t + { + void *app_context; + DNSServiceRegisterRecordReply app_callback; + DNSRecordRef recref; + int record_index; // index is unique to the ServiceDiscoveryRef + DNSServiceRef sdr; + } _DNSRecordRef_t; + + +// exported functions + +int DNSServiceRefSockFD(DNSServiceRef sdRef) + { + if (!sdRef) return -1; + return sdRef->sockfd; + } + +// handle reply from server, calling application client callback. If there is no reply +// from the daemon on the socket contained in sdRef, the call will block. +DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef) + { + ipc_msg_hdr hdr; + char *data; + + if (!sdRef || sdRef->sockfd < 0 || !sdRef->process_reply) + return kDNSServiceErr_BadReference; + + if (my_read(sdRef->sockfd, (void *)&hdr, sizeof(hdr)) < 0) + return kDNSServiceErr_Unknown; + if (hdr.version != VERSION) + return kDNSServiceErr_Incompatible; + data = malloc(hdr.datalen); + if (!data) return kDNSServiceErr_NoMemory; + if (my_read(sdRef->sockfd, data, hdr.datalen) < 0) + return kDNSServiceErr_Unknown; + sdRef->process_reply(sdRef, &hdr, data); + return kDNSServiceErr_Unknown; + } + + +void DNSServiceRefDeallocate(DNSServiceRef sdRef) + { + if (!sdRef) return; + if (sdRef->sockfd > 0) close(sdRef->sockfd); + free(sdRef); + } + + +DNSServiceErrorType DNSServiceResolve + ( + DNSServiceRef *sdRef, + const DNSServiceFlags flags, + const uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + const DNSServiceResolveReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + int len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + // calculate total message length + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += strlen(name) + 1; + len += strlen(regtype) + 1; + len += strlen(domain) + 1; + + hdr = create_hdr(resolve_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (void *)hdr; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(name, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + sdr->op = resolve_request; + sdr->process_reply = handle_resolve_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + + +static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + char fullname[kDNSServiceMaxDomainName]; + char target[kDNSServiceMaxDomainName]; + uint16_t port, txtlen; + uint32_t ifi; + DNSServiceErrorType err; + char *txtrecord; + + (void)hdr; //unused + + flags = get_flags(&data); + ifi = get_long(&data); + err = get_error_code(&data); + get_string(&data, fullname, kDNSServiceMaxDomainName); + get_string(&data, target, kDNSServiceMaxDomainName); + port = get_short(&data); + txtlen = get_short(&data); + txtrecord = get_rdata(&data, txtlen); + + ((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port, txtlen, txtrecord, sdr->app_context); + } + + + + +DNSServiceErrorType DNSServiceQueryRecord +( + DNSServiceRef *sdRef, + const DNSServiceFlags flags, + const uint32_t interfaceIndex, + const char *name, + const uint16_t rrtype, + const uint16_t rrclass, + const DNSServiceQueryRecordReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + int len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!name) name = "\0"; + + // calculate total message length + len = sizeof(flags); + len += sizeof(uint32_t); //interfaceIndex + len += strlen(name) + 1; + len += 2 * sizeof(uint16_t); // rrtype, rrclass + + hdr = create_hdr(query_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (void *)hdr; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(name, &ptr); + put_short(rrtype, &ptr); + put_short(rrclass, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + + sdr->op = query_request; + sdr->process_reply = handle_query_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + + +static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex, ttl; + DNSServiceErrorType errorCode; + char name[256]; + uint16_t rrtype, rrclass, rdlen; + char *rdata; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + (get_string(&data, name, 256) < 0); + rrtype = get_short(&data); + rrclass = get_short(&data); + rdlen = get_short(&data); + rdata = get_rdata(&data, rdlen); + ttl = get_long(&data); + if (!rdata) return; + ((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass, + rdlen, rdata, ttl, sdr->app_context); + return; + } + +DNSServiceErrorType DNSServiceBrowse +( + DNSServiceRef *sdRef, + const DNSServiceFlags flags, + const uint32_t interfaceIndex, + const char *regtype, + const char *domain, + const DNSServiceBrowseReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + int len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!domain) domain = ""; + + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += strlen(regtype) + 1; + len += strlen(domain) + 1; + + hdr = create_hdr(browse_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (char *)hdr; + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + sdr->op = browse_request; + sdr->process_reply = handle_browse_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + + + + +static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + char replyName[256], replyType[256], replyDomain[256]; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + get_string(&data, replyName, 256); + get_string(&data, replyType, 256); + get_string(&data, replyDomain, 256); + ((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context); + } + + +DNSServiceErrorType DNSServiceRegister + ( + DNSServiceRef *sdRef, + const DNSServiceFlags flags, + const uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + const char *host, + const uint16_t port, + const uint16_t txtLen, + const void *txtRecord, + const DNSServiceRegisterReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + int len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!name) name = ""; + if (!regtype) return kDNSServiceErr_BadParam; + if (!domain) domain = ""; + if (!host) host = ""; + if (!txtRecord) (char *)txtRecord = ""; + + // auto-name must also have auto-rename + if (!name[0] && (flags & kDNSServiceFlagsNoAutoRename)) + return kDNSServiceErr_BadParam; + + // no callback must have auto-name + if (!callBack && name[0]) return kDNSServiceErr_BadParam; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); // interfaceIndex + len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4; + len += 2 * sizeof(uint16_t); // port, txtLen + len += txtLen; + + hdr = create_hdr(reg_service_request, &len, &ptr, 1); + if (!hdr) goto error; + if (!callBack) hdr->flags |= IPC_FLAGS_NOREPLY; + msg = (char *)hdr; + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(name, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + put_string(host, &ptr); + put_short(port, &ptr); + put_short(txtLen, &ptr); + put_rdata(txtLen, txtRecord, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + + sdr->op = reg_service_request; + sdr->process_reply = callBack ? handle_regservice_response : NULL; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + + +static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + char name[256], regtype[256], domain[256]; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + get_string(&data, name, 256); + get_string(&data, regtype, 256); + get_string(&data, domain, 256); + ((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context); + } + +DNSServiceErrorType DNSServiceEnumerateDomains +( + DNSServiceRef *sdRef, + const DNSServiceFlags flags, + const uint32_t interfaceIndex, + const DNSServiceDomainEnumReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + int len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + + hdr = create_hdr(enumeration_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (void *)hdr; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + + sdr->op = enumeration_request; + sdr->process_reply = handle_enumeration_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + + +static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType err; + char domain[256]; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + err = get_error_code(&data); + get_string(&data, domain, 256); + ((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context); + } + + +DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef) + { + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = connect_to_server(); + if (!*sdRef) + return kDNSServiceErr_Unknown; + (*sdRef)->op = connection; + (*sdRef)->process_reply = handle_regrecord_response; + return 0; + } + + + +static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + DNSRecordRef rref = hdr->client_context.context; + + if (sdr->op != connection) + { + rref->app_callback(rref->sdr, rref, 0, kDNSServiceErr_Unknown, rref->app_context); + return; + } + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + + rref->app_callback(rref->sdr, rref, flags, errorCode, rref->app_context); + } + +DNSServiceErrorType DNSServiceRegisterRecord +( + const DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + const DNSServiceFlags flags, + const uint32_t interfaceIndex, + const char *fullname, + const uint16_t rrtype, + const uint16_t rrclass, + const uint16_t rdlen, + const void *rdata, + const uint32_t ttl, + const DNSServiceRegisterRecordReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + int len; + ipc_msg_hdr *hdr = NULL; + DNSServiceRef tmp = NULL; + DNSRecordRef rref = NULL; + + if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0) + return kDNSServiceErr_BadReference; + *RecordRef = NULL; + + len = sizeof(DNSServiceFlags); + len += 2 * sizeof(uint32_t); // interfaceIndex, ttl + len += 3 * sizeof(uint16_t); // rrtype, rrclass, rdlen + len += strlen(fullname) + 1; + len += rdlen; + + hdr = create_hdr(reg_record_request, &len, &ptr, 0); + if (!hdr) goto error; + msg = (char *)hdr; + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(fullname, &ptr); + put_short(rrtype, &ptr); + put_short(rrclass, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_long(ttl, &ptr); + + rref = malloc(sizeof(_DNSRecordRef_t)); + if (!rref) goto error; + rref->app_context = context; + rref->app_callback = callBack; + rref->record_index = sdRef->max_index++; + rref->sdr = sdRef; + *RecordRef = rref; + hdr->client_context.context = rref; + hdr->reg_index = rref->record_index; + + return deliver_request(msg, sdRef, 0); + +error: + if (rref) free(rref); + if (tmp) free(tmp); + if (hdr) free(hdr); + return kDNSServiceErr_Unknown; + } + +//sdRef returned by DNSServiceRegister() +DNSServiceErrorType DNSServiceAddRecord + ( + const DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + const DNSServiceFlags flags, + const uint16_t rrtype, + const uint16_t rdlen, + const void *rdata, + const uint32_t ttl + ) + { + ipc_msg_hdr *hdr; + int len = 0; + char *ptr; + DNSRecordRef rref; + + if (!sdRef || (sdRef->op != reg_service_request) || !RecordRef) + return kDNSServiceErr_BadReference; + *RecordRef = NULL; + + len += 2 * sizeof(uint16_t); //rrtype, rdlen + len += rdlen; + len += sizeof(uint32_t); + len += sizeof(DNSServiceFlags); + + hdr = create_hdr(add_record_request, &len, &ptr, 0); + if (!hdr) return kDNSServiceErr_Unknown; + put_flags(flags, &ptr); + put_short(rrtype, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_long(ttl, &ptr); + + rref = malloc(sizeof(_DNSRecordRef_t)); + if (!rref) goto error; + rref->app_context = NULL; + rref->app_callback = NULL; + rref->record_index = sdRef->max_index++; + rref->sdr = sdRef; + *RecordRef = rref; + hdr->client_context.context = rref; + hdr->reg_index = rref->record_index; + return deliver_request((char *)hdr, sdRef, 0); + +error: + if (hdr) free(hdr); + if (rref) free(rref); + if (*RecordRef) *RecordRef = NULL; + return kDNSServiceErr_Unknown; +} + + +//DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord +DNSServiceErrorType DNSServiceUpdateRecord + ( + const DNSServiceRef sdRef, + DNSRecordRef RecordRef, + const DNSServiceFlags flags, + const uint16_t rdlen, + const void *rdata, + const uint32_t ttl + ) + { + ipc_msg_hdr *hdr; + int len = 0; + char *ptr; + + if (!sdRef || !RecordRef || !sdRef->max_index) + return kDNSServiceErr_BadReference; + + len += sizeof(uint16_t); + len += rdlen; + len += sizeof(uint32_t); + len += sizeof(DNSServiceFlags); + + hdr = create_hdr(update_record_request, &len, &ptr, 0); + if (!hdr) return kDNSServiceErr_Unknown; + hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX; + put_flags(flags, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_long(ttl, &ptr); + return deliver_request((char *)hdr, sdRef, 0); + } + + + +DNSServiceErrorType DNSServiceRemoveRecord +( + const DNSServiceRef sdRef, + const DNSRecordRef RecordRef, + const DNSServiceFlags flags + ) + { + ipc_msg_hdr *hdr; + int len = 0; + char *ptr; + DNSServiceErrorType err; + + if (!sdRef || !RecordRef || !sdRef->max_index) + return kDNSServiceErr_BadReference; + + len += sizeof(flags); + hdr = create_hdr(remove_record_request, &len, &ptr, 0); + if (!hdr) return kDNSServiceErr_Unknown; + hdr->reg_index = RecordRef->record_index; + put_flags(flags, &ptr); + err = deliver_request((char *)hdr, sdRef, 0); + if (!err) free(RecordRef); + return err; + } + + +void DNSServiceReconfirmRecord +( + const DNSServiceFlags flags, + const uint32_t interfaceIndex, + const char *fullname, + const uint16_t rrtype, + const uint16_t rrclass, + const uint16_t rdlen, + const void *rdata + ) + { + char *ptr; + int len; + ipc_msg_hdr *hdr; + DNSServiceRef tmp; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + len += strlen(fullname) + 1; + len += 3 * sizeof(uint16_t); + len += rdlen; + tmp = connect_to_server(); + if (!tmp) return; + hdr = create_hdr(reconfirm_record_request, &len, &ptr, 1); + if (!hdr) return; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(fullname, &ptr); + put_short(rrtype, &ptr); + put_short(rrclass, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + my_write(tmp->sockfd, (char *)hdr, len); + DNSServiceRefDeallocate(tmp); + } + + +int DNSServiceConstructFullName + ( + char *fullName, + const char *service, /* may be NULL */ + const char *regtype, + const char *domain + ) + { + int len; + u_char c; + char *fn = fullName; + const char *s = service; + const char *r = regtype; + const char *d = domain; + + if (service) + { + while(*s) + { + c = *s++; + if (c == '.' || (c == '\\')) *fn++ = '\\'; // escape dot and backslash literals + else if (c <= ' ') // escape non-printable characters + { + *fn++ = '\\'; + *fn++ = (char) ('0' + (c / 100)); + *fn++ = (char) ('0' + (c / 10) % 10); + c = (u_char)('0' + (c % 10)); + } + *fn++ = c; + } + *fn++ = '.'; + } + + if (!regtype) return -1; + len = strlen(regtype); + if (domain_ends_in_dot(regtype)) len--; + if (len < 4) return -1; // regtype must end in _udp or _tcp + if (strncmp((regtype + len - 4), "_tcp", 4) && strncmp((regtype + len - 4), "_udp", 4)) return -1; + while(*r) + *fn++ = *r++; + if (!domain_ends_in_dot(regtype)) *fn++ = '.'; + + if (!domain) return -1; + len = strlen(domain); + if (!len) return -1; + while(*d) + *fn++ = *d++; + if (!domain_ends_in_dot(domain)) *fn++ = '.'; + *fn = '\0'; + return 0; + } + +static int domain_ends_in_dot(const char *dom) + { + while(*dom && *(dom + 1)) + { + if (*dom == '\\') // advance past escaped byte sequence + { + if (*(dom + 1) >= '0' && *(dom + 1) <= '9') dom += 4; + else dom += 2; + } + else dom++; // else read one character + } + return (*dom == '.'); + } + + + + // return a connected service ref (deallocate with DNSServiceRefDeallocate) +static DNSServiceRef connect_to_server(void) + { + struct sockaddr_un saddr; + DNSServiceRef sdr; + + sdr = malloc(sizeof(_DNSServiceRef_t)); + if (!sdr) return NULL; + + if ((sdr->sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) + { + free(sdr); + return NULL; + } + + saddr.sun_family = AF_LOCAL; + strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH); + if (connect(sdr->sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) + { + free(sdr); + return NULL; + } + return sdr; + } + + + + +int my_write(int sd, char *buf, int len) + { + if (send(sd, buf, len, MSG_WAITALL) != len) return -1; + return 0; + } + + +// read len bytes. return 0 on success, -1 on error +int my_read(int sd, char *buf, int len) + { + if (recv(sd, buf, len, MSG_WAITALL) != len) return -1; + return 0; + } + + +DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd) + { + ipc_msg_hdr *hdr = msg; + mode_t mask; + struct sockaddr_un caddr, daddr; // (client and daemon address structs) + char *path = NULL; + int listenfd = -1, errsd = -1, len; + DNSServiceErrorType err = kDNSServiceErr_Unknown; + + if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown; + + if (!reuse_sd) + { + // setup temporary error socket + if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) + goto cleanup; + + bzero(&caddr, sizeof(caddr)); + caddr.sun_family = AF_LOCAL; + caddr.sun_len = sizeof(struct sockaddr_un); + path = (char *)msg + sizeof(ipc_msg_hdr); + strcpy(caddr.sun_path, path); + mask = umask(0); + if (bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)) < 0) + { + umask(mask); + goto cleanup; + } + umask(mask); + listen(listenfd, 1); + } + + if (my_write(sdr->sockfd, msg, hdr->datalen + sizeof(ipc_msg_hdr)) < 0) + goto cleanup; + free(msg); + msg = NULL; + + if (reuse_sd) errsd = sdr->sockfd; + else + { + len = sizeof(daddr); + errsd = accept(listenfd, (struct sockaddr *)&daddr, &len); + if (errsd < 0) goto cleanup; + } + + len = recv(errsd, &err, sizeof(err), MSG_WAITALL); + if (len != sizeof(err)) + { + err = kDNSServiceErr_Unknown; + } +cleanup: + if (!reuse_sd && listenfd > 0) close(listenfd); + if (!reuse_sd && errsd > 0) close(errsd); + if (!reuse_sd && path) unlink(path); + if (msg) free(msg); + return err; + } + + + +/* create_hdr + * + * allocate and initialize an ipc message header. value of len should initially be the + * length of the data, and is set to the value of the data plus the header. data_start + * is set to point to the beginning of the data section. reuse_socket should be non-zero + * for calls that can receive an immediate error return value on their primary socket. + * if zero, the path to a control socket is appended at the beginning of the message buffer. + * data_start is set past this string. + */ + +static ipc_msg_hdr *create_hdr(int op, int *len, char **data_start, int reuse_socket) + { + char *msg = NULL; + ipc_msg_hdr *hdr; + int datalen; + char ctrl_path[256]; + struct timeval time; + + if (!reuse_socket) + { + if (gettimeofday(&time, NULL) < 0) return NULL; + sprintf(ctrl_path, "%s%d-%.3x-%.6u", CTL_PATH_PREFIX, (int)getpid(), + time.tv_sec & 0xFFF, time.tv_usec); + + *len += strlen(ctrl_path) + 1; + } + + + datalen = *len; + *len += sizeof(ipc_msg_hdr); + + // write message to buffer + msg = malloc(*len); + if (!msg) return NULL; + + bzero(msg, *len); + hdr = (void *)msg; + hdr->datalen = datalen; + hdr->version = VERSION; + hdr->op.request_op = op; + if (reuse_socket) hdr->flags |= IPC_FLAGS_REUSE_SOCKET; + *data_start = msg + sizeof(ipc_msg_hdr); + if (!reuse_socket) put_string(ctrl_path, data_start); + return hdr; + } diff --git a/mDNSMacOSX/dnssd_ipc.c b/mDNSMacOSX/dnssd_ipc.c new file mode 100644 index 0000000..186f4d3 --- /dev/null +++ b/mDNSMacOSX/dnssd_ipc.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: dnssd_ipc.c,v $ +Revision 1.7 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#include "dnssd_ipc.h" + +void put_flags(const DNSServiceFlags flags, char **ptr) + { + memcpy(*ptr, &flags, sizeof(DNSServiceFlags)); + *ptr += sizeof(flags); + } + +DNSServiceFlags get_flags(char **ptr) + { + DNSServiceFlags flags; + + flags = *(DNSServiceFlags *)*ptr; + *ptr += sizeof(DNSServiceFlags); + return flags; + } + +void put_long(const uint32_t l, char **ptr) + { + + *(uint32_t *)(*ptr) = l; + *ptr += sizeof(uint32_t); + } + +uint32_t get_long(char **ptr) + { + uint32_t l; + + l = *(uint32_t *)(*ptr); + *ptr += sizeof(uint32_t); + return l; + } + +void put_error_code(const DNSServiceErrorType error, char **ptr) + { + memcpy(*ptr, &error, sizeof(error)); + *ptr += sizeof(DNSServiceErrorType); + } + +DNSServiceErrorType get_error_code(char **ptr) + { + DNSServiceErrorType error; + + error = *(DNSServiceErrorType *)(*ptr); + *ptr += sizeof(DNSServiceErrorType); + return error; + } + +void put_short(const uint16_t s, char **ptr) + { + *(uint16_t *)(*ptr) = s; + *ptr += sizeof(uint16_t); + } + +uint16_t get_short(char **ptr) + { + uint16_t s; + + s = *(uint16_t *)(*ptr); + *ptr += sizeof(uint16_t); + return s; + } + + +int put_string(const char *str, char **ptr) + { + if (!str) str = ""; + strcpy(*ptr, str); + *ptr += strlen(str) + 1; + return 0; + } + +// !!!KRS we don't properly handle the case where the string is longer than the buffer!!! +int get_string(char **ptr, char *buffer, int buflen) + { + int overrun; + + overrun = (int)strlen(*ptr) < buflen ? 0 : -1; + strncpy(buffer, *ptr, buflen - 1); + buffer[buflen - 1] = '\0'; + *ptr += strlen(buffer) + 1; + return overrun; + } + +void put_rdata(const int rdlen, const char *rdata, char **ptr) + { + memcpy(*ptr, rdata, rdlen); + *ptr += rdlen; + } + +char *get_rdata(char **ptr, int rdlen) + { + char *rd; + + rd = *ptr; + *ptr += rdlen; + return rd; + } + + + + + + + + + diff --git a/mDNSMacOSX/dnssd_ipc.h b/mDNSMacOSX/dnssd_ipc.h new file mode 100644 index 0000000..2b7b323 --- /dev/null +++ b/mDNSMacOSX/dnssd_ipc.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: dnssd_ipc.h,v $ +Revision 1.6 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#ifndef DNSSD_IPC_H +#define DNSSD_IPC_H + +#include "dns_sd.h" +#include +#include +#include +#include +#include +#include +#include + +//#define UDSDEBUG // verbose debug output + +// General UDS constants +#define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder" +#define LISTENQ 100 +#define TXT_RECORD_INDEX -1 // record index for default text record +#define MAX_CTLPATH 256 // longest legal control path length + +// IPC data encoding constants and types +#define VERSION 1 +#define IPC_FLAGS_NOREPLY 1 // set flag if no asynchronous replies are to be sent to client +#define IPC_FLAGS_REUSE_SOCKET 2 // set flag if synchronous errors are to be sent via the primary socket + // (if not set, first string in message buffer must be path to error socket + + +typedef enum + { + connection = 1, // connected socket via DNSServiceConnect() + reg_record_request, // reg/remove record only valid for connected sockets + remove_record_request, + enumeration_request, + reg_service_request, + browse_request, + resolve_request, + query_request, + reconfirm_record_request, + add_record_request, + update_record_request + } request_op_t; + +typedef enum + { + enumeration_reply = 64, + reg_service_reply, + browse_reply, + resolve_reply, + query_reply, + reg_record_reply + } reply_op_t; + + +typedef struct ipc_msg_hdr_struct ipc_msg_hdr; + + +// client stub callback to process message from server and deliver results to +// client application + +typedef void (*process_reply_callback) + ( + DNSServiceRef sdr, + ipc_msg_hdr *hdr, + char *msg + ); + +// allow 64-bit client to interoperate w/ 32-bit daemon +typedef union + { + void *context; + uint32_t ptr64[2]; + } client_context_t; + + +typedef struct ipc_msg_hdr_struct + { + uint32_t version; + uint32_t datalen; + uint32_t flags; + union + { + request_op_t request_op; + reply_op_t reply_op; + } op; + client_context_t client_context; // context passed from client, returned by server in corresponding reply + int reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a + // socket connected by DNSServiceConnect(). Must be unique in the scope of the connection, such that and + // index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord()) + } ipc_msg_hdr_struct; + + + + +// routines to write to and extract data from message buffers. +// caller responsible for bounds checking. +// ptr is the address of the pointer to the start of the field. +// it is advanced to point to the next field, or the end of the message + + +void put_flags(const DNSServiceFlags flags, char **ptr); +DNSServiceFlags get_flags(char **ptr); + +void put_long(const uint32_t l, char **ptr); +uint32_t get_long(char **ptr); + +void put_error_code(const DNSServiceErrorType, char **ptr); +DNSServiceErrorType get_error_code(char **ptr); + +int put_string(const char *str, char **ptr); +int get_string(char **ptr, char *buffer, int buflen); + +void put_rdata(const int rdlen, const char *rdata, char **ptr); +char *get_rdata(char **ptr, int rdlen); // return value is rdata pointed to by *ptr - + // rdata is not copied from buffer. + +void put_short(uint16_t s, char **ptr); +uint16_t get_short(char **ptr); + + + +#endif // DNSSD_IPC_H + + + + + + + + + + + diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h new file mode 100644 index 0000000..b255cd0 --- /dev/null +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSMacOSX.h,v $ +Revision 1.21 2003/08/19 22:20:00 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured +More minor refinements + +Revision 1.20 2003/08/19 05:39:43 cheshire + SIGINFO dump should include resolves started by DNSServiceQueryRecord + +Revision 1.19 2003/08/19 05:36:45 cheshire +Add missing "extern" directives + +Revision 1.18 2003/08/19 03:04:43 cheshire + Don't use IPv6 on interfaces that have a routable IPv4 address configured + +Revision 1.17 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + +Revision 1.16 2003/08/08 18:36:04 cheshire + Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug + +Revision 1.15 2003/08/05 00:32:28 cheshire + Time to turn off MACOSX_MDNS_MALLOC_DEBUGGING + +Revision 1.14 2003/07/20 03:38:51 ksekar +Bug #: 3320722 +Completed support for Unix-domain socket based API. + +Revision 1.13 2003/07/18 00:30:00 cheshire + Remove mDNSResponder version from packet header and use HINFO record instead + +Revision 1.12 2003/07/12 03:15:20 cheshire + After SCDynamicStore notification, mDNSResponder updates +m->hostlabel even if user hasn't actually actually changed their dot-local hostname + +Revision 1.11 2003/07/02 21:19:51 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.10 2003/06/25 23:42:19 ksekar +Bug #: : Feature: New Rendezvous APIs (#7875) +Reviewed by: Stuart Cheshire +Added files necessary to implement Unix domain sockets based enhanced +Rendezvous APIs, and integrated with existing Mach-port based daemon. + +Revision 1.9 2003/06/10 01:14:11 cheshire + New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call + +Revision 1.8 2003/05/14 07:08:37 cheshire + mDNSResponder should be smarter about reconfigurations +Previously, when there was any network configuration change, mDNSResponder +would tear down the entire list of active interfaces and start again. +That was very disruptive, and caused the entire cache to be flushed, +and caused lots of extra network traffic. Now it only removes interfaces +that have really gone, and only adds new ones that weren't there before. + +Revision 1.7 2003/04/26 02:39:24 cheshire +Remove extern void LogMsg(const char *format, ...); + +Revision 1.6 2003/03/05 21:59:56 cheshire +Bug #: 3189097 Additional debugging code in mDNSResponder + +Revision 1.5 2003/03/05 01:50:38 cheshire +Bug #: 3189097 Additional debugging code in mDNSResponder + +Revision 1.4 2003/02/21 01:54:10 cheshire +Bug #: 3099194 mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.3 2002/09/21 20:44:51 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 04:20:44 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.1 2002/09/17 01:04:09 cheshire +Defines mDNS_PlatformSupport_struct for OS X + +*/ + +#ifndef __mDNSOSX_h +#define __mDNSOSX_h + +#ifdef __cplusplus + extern "C" { +#endif + +#include +#include +#include +#include + +typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX; +struct NetworkInterfaceInfoOSX_struct + { + NetworkInterfaceInfo ifinfo; // MUST be the first element in this structure + NetworkInterfaceInfoOSX *next; + mDNS *m; + mDNSu32 CurrentlyActive; // 0 not active; 1 active; 2 active but TxRx state changed + char *ifa_name; // Memory for this is allocated using malloc + mDNSu32 scope_id; // interface index / IPv6 scope ID + u_short sa_family; +#if mDNS_AllowPort53 + int skt53; + CFSocketRef cfs53; +#endif + int sktv4; + CFSocketRef cfsv4; + int sktv6; + CFSocketRef cfsv6; + }; + +struct mDNS_PlatformSupport_struct + { + NetworkInterfaceInfoOSX *InterfaceList; + domainlabel userhostlabel; + SCDynamicStoreRef Store; + CFRunLoopSourceRef StoreRLS; + io_connect_t PowerConnection; + io_object_t PowerNotifier; + CFRunLoopSourceRef PowerRLS; + }; + +extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index); +extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id); +extern mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); + +extern const char mDNSResponderVersionString[]; + +// Set this symbol to 1 to do extra debug checks on malloc() and free() +// Set this symbol to 2 to write a log message for every malloc() and free() +#define MACOSX_MDNS_MALLOC_DEBUGGING 0 + +#if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 +extern void *mallocL(char *msg, unsigned int size); +extern void freeL(char *msg, void *x); +#else +#define mallocL(X,Y) malloc(Y) +#define freeL(X,Y) free(Y) +#endif + +#if MACOSX_MDNS_MALLOC_DEBUGGING >= 2 +#define LogMalloc LogMsg +#else +#define LogMalloc(ARGS...) ((void)0) +#endif + +#define LogAllOperations 0 + +#if LogAllOperations +#define LogOperation LogMsg +#else +#define LogOperation(ARGS...) ((void)0) +#endif + +#ifdef __cplusplus + } +#endif + +// UDS Server <-> daemon crossover routines/globals +extern mDNS mDNSStorage; +extern int udsserver_init(void); +extern int udsserver_add_rl_source(void); +extern mDNSs32 udsserver_idle(mDNSs32 nextevent); // takes the next scheduled event time, does idle work, + // and returns the updated nextevent time +extern void udsserver_info(void); +extern void udsserver_handle_configchange(void); +extern int udsserver_exit(void); + +#endif diff --git a/mDNSResponder.pbproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj similarity index 51% rename from mDNSResponder.pbproj/project.pbxproj rename to mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj index 7fc8cc5..2bd8d35 100644 --- a/mDNSResponder.pbproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj @@ -5,48 +5,298 @@ }; objectVersion = 38; objects = { - 00CA213D02786FC30CCA2C71 = { - isa = PBXFrameworkReference; - name = IOKit.framework; - path = /System/Library/Frameworks/IOKit.framework; - refType = 0; + 000753D303367C1C0CCA2C71 = { + isa = PBXFileReference; + path = mDNSMacOSX.h; + refType = 4; }; -//000 -//001 -//002 -//003 -//004 -//010 -//011 -//012 -//013 -//014 - 014CEA490018CE3211CA2923 = { - buildRules = ( + 0017390704CC75C30CCA2C71 = { + isa = PBXFileReference; + path = SampleUDSClient.c; + refType = 2; + }; + 0017390804CC75C30CCA2C71 = { + fileRef = 0017390704CC75C30CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + 0044D34804CC73600CCA2C71 = { + buildPhases = ( + 0044D34904CC73600CCA2C71, + 0044D34A04CC73600CCA2C71, + 0044D34C04CC73600CCA2C71, + 0044D34E04CC73600CCA2C71, ); buildSettings = { - COPY_PHASE_STRIP = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + OTHER_CFLAGS = "-no-cpp-precomp -DmDNSResponderVersion=$(MVERS)"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = uds_test; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = "UDS API Test Tool"; + productName = "UDS API Test Tool"; + productReference = 0044D34F04CC73600CCA2C71; + shouldUseHeadermap = 0; + }; + 0044D34904CC73600CCA2C71 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 0044D34A04CC73600CCA2C71 = { + buildActionMask = 2147483647; + files = ( + 0017390804CC75C30CCA2C71, + 00DD152B04CC79700CCA2C71, + 00DD152C04CC79A50CCA2C71, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 0044D34C04CC73600CCA2C71 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 0044D34E04CC73600CCA2C71 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 0044D34F04CC73600CCA2C71 = { + isa = PBXExecutableFileReference; + path = uds_test; + refType = 3; + }; + 004EFB9604CC78130CCA2C71 = { + isa = PBXFileReference; + path = dnssd_clientstub.c; + refType = 2; + }; + 0066920A04CC7AA80CCA2C71 = { + isa = PBXTargetDependency; + target = 0044D34804CC73600CCA2C71; + }; + 00AD62A3032D799A0CCA2C71 = { + buildPhases = ( + 00AD62A4032D799A0CCA2C71, + 00AD62AC032D799A0CCA2C71, + 00AD62B3032D799A0CCA2C71, + 00AD62B7032D799A0CCA2C71, + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; + LIBRARY_SEARCH_PATHS = ""; OPTIMIZATION_CFLAGS = "-O0"; - OTHER_CFLAGS = "-D__MACOSX__ -DDEBUGBREAKS=1"; + OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DMDNS_DEBUGMSGS=1 -DmDNSResponderVersion=$(MVERS)"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = mDNSResponder.debug; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; }; - isa = PBXBuildStyle; - name = Development; + dependencies = ( + ); + isa = PBXToolTarget; + name = "mDNSResponder debug"; + productName = mDNSResponder; + productReference = 00AD62B8032D799A0CCA2C71; + shouldUseHeadermap = 1; + }; + 00AD62A4032D799A0CCA2C71 = { + buildActionMask = 2147483647; + files = ( + 00AD62A5032D799A0CCA2C71, + F5E11B5F04A28126019798ED, + F515E29604A37BB701CA296C, + F515E29704A37BB801CA296C, + F515E29804A37BBB01CA296C, + F515E29904A37BBB01CA296C, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 00AD62A5032D799A0CCA2C71 = { + fileRef = 6575FBFF022EAFBA00000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62AC032D799A0CCA2C71 = { + buildActionMask = 2147483647; + files = ( + 00AD62AD032D799A0CCA2C71, + 00AD62AE032D799A0CCA2C71, + 00AD62AF032D799A0CCA2C71, + 00AD62B0032D799A0CCA2C71, + 00AD62B1032D799A0CCA2C71, + F5E11B5E04A28126019798ED, + F525E72B04AA167A01F1CF4D, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 00AD62AD032D799A0CCA2C71 = { + fileRef = 6575FC00022EAFBA00000109; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + Client, + ); + }; + }; + 00AD62AE032D799A0CCA2C71 = { + fileRef = 6575FC01022EAFBA00000109; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + Server, + Client, + ); + }; + }; + 00AD62AF032D799A0CCA2C71 = { + fileRef = 6575FBE9022EAF5A00000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B0032D799A0CCA2C71 = { + fileRef = 6575FBEB022EAF7200000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B1032D799A0CCA2C71 = { + fileRef = 6575FBEC022EAF7200000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B3032D799A0CCA2C71 = { + buildActionMask = 2147483647; + files = ( + 00AD62B4032D799A0CCA2C71, + 00AD62B5032D799A0CCA2C71, + 00AD62B6032D799A0CCA2C71, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 00AD62B4032D799A0CCA2C71 = { + fileRef = 09AB6884FE841BABC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B5032D799A0CCA2C71 = { + fileRef = 65713D46025A293200000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B6032D799A0CCA2C71 = { + fileRef = 00CA213D02786FC30CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B7032D799A0CCA2C71 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 00AD62B8032D799A0CCA2C71 = { + isa = PBXExecutableFileReference; + path = mDNSResponder.debug; + refType = 3; }; - 014CEA4A0018CE3211CA2923 = { + 00AD62BB032D7A0C0CCA2C71 = { + buildPhases = ( + ); + buildSettings = { + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "Build All"; + SECTORDER_FLAGS = ""; + }; + dependencies = ( + 00AD62BC032D7A160CCA2C71, + 00AD62BD032D7A1B0CCA2C71, + 00AD62BE032D7A1D0CCA2C71, + 0066920A04CC7AA80CCA2C71, + ); + isa = PBXAggregateTarget; + name = "Build All"; + productName = "Build All"; + shouldUseHeadermap = 0; + }; + 00AD62BC032D7A160CCA2C71 = { + isa = PBXTargetDependency; + target = 08FB779FFE84155DC02AAC07; + }; + 00AD62BD032D7A1B0CCA2C71 = { + isa = PBXTargetDependency; + target = 00AD62A3032D799A0CCA2C71; + }; + 00AD62BE032D7A1D0CCA2C71 = { + isa = PBXTargetDependency; + target = 6575FC1C022EB76000000109; + }; + 00B2AB0C032D7B220CCA2C71 = { buildRules = ( ); buildSettings = { - COPY_PHASE_STRIP = YES; - OTHER_CFLAGS = "-D__MACOSX__"; + MVERS = 1; }; isa = PBXBuildStyle; - name = Deployment; + name = Development; + }; + 00CA213D02786FC30CCA2C71 = { + isa = PBXFrameworkReference; + name = IOKit.framework; + path = /System/Library/Frameworks/IOKit.framework; + refType = 0; }; -//010 -//011 -//012 -//013 -//014 + 00DD152B04CC79700CCA2C71 = { + fileRef = 004EFB9604CC78130CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + 00DD152C04CC79A50CCA2C71 = { + fileRef = F5E11B5A04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; +//000 +//001 +//002 +//003 +//004 //030 //031 //032 @@ -69,15 +319,17 @@ //084 08FB7793FE84155DC02AAC07 = { buildStyles = ( - 014CEA490018CE3211CA2923, - 014CEA4A0018CE3211CA2923, + 00B2AB0C032D7B220CCA2C71, ); isa = PBXProject; mainGroup = 08FB7794FE84155DC02AAC07; projectDirPath = ""; targets = ( + 00AD62BB032D7A0C0CCA2C71, 08FB779FFE84155DC02AAC07, + 00AD62A3032D799A0CCA2C71, 6575FC1C022EB76000000109, + 0044D34804CC73600CCA2C71, ); }; 08FB7794FE84155DC02AAC07 = { @@ -94,19 +346,20 @@ }; 08FB7795FE84155DC02AAC07 = { children = ( + F525E72804AA167501F1CF4D, + F5E11B5A04A28126019798ED, + F5E11B5B04A28126019798ED, 6575FBEC022EAF7200000109, 6575FBE9022EAF5A00000109, - 6575FC12022EB27800000109, 6575FBEB022EAF7200000109, 654BE64F02B63B93000001D1, 654BE65002B63B93000001D1, - 654BE65102B63B93000001D1, 654BE65202B63B93000001D1, - 654BE65302B63B93000001D1, - 654BE65402B63B93000001D1, + 000753D303367C1C0CCA2C71, ); isa = PBXGroup; name = "mDNS Server Sources"; + path = ""; refType = 4; }; 08FB779DFE84155DC02AAC07 = { @@ -128,16 +381,18 @@ ); buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; INSTALL_PATH = /usr/sbin; LIBRARY_SEARCH_PATHS = ""; - OTHER_CFLAGS = "-D__MACOSX__"; + OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder; REZ_EXECUTABLE = YES; SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; }; dependencies = ( ); @@ -152,12 +407,7 @@ buildActionMask = 2147483647; files = ( 6575FC02022EAFBA00000109, - 654BE65502B63B93000001D1, - 654BE65602B63B93000001D1, - 654BE65702B63B93000001D1, - 654BE65802B63B93000001D1, - 654BE65902B63B93000001D1, - 654BE65A02B63B93000001D1, + F5E11B5D04A28126019798ED, ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -170,7 +420,8 @@ 6575FBEA022EAF5A00000109, 6575FBED022EAF7200000109, 6575FBEE022EAF7200000109, - 6575FC15022EB27800000109, + F5E11B5C04A28126019798ED, + F525E72904AA167501F1CF4D, ); isa = PBXSourcesBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -228,6 +479,8 @@ children = ( 034768E2FF38A6DC11DB9C8B, 6575FC1D022EB76000000109, + 00AD62B8032D799A0CCA2C71, + 0044D34F04CC73600CCA2C71, ); isa = PBXGroup; name = Products; @@ -246,75 +499,21 @@ 654BE64F02B63B93000001D1 = { isa = PBXFileReference; name = mDNSClientAPI.h; - path = mDNSCore/mDNSClientAPI.h; + path = ../mDNSCore/mDNSClientAPI.h; refType = 4; }; 654BE65002B63B93000001D1 = { isa = PBXFileReference; name = mDNSDebug.h; - path = mDNSCore/mDNSDebug.h; - refType = 4; - }; - 654BE65102B63B93000001D1 = { - isa = PBXFileReference; - name = mDNSPlatformEnvironment.h; - path = mDNSCore/mDNSPlatformEnvironment.h; + path = ../mDNSCore/mDNSDebug.h; refType = 4; }; 654BE65202B63B93000001D1 = { isa = PBXFileReference; name = mDNSPlatformFunctions.h; - path = mDNSCore/mDNSPlatformFunctions.h; - refType = 4; - }; - 654BE65302B63B93000001D1 = { - isa = PBXFileReference; - name = mDNSsprintf.h; - path = mDNSCore/mDNSsprintf.h; + path = ../mDNSCore/mDNSPlatformFunctions.h; refType = 4; }; - 654BE65402B63B93000001D1 = { - isa = PBXFileReference; - name = mDNSvsprintf.h; - path = mDNSCore/mDNSvsprintf.h; - refType = 4; - }; - 654BE65502B63B93000001D1 = { - fileRef = 654BE64F02B63B93000001D1; - isa = PBXBuildFile; - settings = { - }; - }; - 654BE65602B63B93000001D1 = { - fileRef = 654BE65002B63B93000001D1; - isa = PBXBuildFile; - settings = { - }; - }; - 654BE65702B63B93000001D1 = { - fileRef = 654BE65102B63B93000001D1; - isa = PBXBuildFile; - settings = { - }; - }; - 654BE65802B63B93000001D1 = { - fileRef = 654BE65202B63B93000001D1; - isa = PBXBuildFile; - settings = { - }; - }; - 654BE65902B63B93000001D1 = { - fileRef = 654BE65302B63B93000001D1; - isa = PBXBuildFile; - settings = { - }; - }; - 654BE65A02B63B93000001D1 = { - fileRef = 654BE65402B63B93000001D1; - isa = PBXBuildFile; - settings = { - }; - }; 65713D46025A293200000109 = { isa = PBXFrameworkReference; name = SystemConfiguration.framework; @@ -331,7 +530,7 @@ indentWidth = 4; isa = PBXFileReference; name = mDNS.c; - path = mDNSCore/mDNS.c; + path = ../mDNSCore/mDNS.c; refType = 4; tabWidth = 4; usesTabs = 1; @@ -420,18 +619,6 @@ ); }; }; - 6575FC12022EB27800000109 = { - isa = PBXFileReference; - name = mDNSsprintf.c; - path = mDNSCore/mDNSsprintf.c; - refType = 4; - }; - 6575FC15022EB27800000109 = { - fileRef = 6575FC12022EB27800000109; - isa = PBXBuildFile; - settings = { - }; - }; 6575FC18022EB76000000109 = { buildActionMask = 2147483647; files = ( @@ -470,19 +657,22 @@ 6575FC1B022EB76000000109, ); buildSettings = { - OTHER_CFLAGS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + INSTALL_PATH = /usr/bin; + OTHER_CFLAGS = "-no-cpp-precomp -DmDNSResponderVersion=$(MVERS)"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNS; REZ_EXECUTABLE = YES; SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; }; dependencies = ( ); isa = PBXToolTarget; - name = mDNS; - productInstallPath = /usr/local/bin; + name = "mDNS Command-Line tool"; + productInstallPath = /usr/bin; productName = "Sample mDNS Client"; productReference = 6575FC1D022EB76000000109; shouldUseHeadermap = 0; @@ -495,6 +685,8 @@ 6575FC1F022EB78C00000109 = { children = ( 6575FC20022EB7AA00000109, + 0017390704CC75C30CCA2C71, + 004EFB9604CC78130CCA2C71, ); isa = PBXGroup; name = SampleMulticastDNSClient; @@ -526,6 +718,91 @@ settings = { }; }; +//650 +//651 +//652 +//653 +//654 +//F50 +//F51 +//F52 +//F53 +//F54 + F515E29604A37BB701CA296C = { + fileRef = 654BE64F02B63B93000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + F515E29704A37BB801CA296C = { + fileRef = 654BE65002B63B93000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + F515E29804A37BBB01CA296C = { + fileRef = 654BE65202B63B93000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + F515E29904A37BBB01CA296C = { + fileRef = 000753D303367C1C0CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + F525E72804AA167501F1CF4D = { + isa = PBXFileReference; + path = uds_daemon.c; + refType = 4; + }; + F525E72904AA167501F1CF4D = { + fileRef = F525E72804AA167501F1CF4D; + isa = PBXBuildFile; + settings = { + }; + }; + F525E72B04AA167A01F1CF4D = { + fileRef = F525E72804AA167501F1CF4D; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5A04A28126019798ED = { + isa = PBXFileReference; + path = dnssd_ipc.c; + refType = 4; + }; + F5E11B5B04A28126019798ED = { + isa = PBXFileReference; + path = dnssd_ipc.h; + refType = 4; + }; + F5E11B5C04A28126019798ED = { + fileRef = F5E11B5A04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5D04A28126019798ED = { + fileRef = F5E11B5B04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5E04A28126019798ED = { + fileRef = F5E11B5A04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5F04A28126019798ED = { + fileRef = F5E11B5B04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; }; rootObject = 08FB7793FE84155DC02AAC07; } diff --git a/mDNSMacOSX/uds_daemon.c b/mDNSMacOSX/uds_daemon.c new file mode 100644 index 0000000..c7df012 --- /dev/null +++ b/mDNSMacOSX/uds_daemon.c @@ -0,0 +1,2212 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: uds_daemon.c,v $ +Revision 1.22 2003/08/19 16:03:55 ksekar +Bug #: : ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord +Check termination_context for NULL before dereferencing. + +Revision 1.21 2003/08/19 05:39:43 cheshire + SIGINFO dump should include resolves started by DNSServiceQueryRecord + +Revision 1.20 2003/08/16 03:39:01 cheshire + InterfaceID -1 indicates "local only" + +Revision 1.19 2003/08/15 20:16:03 cheshire + mDNSResponder takes too much RPRVT +We want to avoid touching the rdata pages, so we don't page them in. +1. RDLength was stored with the rdata, which meant touching the page just to find the length. + Moved this from the RData to the ResourceRecord object. +2. To avoid unnecessarily touching the rdata just to compare it, + compute a hash of the rdata and store the hash in the ResourceRecord object. + +Revision 1.18 2003/08/15 00:38:00 ksekar +Bug #: : Bug: buffer overrun when reading long rdata from client + +Revision 1.17 2003/08/14 02:18:21 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.16 2003/08/13 23:58:52 ksekar +Bug #: : Bug: UDS Sub-type browsing works, but not sub-type registration +Fixed pointer increment error, moved subtype reading for-loop for easier error bailout. + +Revision 1.15 2003/08/13 17:30:33 ksekar +Bug #: : DNSServiceAddRecord doesn't work +Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords. + +Revision 1.14 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#include "mDNSClientAPI.h" +#include "mDNSMacOSX.h" +#include "dns_sd.h" +#include "dnssd_ipc.h" +#include +#include +#include +#include +#include + +// Types and Data Structures +// ---------------------------------------------------------------------- + +typedef enum + { + t_uninitialized, + t_morecoming, + t_complete, + t_error, + t_terminated + } transfer_state; + +typedef void (*req_termination_fn)(void *); + + +typedef struct registered_record_entry + { + int key; + AuthRecord *rr; + struct registered_record_entry *next; + } registered_record_entry; + +typedef struct registered_service + { + //struct registered_service *next; + int autoname; + int renameonconflict; + int rename_on_memfree; // set flag on config change when we deregister original name + domainlabel name; + ServiceRecordSet *srs; + struct request_state *request; + AuthRecord *subtypes; + } registered_service; + +typedef struct + { + mStatus err; + int nwritten; + int sd; + } undelivered_error_t; + +typedef struct request_state + { + // connection structures + CFRunLoopSourceRef rls; + CFSocketRef sr; + int sd; + int errfd; + + // state of read (in case message is read over several recv() calls) + transfer_state ts; + uint32_t hdr_bytes; // bytes of header already read + ipc_msg_hdr hdr; + uint32_t data_bytes; // bytes of message data already read + char *msgbuf; // pointer to data storage to pass to free() + char *msgdata; // pointer to data to be read from (may be modified) + int bufsize; // size of data storage + + // reply, termination, error, and client context info + int no_reply; // don't send asynchronous replies to client + void *client_context; // don't touch this - pointer only valid in client's addr space + struct reply_state *replies; // corresponding (active) reply list + undelivered_error_t *u_err; + void *termination_context; + req_termination_fn terminate; + + //!!!KRS toss these pointers in a union + // registration context associated with this request (null if not applicable) + registered_record_entry *reg_recs; // muliple registrations for a connection-oriented request + registered_service *service; // service record set and flags + struct resolve_result_t *resolve_results; + + struct request_state *next; + } request_state; + +// struct physically sits between ipc message header and call-specific fields in the message buffer +typedef struct + { + DNSServiceFlags flags; + uint32_t ifi; + DNSServiceErrorType error; + } reply_hdr; + + +typedef struct reply_state + { + // state of the transmission + int sd; + transfer_state ts; + uint32_t nwriten; + uint32_t len; + // context of the reply + struct request_state *request; // the request that this answers + struct reply_state *next; // if there are multiple unsent replies + // pointer into message buffer - allows fields to be changed after message is formatted + ipc_msg_hdr *mhdr; + reply_hdr *rhdr; + char *sdata; // pointer to start of call-specific data + // pointer to malloc'd buffer + char *msgbuf; + } reply_state; + + +// domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected +// structures to handle callbacks +typedef struct + { + DNSQuestion question; + mDNS_DomainType type; + request_state *rstate; + } domain_enum_t; + +typedef struct + { + domain_enum_t *all; + domain_enum_t *def; + request_state *rstate; + } enum_termination_t; + +typedef struct + { + DNSQuestion question; + uint16_t qtype; + request_state *rstate; + } resolve_t; + +typedef struct + { + resolve_t *txt; + resolve_t *srv; + request_state *rstate; + } resolve_termination_t; + +typedef struct resolve_result_t + { + const ResourceRecord *txt; + const ResourceRecord *srv; + } resolve_result_t; + +typedef struct + { + request_state *rstate; + client_context_t client_context; + } regrecord_callback_context; + + + + +// globals +static int listenfd = -1; +static request_state *all_requests = NULL; +//!!!KRS we should keep a separate list containing only the requests that need to be examined +//in the idle() routine. + + +#define MAX_OPENFILES 1024 + + +// private function prototypes +static void connect_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i); +static int read_msg(request_state *rs); +static int send_msg(reply_state *rs); +static void abort_request(request_state *rs); +static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i); +static void handle_resolve_request(request_state *rstate); +static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +static void question_termination_callback(void *context); +static void handle_browse_request(request_state *request); +static void browse_termination_callback(void *context); +static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +static void handle_regservice_request(request_state *request); +static void regservice_termination_callback(void *context); +static void process_service_registration(ServiceRecordSet *const srs); +static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result); +static void handle_add_request(request_state *rstate); +static void handle_update_request(request_state *rstate); +static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep); +static void append_reply(request_state *req, reply_state *rep); +static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain); +static void enum_termination_callback(void *context); +static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +static void handle_query_request(request_state *rstate); +static mStatus do_question(request_state *rstate, domainname *name, uint32_t ifi, uint16_t rrtype, int16_t rrclass); +static reply_state *format_enumeration_reply(request_state *rstate, char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err); +static void handle_enum_request(request_state *rstate); +static void handle_regrecord_request(request_state *rstate); +static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus result); +static void connected_registration_termination(void *context); +static void handle_reconfirm_request(request_state *rstate); +static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl); +static void handle_removerecord_request(request_state *rstate); +static void reset_connected_rstate(request_state *rstate); +static int deliver_error(request_state *rstate, mStatus err); +static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err); +static transfer_state send_undelivered_error(request_state *rs); +static reply_state *create_reply(reply_op_t op, int datalen, request_state *request); +static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd); +static void my_perror(char *errmsg); +static void unlink_request(request_state *rs); +static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord); +static void resolve_termination_callback(void *context); + +// initialization, setup/teardown functions + +int udsserver_init(void) + { + mode_t mask; + struct sockaddr_un laddr; + struct rlimit maxfds; + + if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) + goto error; + unlink(MDNS_UDS_SERVERPATH); //OK if this fails + bzero(&laddr, sizeof(laddr)); + laddr.sun_family = AF_LOCAL; + laddr.sun_len = sizeof(struct sockaddr_un); + strcpy(laddr.sun_path, MDNS_UDS_SERVERPATH); + mask = umask(0); + if (bind(listenfd, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) + goto error; + umask(mask); + + if (fcntl(listenfd, F_SETFL, O_NONBLOCK) < 0) + { + my_perror("ERROR: could not set listen socket to non-blocking mode"); + goto error; + } + listen(listenfd, LISTENQ); + + + // set maximum file descriptor to 1024 + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) + { + my_perror("ERROR: Unable to get file descriptor limit"); + return 0; + } + if (maxfds.rlim_max >= MAX_OPENFILES && maxfds.rlim_cur == maxfds.rlim_max) + { + // proper values already set + return 0; + } + maxfds.rlim_max = MAX_OPENFILES; + maxfds.rlim_cur = MAX_OPENFILES; + if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) + my_perror("ERROR: Unable to set maximum file descriptor limit"); + return 0; + +error: + my_perror("ERROR: udsserver_init"); + return -1; + } + +int udsserver_exit(void) + { + close(listenfd); + unlink(MDNS_UDS_SERVERPATH); + return 0; + } + + +// add the named socket as a runloop source +int udsserver_add_rl_source(void) + { + CFSocketContext context = { 0, NULL, NULL, NULL, NULL }; + CFSocketRef sr = CFSocketCreateWithNative(kCFAllocatorDefault, listenfd, kCFSocketReadCallBack, connect_callback, &context); + if (!sr) + { + debugf("ERROR: udsserver_add_rl_source - CFSocketCreateWithNative"); + return -1; + } + CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sr, 0); + + if (!rls) + { + debugf("ERROR: udsserver_add_rl_source - CFSocketCreateRunLoopSource"); + return -1; + } + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + return 0; + } + +mDNSs32 udsserver_idle(mDNSs32 nextevent) + { + request_state *req = all_requests, *tmp, *prev = NULL; + reply_state *fptr; + transfer_state result; + mDNSs32 now = mDNSPlatformTimeNow(); + + + while(req) + { + result = t_uninitialized; + if (req->u_err) + result = send_undelivered_error(req); + if (result != t_error && result != t_morecoming && // don't try to send msg if send_error failed + (req->ts == t_complete || req->ts == t_morecoming)) + { + while(req->replies) + { + if (req->replies->next) req->replies->rhdr->flags |= kDNSServiceFlagsMoreComing; + else req->replies->rhdr->flags |= kDNSServiceFlagsFinished; + result = send_msg(req->replies); + if (result == t_complete) + { + fptr = req->replies; + req->replies = req->replies->next; + freeL("udsserver_idle", fptr); + } + else if (result == t_terminated || result == t_error) + { + abort_request(req); + break; + } + else if (result == t_morecoming) // client's queues are full, move to next + { + if (nextevent - now > mDNSPlatformOneSecond) + nextevent = now + mDNSPlatformOneSecond; + break; // start where we left off in a second + } + } + } + if (result == t_terminated || result == t_error) + //since we're already doing a list traversal, we unlink the request manunally instead of calling unlink_request() + { + tmp = req; + if (prev) prev->next = req->next; + if (req == all_requests) all_requests = all_requests->next; + req = req->next; + freeL("udsserver_idle", tmp); + } + else + { + prev = req; + req = req->next; + } + } + return nextevent; + } + +void udsserver_info(void) + { + request_state *req; + for (req = all_requests; req; req=req->next) + { + void *t = req->termination_context; + if (!t) continue; + if (req->terminate == regservice_termination_callback) + LogMsg("DNSServiceRegister %##s", ((registered_service *) t)->srs->RR_SRV.resrec.name.c); + else if (req->terminate == browse_termination_callback) + LogMsg("DNSServiceBrowse %##s", ((DNSQuestion *) t)->qname.c); + else if (req->terminate == resolve_termination_callback) + LogMsg("DNSServiceResolve %##s", ((resolve_termination_t *)t)->srv->question.qname.c); + else if (req->terminate == question_termination_callback) + LogMsg("DNSServiceQueryRecord %##s", ((DNSQuestion *) t)->qname.c); + else if (req->terminate == enum_termination_callback) + LogMsg("DNSServiceEnumerateDomains %##s", ((enum_termination_t *) t)->all->question.qname.c); + } + } + +void udsserver_handle_configchange(void) + { + registered_service *srv; + request_state *req; + mStatus err; + + for (req = all_requests; req; req = req->next) + { + srv = req->service; + if (srv->autoname && !SameDomainLabel(srv->name.c, mDNSStorage.nicelabel.c)) + { + srv->rename_on_memfree = 1; + err = mDNS_DeregisterService(&mDNSStorage, srv->srs); + if (err) LogMsg("ERROR: udsserver_handle_configchange: DeregisterService returned error %d. Continuing.", err); + // error should never occur - safest to log and continue + } + } + } + + + + +// accept a connection on the named socket, adding the new descriptor to the runloop and passing the error +// descriptor to the client +static void connect_callback(CFSocketRef s, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i) + { + int sd, clilen, optval; + struct sockaddr_un cliaddr; + CFSocketContext context = { 0, NULL, NULL, NULL, NULL }; + request_state *rstate; +// int errpipe[2]; + + #pragma unused(s, t, dr, c, i) + + clilen = sizeof(cliaddr); + sd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen); + + if (sd < 0) + { + if (errno == EWOULDBLOCK) return; + my_perror("ERROR: accept"); + return; + } + optval = 1; + if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) + { + my_perror("ERROR: setsockopt - SOL_NOSIGPIPE - aborting client"); + close(sd); + return; + } + + if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) + { + my_perror("ERROR: could not set connected socket to non-blocking mode - aborting client"); + close(sd); + return; + } + +/* + // open a pipe to deliver error messages, pass descriptor to client + if (pipe(errpipe) < 0) + { + my_perror("ERROR: could not create pipe"); + exit(1); + } + + if (ioctl(sd, I_SENDFD, errpipe[0]) < 0) + { + my_perror("ERROR: could not pass pipe descriptor to client. Aborting client.\n"); + close(sd); + return; + } + if (fcntl(errpipe[1], F_SETFL, O_NONBLOCK) < 0) + { + my_perror("ERROR: could not set error pipe to non-blocking mode - aborting client"); + close(sd); + close(errpipe[1]); + return; + } + */ + + // allocate a request_state struct that will live with the socket + rstate = mallocL("connect_callback", sizeof(request_state)); + if (!rstate) + { + my_perror("ERROR: malloc"); + exit(1); + } + bzero(rstate, sizeof(request_state)); + rstate->ts = t_morecoming; + rstate->sd = sd; + //rstate->errfd = errpipe[1]; + + //now create CFSocket wrapper and add to run loop + context.info = rstate; + rstate->sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketReadCallBack, request_callback, &context); + if (!rstate->sr) + { + debugf("ERROR: connect_callback - CFSocketCreateWithNative"); + freeL("connect_callback", rstate); + close(sd); + return; + } + rstate->rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, rstate->sr, 0); + if (!rstate->rls) + { + debugf("ERROR: connect_callback - CFSocketCreateRunLoopSource"); + CFSocketInvalidate(rstate->sr); // automatically closes socket + CFRelease(rstate->sr); + freeL("connect_callback", rstate); + return; + } + CFRunLoopAddSource(CFRunLoopGetCurrent(), rstate->rls, kCFRunLoopDefaultMode); + if (!CFRunLoopContainsSource(CFRunLoopGetCurrent(), rstate->rls, kCFRunLoopDefaultMode)) + { + LogMsg("ERROR: connect_callback, CFRunLoopAddSource"); + abort_request(rstate); + return; + } + rstate->next = all_requests; + all_requests = rstate; + } + + +// main client request handling routine. reads request and calls the appropriate request-specific +// handler +static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *context, void *info) + { + request_state *rstate = info; + transfer_state result; + struct sockaddr_un cliaddr; + char ctrl_path[MAX_CTLPATH]; + + #pragma unused(sr, t, dr, context) + + int native = CFSocketGetNative(sr); + if (native != rstate->sd) + { + LogMsg("ERROR: request_callback - CFSocket's native descriptor does not match rstate member descriptor."); + abort_request(rstate); + unlink_request(rstate); + return; + } + + result = read_msg(rstate); + if (result == t_morecoming) + { + return; + } + if (result == t_terminated) + { + abort_request(rstate); + unlink_request(rstate); + return; + } + if (result == t_error) + { + abort_request(rstate); + unlink_request(rstate); + return; + } + + if (rstate->hdr.version != VERSION) + { + LogMsg("ERROR: client incompatible with daemon (client version = %d, " + "daemon version = %d)\n", rstate->hdr.version, VERSION); + abort_request(rstate); + unlink_request(rstate); + return; + } + + // check if client wants silent operation + if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1; + + // check if primary socket is to be used for synchronous errors, else open new socket + if (rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET) + rstate->errfd = rstate->sd; + else + { + if ((rstate->errfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) + { + my_perror("ERROR: socket"); + exit(1); + } + if (fcntl(rstate->errfd, F_SETFL, O_NONBLOCK) < 0) + { + my_perror("ERROR: could not set control socket to non-blocking mode"); + abort_request(rstate); + unlink_request(rstate); + return; + } + get_string(&rstate->msgdata, ctrl_path, 256); // path is first element in message buffer + bzero(&cliaddr, sizeof(cliaddr)); + cliaddr.sun_family = AF_LOCAL; + strcpy(cliaddr.sun_path, ctrl_path); + if (connect(rstate->errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) + { + my_perror("ERROR: connect"); + abort_request(rstate); + unlink_request(rstate); + } + } + + + + + switch(rstate->hdr.op.request_op) + { + case resolve_request: handle_resolve_request(rstate); break; + case query_request: handle_query_request(rstate); break; + case browse_request: handle_browse_request(rstate); break; + case reg_service_request: handle_regservice_request(rstate); break; + case enumeration_request: handle_enum_request(rstate); break; + case reg_record_request: handle_regrecord_request(rstate); break; + case add_record_request: handle_add_request(rstate); break; + case update_record_request: handle_update_request(rstate); break; + case remove_record_request: handle_removerecord_request(rstate); break; + case reconfirm_record_request: handle_reconfirm_request(rstate); break; + default: + debugf("ERROR: udsserver_recv_request - unsupported request type: %d", rstate->hdr.op.request_op); + } + } + +// mDNS operation functions. Each operation has 3 associated functions - a request handler that parses +// the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback +// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts +// the mDNSCore operation if the client dies or closes its socket. + + +// query and resolve calls have separate request handlers that parse the arguments from the client and +// massage the name parameters appropriately, but the rest of the operations (making the query call, +// delivering the result to the client, and termination) are identical. + +static void handle_query_request(request_state *rstate) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + char name[256]; + uint16_t rrtype, rrclass; + char *ptr; + domainname dname; + mStatus result; + + if (rstate->ts != t_complete) + { + LogMsg("ERROR: handle_query_request - transfer state != t_complete"); + goto error; + } + ptr = rstate->msgdata; + if (!ptr) + { + LogMsg("ERROR: handle_query_request - NULL msgdata"); + goto error; + } + flags = get_flags(&ptr); + interfaceIndex = get_long(&ptr); + if (get_string(&ptr, name, 256) < 0) goto bad_param; + rrtype = get_short(&ptr); + rrclass = get_short(&ptr); + if (!MakeDomainNameFromDNSNameString(&dname, name)) goto bad_param; + result = do_question(rstate, &dname, interfaceIndex, rrtype, rrclass); + if (result) rstate->terminate = NULL; + if (deliver_error(rstate, result) < 0) goto error; + return; + +bad_param: + deliver_error(rstate, mStatus_BadParamErr); + rstate->terminate = NULL; // don't try to terminate insuccessful Core calls +error: + abort_request(rstate); + unlink_request(rstate); + return; + } + +static void handle_resolve_request(request_state *rstate) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + char name[256], regtype[256], domain[256]; + char *ptr; // message data pointer + domainname fqdn; + resolve_t *srv, *txt; + resolve_termination_t *term; + mStatus err; + + if (rstate->ts != t_complete) + { + LogMsg("ERROR: handle_resolve_request - transfer state != t_complete"); + abort_request(rstate); + unlink_request(rstate); + return; + } + + // extract the data from the message + ptr = rstate->msgdata; + if (!ptr) + { + LogMsg("ERROR: handle_resolve_request - NULL msgdata"); + abort_request(rstate); + unlink_request(rstate); + return; + } + flags = get_flags(&ptr); + interfaceIndex = get_long(&ptr); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (interfaceIndex && !InterfaceID) goto bad_param; + if (get_string(&ptr, name, 256) < 0 || + get_string(&ptr, regtype, 256) < 0 || + get_string(&ptr, domain, 256) < 0) + goto bad_param; + + // free memory in rstate since we don't need it anymore + freeL("handle_resolve_request", rstate->msgbuf); + rstate->msgbuf = NULL; + + if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0) + goto bad_param; + + // allocate question wrapper structs + srv = mallocL("handle_resolve_request", sizeof(resolve_t)); + txt = mallocL("handle_resolve_request", sizeof(resolve_t)); + if (!srv || !txt) goto malloc_error; + srv->qtype = kDNSType_SRV; + txt->qtype = kDNSType_TXT; + srv->rstate = rstate; + txt->rstate = rstate; + + // format questions + srv->question.QuestionContext = rstate; + srv->question.QuestionCallback = resolve_result_callback; + memcpy(&srv->question.qname, &fqdn, MAX_DOMAIN_NAME); + srv->question.qtype = kDNSType_SRV; + srv->question.qclass = kDNSClass_IN; + srv->question.InterfaceID = InterfaceID; + + txt->question.QuestionContext = rstate; + txt->question.QuestionCallback = resolve_result_callback; + memcpy(&txt->question.qname, &fqdn, MAX_DOMAIN_NAME); + txt->question.qtype = kDNSType_TXT; + txt->question.qclass = kDNSClass_IN; + txt->question.InterfaceID = InterfaceID; + + // set up termination info + term = mallocL("handle_resolve_request", sizeof(resolve_termination_t)); + if (!term) goto malloc_error; + term->srv = srv; + term->txt = txt; + term->rstate = rstate; + rstate->termination_context = term; + rstate->terminate = resolve_termination_callback; + + // set up reply wrapper struct (since answer will come via 2 callbacks) + rstate->resolve_results = mallocL("handle_resolve_response", sizeof(resolve_result_t)); + if (!rstate->resolve_results) goto malloc_error; + bzero(rstate->resolve_results, sizeof(resolve_result_t)); + + // ask the questions + err = mDNS_StartQuery(&mDNSStorage, &srv->question); + if (!err) err = mDNS_StartQuery(&mDNSStorage, &txt->question); + + if (err) + { + freeL("handle_resolve_request", txt); + freeL("handle_resolve_request", srv); + freeL("handle_resolve_request", term); + freeL("handle_resolve_request", rstate->resolve_results); + rstate->terminate = NULL; // prevent abort_request() from invoking termination callback + } + if (deliver_error(rstate, err) < 0 || err) + { + abort_request(rstate); + unlink_request(rstate); + } + return; + +bad_param: + deliver_error(rstate, mStatus_BadParamErr); + abort_request(rstate); + unlink_request(rstate); + return; + +malloc_error: + my_perror("ERROR: malloc"); + exit(1); + } + +static void resolve_termination_callback(void *context) + { + resolve_termination_t *term = context; + request_state *rs; + + if (!term) + { + LogMsg("ERROR: resolve_termination_callback: double termination"); + return; + } + rs = term->rstate; + + mDNS_StopQuery(&mDNSStorage, &term->txt->question); + mDNS_StopQuery(&mDNSStorage, &term->srv->question); + + freeL("resolve_termination_callback", term->txt); + freeL("resolve_termination_callback", term->srv); + freeL("resolve_termination_callback", term); + rs->termination_context = NULL; + freeL("resolve_termination_callback", rs->resolve_results); + rs->resolve_results = NULL; + } + + + +static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +{ + int len = 0; + char fullname[MAX_DOMAIN_NAME], target[MAX_DOMAIN_NAME]; + char *data; + transfer_state result; + reply_state *rep; + request_state *rs = question->QuestionContext; + resolve_result_t *res = rs->resolve_results; + #pragma unused(m) + + if (!AddRecord) + { + if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL; + if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL; + return; + } + + if (answer->rrtype == kDNSType_TXT) res->txt = answer; + if (answer->rrtype == kDNSType_SRV) res->srv = answer; + + if (!res->txt || !res->srv) return; // only deliver result to client if we have both answers + + ConvertDomainNameToCString(&answer->name, fullname); + ConvertDomainNameToCString(&res->srv->rdata->u.srv.target, target); + + // calculate reply length + len += sizeof(DNSServiceFlags); + len += sizeof(uint32_t); // interface index + len += sizeof(DNSServiceErrorType); + len += strlen(fullname) + 1; + len += strlen(target) + 1; + len += 2 * sizeof(uint16_t); // port, txtLen + len += res->txt->rdlength; + + // allocate/init reply header + rep = create_reply(resolve_reply, len, rs); + rep->rhdr->flags = 0; + rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID); + rep->rhdr->error = kDNSServiceErr_NoError; + data = rep->sdata; + + // write reply data to message + put_string(fullname, &data); + put_string(target, &data); + put_short(res->srv->rdata->u.srv.port.NotAnInteger, &data); + put_short(res->txt->rdlength, &data); + put_rdata(res->txt->rdlength, res->txt->rdata->u.txt.c, &data); + + result = send_msg(rep); + if (result == t_error || result == t_terminated) + { + abort_request(rs); + unlink_request(rs); + freeL("resolve_result_callback", rep); + } + else if (result == t_complete) freeL("resolve_result_callback", rep); + else append_reply(rs, rep); + } + + + + +// common query issuing routine for resolve and query requests +static mStatus do_question(request_state *rstate, domainname *name, uint32_t ifi, uint16_t rrtype, int16_t rrclass) + { + DNSQuestion *q; + mStatus result; + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi); + if (ifi && !InterfaceID) return(mStatus_BadParamErr); + + q = mallocL("do_question", sizeof(DNSQuestion)); + if (!q) + { + my_perror("ERROR: do_question - malloc"); + exit(1); + } + bzero(q, sizeof(DNSQuestion)); + + q->QuestionContext = rstate; + q->QuestionCallback = question_result_callback; + memcpy(&q->qname, name, MAX_DOMAIN_NAME); + q->qtype = rrtype; + q->qclass = rrclass; + q->InterfaceID = InterfaceID; + + + rstate->termination_context = q; + rstate->terminate = question_termination_callback; + + result = mDNS_StartQuery(&mDNSStorage, q); + if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result); + return result; + } + +// what gets called when a resolve is completed and we need to send the data back to the client +static void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + char *data; + char name[256]; + request_state *req; + reply_state *rep; + int len; + + #pragma unused(m) + //mDNS_StopQuery(m, question); + req = question->QuestionContext; + + // calculate reply data length + len = sizeof(DNSServiceFlags); + len += 2 * sizeof(uint32_t); // if index + ttl + len += sizeof(DNSServiceErrorType); + len += 3 * sizeof(uint16_t); // type, class, rdlen + len += answer->rdlength; + ConvertDomainNameToCString(&answer->name, name); + len += strlen(name) + 1; + + rep = create_reply(query_reply, len, req); + rep->rhdr->flags = AddRecord ? kDNSServiceFlagsAdd : kDNSServiceFlagsRemove; + rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID); + rep->rhdr->error = kDNSServiceErr_NoError; + data = rep->sdata; + + put_string(name, &data); + put_short(answer->rrtype, &data); + put_short(answer->rrclass, &data); + put_short(answer->rdlength, &data); + put_rdata(answer->rdlength, (char *)&answer->rdata->u, &data); + put_long(AddRecord ? answer->rroriginalttl : 0, &data); + + append_reply(req, rep); + return; + } + +static void question_termination_callback(void *context) + { + DNSQuestion *q = context; + + + mDNS_StopQuery(&mDNSStorage, q); // no need to error check + freeL("question_termination_callback", q); + } + + +static void handle_browse_request(request_state *request) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + char regtype[256], domain[256]; + DNSQuestion *q; + domainname typedn, domdn; + char *ptr; + mStatus result; + + if (request->ts != t_complete) + { + LogMsg("ERROR: handle_browse_request - transfer state != t_complete"); + abort_request(request); + unlink_request(request); + return; + } + q = mallocL("handle_browse_request", sizeof(DNSQuestion)); + if (!q) + { + my_perror("ERROR: handle_browse_request - malloc"); + exit(1); + } + bzero(q, sizeof(DNSQuestion)); + + // extract data from message + ptr = request->msgdata; + flags = get_flags(&ptr); + interfaceIndex = get_long(&ptr); + if (get_string(&ptr, regtype, 256) < 0 || + get_string(&ptr, domain, 256) < 0) + goto bad_param; + + freeL("handle_browse_request", request->msgbuf); + request->msgbuf = NULL; + + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (interfaceIndex && !InterfaceID) goto bad_param; + q->QuestionContext = request; + q->QuestionCallback = browse_result_callback; + if (!MakeDomainNameFromDNSNameString(&typedn, regtype) || + !MakeDomainNameFromDNSNameString(&domdn, domain[0] ? domain : "local.")) + goto bad_param; + request->termination_context = q; + request->terminate = browse_termination_callback; + result = mDNS_StartBrowse(&mDNSStorage, q, &typedn, &domdn, InterfaceID, browse_result_callback, request); + deliver_error(request, result); + return; + +bad_param: + deliver_error(request, mStatus_BadParamErr); + abort_request(request); + unlink_request(request); + } + +static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + request_state *req; + reply_state *rep; + mStatus err; + + #pragma unused(m) + req = question->QuestionContext; + + err = gen_rr_response(&answer->rdata->u.name, answer->InterfaceID, req, &rep); + if (err) + { + if (deliver_async_error(req, browse_reply, err) < 0) + { + abort_request(req); + unlink_request(req); + } + return; + } + if (AddRecord) rep->rhdr->flags |= kDNSServiceFlagsAdd; // non-zero TTL indicates add + append_reply(req, rep); + return; + } + +static void browse_termination_callback(void *context) + { + DNSQuestion *q = context; + + mDNS_StopBrowse(&mDNSStorage, q); // no need to error-check result + freeL("browse_termination_callback", q); + } + +// service registration +static void handle_regservice_request(request_state *request) + { + DNSServiceFlags flags; + uint32_t ifi; + char name[256], regtype[256], domain[256], host[256]; + uint16_t txtlen; + mDNSIPPort port; + void *txtdata; + char *ptr; + domainlabel n; + domainname t, d, h, srv; + registered_service *r_srv; + int srs_size; + mStatus result; + + char *sub, *rtype_ptr; + int i, num_subtypes; + + + if (request->ts != t_complete) + { + LogMsg("ERROR: handle_regservice_request - transfer state != t_complete"); + abort_request(request); + unlink_request(request); + return; + } + + // extract data from message + ptr = request->msgdata; + flags = get_flags(&ptr); + ifi = get_long(&ptr); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi); + if (ifi && !InterfaceID) goto bad_param; + if (get_string(&ptr, name, 256) < 0 || + get_string(&ptr, regtype, 256) < 0 || + get_string(&ptr, domain, 256) < 0 || + get_string(&ptr, host, 256) < 0) + goto bad_param; + + port.NotAnInteger = get_short(&ptr); + txtlen = get_short(&ptr); + txtdata = get_rdata(&ptr, txtlen); + + // count subtypes, replacing commas w/ whitespace + rtype_ptr = regtype; + num_subtypes = -1; + while((sub = strsep(&rtype_ptr, ","))) + if (*sub) num_subtypes++; + + if (!name[0]) n = (&mDNSStorage)->nicelabel; + else if (!MakeDomainLabelFromLiteralString(&n, name)) + goto bad_param; + if ((!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) || + (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) || + (!ConstructServiceName(&srv, &n, &t, &d))) + goto bad_param; + if (host[0] && !MakeDomainNameFromDNSNameString(&h, host)) goto bad_param; + + r_srv = mallocL("handle_regservice_request", sizeof(registered_service)); + if (!r_srv) goto malloc_error; + srs_size = sizeof(ServiceRecordSet) + (sizeof(RDataBody) > txtlen ? 0 : txtlen - sizeof(RDataBody)); + r_srv->srs = mallocL("handle_regservice_request", srs_size); + if (!r_srv->srs) goto malloc_error; + if (num_subtypes > 0) + { + r_srv->subtypes = mallocL("handle_regservice_request", num_subtypes * sizeof(AuthRecord)); + if (!r_srv->subtypes) goto malloc_error; + sub = regtype + strlen(regtype) + 1; + for (i = 0; i < num_subtypes; i++) + { + if (!MakeDomainNameFromDNSNameString(&(r_srv->subtypes + i)->resrec.name, sub)) + { + freeL("handle_regservice_request", r_srv->subtypes); + freeL("handle_regservice_request", r_srv); + r_srv = NULL; + goto bad_param; + } + sub += strlen(sub) + 1; + } + } + else r_srv->subtypes = NULL; + r_srv->request = request; + + r_srv->autoname = (!name[0]); + r_srv->rename_on_memfree = 0; + r_srv->renameonconflict = !(flags & kDNSServiceFlagsNoAutoRename); + r_srv->name = n; + request->termination_context = r_srv; + request->terminate = regservice_termination_callback; + request->service = r_srv; + + result = mDNS_RegisterService(&mDNSStorage, r_srv->srs, &n, &t, &d, host[0] ? &h : NULL, port, + txtdata, txtlen, r_srv->subtypes, num_subtypes, InterfaceID, regservice_callback, r_srv); + deliver_error(request, result); + if (result != mStatus_NoError) + { + abort_request(request); + unlink_request(request); + } + else + { + reset_connected_rstate(request); // reset to receive add/remove messages + } + return; + +bad_param: + deliver_error(request, mStatus_BadParamErr); + abort_request(request); + unlink_request(request); +return; + +malloc_error: + my_perror("ERROR: malloc"); + exit(1); + } + +// service registration callback performs three duties - frees memory for deregistered services, +// handles name conflicts, and delivers completed registration information to the client (via +// process_service_registraion()) + +static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) + { + mStatus err; + ExtraResourceRecord *extra; + registered_service *r_srv = srs->ServiceContext; + request_state *rs = r_srv->request; + + #pragma unused(m) + + if (!rs && (result != mStatus_MemFree && !r_srv->rename_on_memfree)) + { + // error should never happen - safest to log and continue + LogMsg("ERROR: regservice_callback: received result %d with a NULL request pointer\n"); + return; + } + + if (result == mStatus_NoError) + return process_service_registration(srs); + else if (result == mStatus_MemFree) + { + if (r_srv->rename_on_memfree) + { + r_srv->rename_on_memfree = 0; + r_srv->name = mDNSStorage.nicelabel; + err = mDNS_RenameAndReregisterService(&mDNSStorage, srs, &r_srv->name); + if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err); + // error should never happen - safest to log and continue + } + else + { + while (r_srv->srs->Extras) + { + extra = r_srv->srs->Extras; + r_srv->srs->Extras = r_srv->srs->Extras->next; + freeL("regservice_callback", extra); + } + freeL("regservice_callback", r_srv->srs); + if (r_srv->subtypes) freeL("regservice_callback", r_srv->subtypes); + if (r_srv->request) r_srv->request->service = NULL; + freeL("regservice_callback", r_srv); + return; + } + } + else if (result == mStatus_NameConflict) + { + if (r_srv->autoname || r_srv->renameonconflict) + { + mDNS_RenameAndReregisterService(&mDNSStorage, srs, mDNSNULL); + return; + } + else + { + freeL("regservice_callback", r_srv); + freeL("regservice_callback", r_srv->srs); + if (r_srv->subtypes) freeL("regservice_callback", r_srv->subtypes); + if (r_srv->request) r_srv->request->service = NULL; + freeL("regservice_callback", r_srv); + if (deliver_async_error(rs, reg_service_reply, result) < 0) + { + abort_request(rs); + unlink_request(rs); + } + return; + } + } + else + { + LogMsg("ERROR: unknown result in regservice_callback"); + if (deliver_async_error(rs, reg_service_reply, result) < 0) + { + abort_request(rs); + unlink_request(rs); + } + return; + } + } + +static void handle_add_request(request_state *rstate) + { + registered_record_entry *re; + ExtraResourceRecord *extra; + uint32_t size, ttl; + uint16_t rrtype, rdlen; + char *ptr, *rdata; + mStatus result; + DNSServiceFlags flags; + ServiceRecordSet *srs = rstate->service->srs; + + if (!srs) + { + LogMsg("ERROR: handle_add_request - no service record set in request state"); + deliver_error(rstate, mStatus_UnknownErr); + return; + } + + ptr = rstate->msgdata; + flags = get_flags(&ptr); + rrtype = get_short(&ptr); + rdlen = get_short(&ptr); + rdata = get_rdata(&ptr, rdlen); + ttl = get_long(&ptr); + + if (rdlen > sizeof(RDataBody)) size = rdlen; + else size = sizeof(RDataBody); + + extra = mallocL("hanle_add_request", sizeof(ExtraResourceRecord) - sizeof(RDataBody) + size); + if (!extra) + { + my_perror("ERROR: malloc"); + exit(1); + } + + bzero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd + extra->r.resrec.rrtype = rrtype; + extra->r.rdatastorage.MaxRDLength = size; + extra->r.resrec.rdlength = rdlen; + memcpy(&extra->r.rdatastorage.u.data, rdata, rdlen); + result = mDNS_AddRecordToService(&mDNSStorage, srs , extra, &extra->r.rdatastorage, ttl); + deliver_error(rstate, result); + reset_connected_rstate(rstate); + if (result) + { + freeL("handle_add_request", rstate->msgbuf); + rstate->msgbuf = NULL; + freeL("handle_add_request", extra); + return; + } + re = mallocL("handle_add_request", sizeof(registered_record_entry)); + if (!re) + { + my_perror("ERROR: malloc"); + exit(1); + } + re->key = rstate->hdr.reg_index; + re->rr = &extra->r; + re->next = rstate->reg_recs; + rstate->reg_recs = re; + } + +static void handle_update_request(request_state *rstate) + { + registered_record_entry *reptr; + AuthRecord *rr; + RData *newrd; + uint16_t rdlen, rdsize; + char *ptr, *rdata; + uint32_t ttl; + mStatus result; + + if (rstate->hdr.reg_index == TXT_RECORD_INDEX) + { + if (!rstate->service) + { + deliver_error(rstate, mStatus_BadParamErr); + return; + } + rr = &rstate->service->srs->RR_TXT; + } + else + { + reptr = rstate->reg_recs; + while(reptr && reptr->key != rstate->hdr.reg_index) reptr = reptr->next; + if (!reptr) deliver_error(rstate, mStatus_BadReferenceErr); + rr = reptr->rr; + } + + ptr = rstate->msgdata; + get_flags(&ptr); // flags unused + rdlen = get_short(&ptr); + rdata = get_rdata(&ptr, rdlen); + ttl = get_long(&ptr); + + if (rdlen > sizeof(RDataBody)) rdsize = rdlen; + else rdsize = sizeof(RDataBody); + newrd = mallocL("handle_update_request", sizeof(RData) - sizeof(RDataBody) + rdsize); + if (!newrd) + { + my_perror("ERROR: malloc"); + exit(1); + } + newrd->MaxRDLength = rdsize; + memcpy(&newrd->u, rdata, rdlen); + result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback); + deliver_error(rstate, result); + reset_connected_rstate(rstate); + } + +static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd) + { + #pragma unused(m) + + if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd); + } + +static void process_service_registration(ServiceRecordSet *const srs) + { + reply_state *rep; + transfer_state send_result; + mStatus err; + registered_service *r_srv = srs->ServiceContext; + request_state *req = r_srv->request; + + + err = gen_rr_response(&srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, req, &rep); + if (err) + { + if (deliver_async_error(req, reg_service_reply, err) < 0) + { + abort_request(req); + unlink_request(req); + } + return; + } + send_result = send_msg(rep); + if (send_result == t_error || send_result == t_terminated) + { + abort_request(req); + unlink_request(req); + freeL("process_service_registration", rep); + } + else if (send_result == t_complete) freeL("process_service_registration", rep); + else append_reply(req, rep); + } + +static void regservice_termination_callback(void *context) + { + registered_service *srv = context; + + // only safe to free memory if registration is not valid, ie deregister fails + if (mDNS_DeregisterService(&mDNSStorage, srv->srs) != mStatus_NoError) + { + freeL("regservice_callback", srv->srs); + if (srv->subtypes) freeL("regservice_callback", srv->subtypes); + freeL("regservice_callback", srv); + freeL("regservice_termination_callback", srv); + } + } + + +static void handle_regrecord_request(request_state *rstate) + { + AuthRecord *rr; + regrecord_callback_context *rcc; + registered_record_entry *re; + mStatus result; + + if (rstate->ts != t_complete) + { + LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete"); + abort_request(rstate); + unlink_request(rstate); + return; + } + + rr = read_rr_from_ipc_msg(rstate->msgdata, 1); + if (!rr) + { + deliver_error(rstate, mStatus_BadParamErr); + return; + } + + rcc = mallocL("hanlde_regrecord_request", sizeof(regrecord_callback_context)); + if (!rcc) goto malloc_error; + rcc->rstate = rstate; + rcc->client_context = rstate->hdr.client_context; + rr->RecordContext = rcc; + rr->RecordCallback = regrecord_callback; + + // allocate registration entry, link into list + re = mallocL("hanlde_regrecord_request", sizeof(registered_record_entry)); + if (!re) goto malloc_error; + re->key = rstate->hdr.reg_index; + re->rr = rr; + re->next = rstate->reg_recs; + rstate->reg_recs = re; + + if (!rstate->terminate) + { + rstate->terminate = connected_registration_termination; + rstate->termination_context = rstate; + } + + result = mDNS_Register(&mDNSStorage, rr); + deliver_error(rstate, result); + reset_connected_rstate(rstate); + return; + +malloc_error: + my_perror("ERROR: malloc"); + return; + } + +static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + regrecord_callback_context *rcc; + int len; + reply_state *reply; + transfer_state ts; + + #pragma unused(m) + + if (result == mStatus_MemFree) { freeL("regrecord_callback", rr); return; } + rcc = rr->RecordContext; + + // format result, add to the list for the request, including the client context in the header + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); //interfaceIndex + len += sizeof(DNSServiceErrorType); + + reply = create_reply(reg_record_reply, len, rcc->rstate); + reply->mhdr->client_context = rcc->client_context; + reply->rhdr->flags = 0; + reply->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID); + reply->rhdr->error = result; + + ts = send_msg(reply); + if (ts == t_error || ts == t_terminated) + { + abort_request(rcc->rstate); + unlink_request(rcc->rstate); + } + else if (ts == t_complete) freeL("regrecord_callback", reply); + else if (ts == t_morecoming) append_reply(rcc->rstate, reply); // client is blocked, link reply into list + } + +static void connected_registration_termination(void *context) + { + registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs; + while(ptr) + { + mDNS_Deregister(&mDNSStorage, ptr->rr); + fptr = ptr; + ptr = ptr->next; + freeL("connected_registration_termination", fptr); + } + } + + + +static void handle_removerecord_request(request_state *rstate) + { + registered_record_entry *reptr, *prev = NULL; + mStatus err = mStatus_UnknownErr; + char *ptr; + reptr = rstate->reg_recs; + + ptr = rstate->msgdata; + get_flags(&ptr); // flags unused + + while(reptr) + { + if (reptr->key == rstate->hdr.reg_index) // found match + { + if (prev) prev->next = reptr->next; + else rstate->reg_recs = reptr->next; + err = mDNS_Deregister(&mDNSStorage, reptr->rr); + freeL("handle_removerecord_request", reptr); //rr gets freed by callback + break; + } + prev = reptr; + reptr = reptr->next; + } + reset_connected_rstate(rstate); + if (deliver_error(rstate, err) < 0) + { + abort_request(rstate); + unlink_request(rstate); + } + } + + +// domain enumeration +static void handle_enum_request(request_state *rstate) + { + DNSServiceFlags flags, add_default; + uint32_t ifi; + char *ptr = rstate->msgdata; + domain_enum_t *def, *all; + enum_termination_t *term; + reply_state *reply; // initial default reply + transfer_state tr; + mStatus err; + int result; + + if (rstate->ts != t_complete) + { + LogMsg("ERROR: handle_enum_request - transfer state != t_complete"); + abort_request(rstate); + unlink_request(rstate); + return; + } + + flags = get_flags(&ptr); + ifi = get_long(&ptr); + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi); + if (ifi && !InterfaceID) + { + deliver_error(rstate, mStatus_BadParamErr); + abort_request(rstate); + unlink_request(rstate); + } + + // allocate context structures + def = mallocL("hanlde_enum_request", sizeof(domain_enum_t)); + all = mallocL("handle_enum_request", sizeof(domain_enum_t)); + term = mallocL("handle_enum_request", sizeof(enum_termination_t)); + if (!def || !all || !term) + { + my_perror("ERROR: malloc"); + exit(1); + } + + // enumeration requires multiple questions, so we must link all the context pointers so that + // necessary context can be reached from the callbacks + def->rstate = rstate; + all->rstate = rstate; + term->def = def; + term->all = all; + term->rstate = rstate; + rstate->termination_context = term; + rstate->terminate = enum_termination_callback; + def->question.QuestionContext = def; + def->type = (flags & kDNSServiceFlagsRegistrationDomains) ? + mDNS_DomainTypeRegistrationDefault: mDNS_DomainTypeBrowseDefault; + all->question.QuestionContext = all; + all->type = (flags & kDNSServiceFlagsRegistrationDomains) ? + mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; + + // make the calls + err = mDNS_GetDomains(&mDNSStorage, &all->question, all->type, InterfaceID, enum_result_callback, all); + if (err == mStatus_NoError) + err = mDNS_GetDomains(&mDNSStorage, &def->question, def->type, InterfaceID, enum_result_callback, def); + result = deliver_error(rstate, err); // send error *before* returning local domain + + if (result < 0 || err) + { + abort_request(rstate); + unlink_request(rstate); + return; + } + + // provide local. as the first domain automatically + add_default = kDNSServiceFlagsDefault | kDNSServiceFlagsAdd | kDNSServiceFlagsFinished; + reply = format_enumeration_reply(rstate, "local.", add_default, ifi, 0); + tr = send_msg(reply); + if (tr == t_error || tr == t_terminated) + { + freeL("handle_enum_request", def); + freeL("handle_enum_request", all); + abort_request(rstate); + unlink_request(rstate); + return; + } + if (tr == t_complete) freeL("handle_enum_request", reply); + if (tr == t_morecoming) append_reply(rstate, reply); // couldn't send whole reply because client is blocked - link into list + } + +static void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + char domain[256]; + domain_enum_t *de = question->QuestionContext; + DNSServiceFlags flags = 0; + reply_state *reply; + + #pragma unused(m) + if (answer->rrtype != kDNSType_PTR) return; + if (AddRecord) + { + flags |= kDNSServiceFlagsAdd; + if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault) + flags |= kDNSServiceFlagsDefault; + } + else + { + flags |= kDNSServiceFlagsRemove; + } + ConvertDomainNameToCString(&answer->rdata->u.name, domain); + reply = format_enumeration_reply(de->rstate, domain, flags, mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID), kDNSServiceErr_NoError); + if (!reply) + { + LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); + return; + } + reply->next = NULL; + append_reply(de->rstate, reply); + return; + } + +static reply_state *format_enumeration_reply(request_state *rstate, char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err) + { + int len; + reply_state *reply; + char *data; + + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + len += sizeof(DNSServiceErrorType); + len += strlen(domain) + 1; + + reply = create_reply(enumeration_reply, len, rstate); + reply->rhdr->flags = flags; + reply->rhdr->ifi = ifi; + reply->rhdr->error = err; + data = reply->sdata; + put_string(domain, &data); + return reply; + } + +static void enum_termination_callback(void *context) + { + enum_termination_t *t = context; + mDNS *coredata = &mDNSStorage; + + mDNS_StopGetDomains(coredata, &t->all->question); + mDNS_StopGetDomains(coredata, &t->def->question); + freeL("enum_termination_callback", t->all); + freeL("enum_termination_callback", t->def); + t->rstate->termination_context = NULL; + freeL("enum_termination_callback", t); + } + +static void handle_reconfirm_request(request_state *rstate) + { + AuthRecord *rr; + + rr = read_rr_from_ipc_msg(rstate->msgdata, 0); + if (!rr) return; + mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec); + abort_request(rstate); + unlink_request(rstate); + freeL("handle_reconfirm_request", rr); + } + + +// setup rstate to accept new reg/dereg requests +static void reset_connected_rstate(request_state *rstate) + { + rstate->ts = t_morecoming; + rstate->hdr_bytes = 0; + rstate->data_bytes = 0; + if (rstate->msgbuf) freeL("reset_connected_rstate", rstate->msgbuf); + rstate->msgbuf = NULL; + rstate->bufsize = 0; + } + + + +// returns a resource record (allocated w/ malloc) containing the data found in an IPC message +// data must be in format flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional)ttl +// (ttl only extracted/set if ttl argument is non-zero). returns NULL for a bad-parameter error +static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl) + { + char *rdata, name[256]; + AuthRecord *rr; + DNSServiceFlags flags; + uint32_t interfaceIndex; + uint16_t type, class, rdlen; + int storage_size; + + flags = get_flags(&msgbuf); + interfaceIndex = get_long(&msgbuf); + if (get_string(&msgbuf, name, 256) < 0) + { + LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); + return NULL; + } + type = get_short(&msgbuf); + class = get_short(&msgbuf); + rdlen = get_short(&msgbuf); + + if (rdlen > sizeof(RDataBody)) storage_size = rdlen; + else storage_size = sizeof(RDataBody); + + rr = mallocL("read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size); + if (!rr) + { + my_perror("ERROR: malloc"); + exit(1); + } + bzero(rr, sizeof(AuthRecord)); // ok if oversized rdata not zero'd + rr->resrec.rdata = &rr->rdatastorage; + rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + if (!MakeDomainNameFromDNSNameString(&rr->resrec.name, name)) + { + LogMsg("ERROR: bad name: %s", name); + freeL("read_rr_from_ipc_msg", rr); + return NULL; + } + rr->resrec.rrtype = type; + if ((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) + rr->resrec.RecordType = kDNSRecordTypeShared; + if ((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique) + rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->resrec.rrclass = class; + rr->resrec.rdlength = rdlen; + rr->resrec.rdata->MaxRDLength = rdlen; + rdata = get_rdata(&msgbuf, rdlen); + memcpy(rr->resrec.rdata->u.data, rdata, rdlen); + if (ttl) + { + rr->resrec.rroriginalttl = get_long(&msgbuf); + } + return rr; + } + + +// generate a response message for a browse result, service registration result, or any other call with the +// identical callback signature. on successful completion rep is set to point to a malloc'd reply_state struct, +// and mStatus_NoError is returned. otherwise the appropriate error is returned. + +static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep) + { + char *data; + int len; + domainlabel name; + domainname type, dom; + char namestr[256], typestr[256], domstr[256]; + + *rep = NULL; + + if (!DeconstructServiceName(servicename, &name, &type, &dom)) + return kDNSServiceErr_Unknown; + + ConvertDomainLabelToCString_unescaped(&name, namestr); + ConvertDomainNameToCString(&type, typestr); + ConvertDomainNameToCString(&dom, domstr); + + // calculate reply data length + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); // if index + len += sizeof(DNSServiceErrorType); + len += strlen(namestr) + 1; + len += strlen(typestr) + 1; + len += strlen(domstr) + 1; + + *rep = create_reply(query_reply, len, request); + (*rep)->rhdr->flags = 0; + (*rep)->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id); + (*rep)->rhdr->error = kDNSServiceErr_NoError; + data = (*rep)->sdata; + + put_string(namestr, &data); + put_string(typestr, &data); + put_string(domstr, &data); + return mStatus_NoError; + } + + +static int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain) + { + domainlabel n; + domainname d, t; + + if (!MakeDomainLabelFromLiteralString(&n, name)) return -1; + if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1; + if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1; + if (!ConstructServiceName(srv, &n, &t, &d)) return -1; + return 0; + } + + +// append a reply to the list in a request object +static void append_reply(request_state *req, reply_state *rep) + { + reply_state *ptr; + + if (!req->replies) req->replies = rep; + else + { + ptr = req->replies; + while (ptr->next) ptr = ptr->next; + ptr->next = rep; + } + rep->next = NULL; + } + + +// read_msg may be called any time when the transfer state (rs->ts) is t_morecoming. +// returns the current state of the request (morecoming, error, complete, terminated.) +// if there is no data on the socket, the socket will be closed and t_terminated will be returned +static int read_msg(request_state *rs) + { + uint32_t nleft; + int nread; + char buf[4]; // dummy for death notification + + if (rs->ts == t_terminated || rs->ts == t_error) + { + LogMsg("ERROR: read_msg called with transfer state terminated or error"); + rs->ts = t_error; + return t_error; + } + + if (rs->ts == t_complete) + { // this must be death or something is wrong + nread = recv(rs->sd, buf, 4, 0); + if (!nread) { rs->ts = t_terminated; return t_terminated; } + if (nread < 0) goto rerror; + LogMsg("ERROR: read data from a completed request."); + rs->ts = t_error; + return t_error; + } + + if (rs->ts != t_morecoming) + { + LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs->ts); + rs->ts = t_error; + return t_error; + } + + if (rs->hdr_bytes < sizeof(ipc_msg_hdr)) + { + nleft = sizeof(ipc_msg_hdr) - rs->hdr_bytes; + nread = recv(rs->sd, (char *)&rs->hdr + rs->hdr_bytes, nleft, 0); + if (nread == 0) { rs->ts = t_terminated; return t_terminated; } + if (nread < 0) goto rerror; + rs->hdr_bytes += nread; + if (rs->hdr_bytes > sizeof(ipc_msg_hdr)) + { + LogMsg("ERROR: read_msg - read too many header bytes"); + rs->ts = t_error; + return t_error; + } + } + + // only read data if header is complete + if (rs->hdr_bytes == sizeof(ipc_msg_hdr)) + { + if (rs->hdr.datalen == 0) // ok in removerecord requests + { + rs->ts = t_complete; + rs->msgbuf = NULL; + return t_complete; + } + + if (!rs->msgbuf) // allocate the buffer first time through + { + rs->msgbuf = mallocL("read_msg", rs->hdr.datalen); + if (!rs->msgbuf) + { + my_perror("ERROR: malloc"); + rs->ts = t_error; + return t_error; + } + rs->msgdata = rs->msgbuf; + } + nleft = rs->hdr.datalen - rs->data_bytes; + nread = recv(rs->sd, rs->msgbuf + rs->data_bytes, nleft, 0); + if (nread == 0) { rs->ts = t_terminated; return t_terminated; } + if (nread < 0) goto rerror; + rs->data_bytes += nread; + if (rs->data_bytes > rs->hdr.datalen) + { + LogMsg("ERROR: read_msg - read too many data bytes"); + rs->ts = t_error; + return t_error; + } + } + + if (rs->hdr_bytes == sizeof(ipc_msg_hdr) && rs->data_bytes == rs->hdr.datalen) + rs->ts = t_complete; + else rs->ts = t_morecoming; + + return rs->ts; + +rerror: + if (errno == EAGAIN || errno == EINTR) return rs->ts; + my_perror("ERROR: read_msg"); + rs->ts = t_error; + return t_error; + } + + +static int send_msg(reply_state *rs) + { + ssize_t nwriten; + + if (!rs->msgbuf) + { + LogMsg("ERROR: send_msg called with NULL message buffer"); + return t_error; + } + + if (rs->request->no_reply) //!!!KRS this behavior should be optimized if it becomes more common + { + rs->ts = t_complete; + freeL("send_msg", rs->msgbuf); + return t_complete; + } + + nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0); + if (nwriten < 0) + { + if (errno == EINTR || errno == EAGAIN) nwriten = 0; + else + { + if (errno == EPIPE) + { + LogMsg("broken pipe - cleanup should be handled by run-loop read wakeup"); + rs->ts = t_terminated; + rs->request->ts = t_terminated; + return t_terminated; + } + else + { + my_perror("ERROR: send\n"); + rs->ts = t_error; + return t_error; + } + } + } + rs->nwriten += nwriten; + + if (rs->nwriten == rs->len) + { + rs->ts = t_complete; + freeL("send_msg", rs->msgbuf); + } + return rs->ts; + } + + + +static reply_state *create_reply(reply_op_t op, int datalen, request_state *request) +{ + reply_state *reply; + int totallen; + + + if ((unsigned)datalen < sizeof(reply_hdr)) + { + LogMsg("ERROR: create_reply - data length less than lenght of required fields"); + return NULL; + } + + totallen = datalen + sizeof(ipc_msg_hdr); + reply = mallocL("create_reply", sizeof(reply_state)); + if (!reply) + { + my_perror("ERROR: malloc"); + exit(1); + } + bzero(reply, sizeof(reply_state)); + reply->ts = t_morecoming; + reply->sd = request->sd; + reply->request = request; + reply->len = totallen; + reply->msgbuf = mallocL("create_reply", totallen); + if (!reply->msgbuf) + { + my_perror("ERROR: malloc"); + exit(1); + } + bzero(reply->msgbuf, totallen); + reply->mhdr = (ipc_msg_hdr *)reply->msgbuf; + reply->rhdr = (reply_hdr *)(reply->msgbuf + sizeof(ipc_msg_hdr)); + reply->sdata = reply->msgbuf + sizeof(ipc_msg_hdr) + sizeof(reply_hdr); + reply->mhdr->version = VERSION; + reply->mhdr->op.reply_op = op; + reply->mhdr->datalen = totallen - sizeof(ipc_msg_hdr); + return reply; + } + + +static int deliver_error(request_state *rstate, mStatus err) + { + int nwritten = -1; + undelivered_error_t *undeliv; + + nwritten = send(rstate->errfd, &err, sizeof(mStatus), 0); + if (nwritten < (int)sizeof(mStatus)) + { + if (errno == EINTR || errno == EAGAIN) + nwritten = 0; + if (nwritten < 0) + { + my_perror("ERROR: send - unable to deliver error to client"); + goto error; + } + //client blocked - store result and come backr + undeliv = mallocL("deliver_error", sizeof(undelivered_error_t)); + if (!undeliv) + { + my_perror("ERROR: malloc"); + exit(1); + } + undeliv->err = err; + undeliv->nwritten = nwritten; + undeliv->sd = rstate->errfd; + rstate->u_err = undeliv; + return 0; + } + if (rstate->errfd != rstate->sd) close(rstate->errfd); + return 0; + +error: + if (rstate->errfd != rstate->sd) close(rstate->errfd); + return -1; + + } + + +// returns 0 on success, -1 if send is incomplete, or on terminal failre (request is aborted) +static transfer_state send_undelivered_error(request_state *rs) + { + int nwritten; + + nwritten = send(rs->u_err->sd, (char *)(&rs->u_err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0); + if (nwritten < 0) + { + if (errno == EINTR || errno == EAGAIN) + nwritten = 0; + else + { + my_perror("ERROR: send - unable to deliver error to client\n"); + if (rs->u_err->sd == rs->sd) close (rs->u_err->sd); + return t_error; + } + } + if (nwritten + rs->u_err->nwritten == sizeof(mStatus)) + { + if (rs->u_err->sd == rs->sd) close(rs->u_err->sd); + freeL("send_undelivered_error", rs->u_err); + rs->u_err = NULL; + return t_complete; + } + rs->u_err->nwritten += nwritten; + return t_morecoming; + } + + +// send bogus data along with an error code to the app callback +// returns 0 on success (linking reply into list of not fully delivered), +// -1 on failure (request should be aborted) +static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err) + { + int len; + reply_state *reply; + transfer_state ts; + + if (rs->no_reply) return 0; + len = 256; // long enough for any reply handler to read all args w/o buffer overrun + reply = create_reply(op, len, rs); + reply->rhdr->error = err; + ts = send_msg(reply); + if (ts == t_error || ts == t_terminated) + { + freeL("deliver_async_error", reply); + return -1; + } + else if (ts == t_complete) freeL("deliver_async_error", reply); + else if (ts == t_morecoming) append_reply(rs, reply); // client is blocked, link reply into list + return 0; + } + + +static void abort_request(request_state *rs) + { + reply_state *rep, *ptr; + + if (rs->terminate) rs->terminate(rs->termination_context); // terminate field may not be set yet + if (rs->msgbuf) freeL("abort_request", rs->msgbuf); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rs->rls, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(rs->rls); + CFRelease(rs->rls); + CFSocketInvalidate(rs->sr); + CFRelease(rs->sr); + rs->sd = -1; + if (rs->errfd >= 0) close(rs->errfd); + rs->errfd = -1; + + // free pending replies + rep = rs->replies; + while(rep) + { + if (rep->msgbuf) freeL("abort_request", rep->msgbuf); + ptr = rep; + rep = rep->next; + freeL("abort_request", ptr); + } + + if (rs->u_err) + { + freeL("abort_request", rs->u_err); + rs->u_err = NULL; + } + } + + +static void unlink_request(request_state *rs) + { + request_state *ptr; + + if (rs == all_requests) + { + all_requests = all_requests->next; + freeL("unlink_request", rs); + return; + } + for(ptr = all_requests; ptr->next; ptr = ptr->next) + if (ptr->next == rs) + { + ptr->next = rs->next; + freeL("unlink_request", rs); + return; + } + } + + + +//hack to search-replace perror's to LogMsg's +static void my_perror(char *errmsg) + { + LogMsg("%s: %s", errmsg, strerror(errno)); + } + + diff --git a/mDNSPosix/Client.c b/mDNSPosix/Client.c new file mode 100755 index 0000000..f3f9801 --- /dev/null +++ b/mDNSPosix/Client.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Client.c,v $ +Revision 1.9 2003/08/14 02:19:55 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.8 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.7 2003/07/02 21:19:58 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.6 2003/06/18 05:48:41 cheshire +Fix warnings + +Revision 1.5 2003/05/06 00:00:50 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.4 2002/12/23 22:13:31 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 04:20:44 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.1 2002/09/17 06:24:35 cheshire +First checkin + +*/ + +#include +#include +#include +#include +#include +#include + +#include "mDNSClientAPI.h"// Defines the interface to the mDNS core code +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include "ExampleClientApp.h" + +// Globals +static mDNS mDNSStorage; // mDNS core uses this to store its globals +static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals +#define RR_CACHE_SIZE 500 +static CacheRecord gRRCache[RR_CACHE_SIZE]; + +static const char *gProgramName = "mDNSResponderPosix"; + +static void BrowseCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + // A callback from the core mDNS code that indicates that we've received a + // response to our query. Note that this code runs on the main thread + // (in fact, there is only one thread!), so we can safely printf the results. +{ + domainlabel name; + domainname type; + domainname domain; + char nameC[256]; + char typeC[256]; + char domainC[256]; + const char *state; + + (void)m; // Unused + (void)question; // Unused + + assert(answer->rrtype == kDNSType_PTR); + + DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain); + + ConvertDomainLabelToCString_unescaped(&name, nameC); + ConvertDomainNameToCString(&type, typeC); + ConvertDomainNameToCString(&domain, domainC); + + // If the TTL has hit 0, the service is no longer available. + if (!AddRecord) { + state = "Lost "; + } else { + state = "Found"; + } + fprintf(stderr, "*** %s name = '%s', type = '%s', domain = '%s'\n", state, nameC, typeC, domainC); +} + +static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation) + // Checks that serviceType is a reasonable service type + // label and, if it isn't and printExplanation is true, prints + // an explanation of why not. +{ + mDNSBool result; + + result = mDNStrue; + if (result && strlen(serviceType) > 63) { + if (printExplanation) { + fprintf(stderr, + "%s: Service type specified by -t is too long (must be 63 characters or less)\n", + gProgramName); + } + result = mDNSfalse; + } + if (result && serviceType[0] == 0) { + if (printExplanation) { + fprintf(stderr, + "%s: Service type specified by -t can't be empty\n", + gProgramName); + } + result = mDNSfalse; + } + return result; +} + +static const char kDefaultServiceType[] = "_afpovertcp._tcp."; + +static void PrintUsage() +{ + fprintf(stderr, + "Usage: %s [-v level] [-t type]\n", + gProgramName); + fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n"); + fprintf(stderr, " 0 = no debugging info (default)\n"); + fprintf(stderr, " 1 = standard debugging info\n"); + fprintf(stderr, " 2 = intense debugging info\n"); + fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType); +} + +static const char *gServiceType = kDefaultServiceType; + +static void ParseArguments(int argc, char **argv) + // Parses our command line arguments into the global variables + // listed above. +{ + int ch; + + // Set gProgramName to the last path component of argv[0] + + gProgramName = strrchr(argv[0], '/'); + if (gProgramName == NULL) { + gProgramName = argv[0]; + } else { + gProgramName += 1; + } + + // Parse command line options using getopt. + + do { + ch = getopt(argc, argv, "v:t:"); + if (ch != -1) { + switch (ch) { + case 'v': + gMDNSPlatformPosixVerboseLevel = atoi(optarg); + if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { + fprintf(stderr, + "%s: Verbose mode must be in the range 0..2\n", + gProgramName); + exit(1); + } + break; + case 't': + gServiceType = optarg; + if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { + exit(1); + } + break; + case '?': + default: + PrintUsage(); + exit(1); + break; + } + } + } while (ch != -1); + + // Check for any left over command line arguments. + + if (optind != argc) { + fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]); + exit(1); + } +} + +int main(int argc, char **argv) + // The program's main entry point. The program does a trivial + // mDNS query, looking for all AFP servers in the local domain. +{ + int result; + mStatus status; + DNSQuestion question; + domainname type; + domainname domain; + + // Parse our command line arguments. This won't come back if there's an error. + ParseArguments(argc, argv); + + // Initialise the mDNS core. + status = mDNS_Init(&mDNSStorage, &PlatformStorage, + gRRCache, RR_CACHE_SIZE, + mDNS_Init_DontAdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (status == mStatus_NoError) { + + // Construct and start the query. + + MakeDomainNameFromDNSNameString(&type, gServiceType); + MakeDomainNameFromDNSNameString(&domain, "local."); + + status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, BrowseCallback, NULL); + + // Run the platform main event loop until the user types ^C. + // The BrowseCallback routine is responsible for printing + // any results that we find. + + if (status == mStatus_NoError) { + fprintf(stderr, "Hit ^C when you're bored waiting for responses.\n"); + ExampleClientEventLoop(&mDNSStorage); + mDNS_StopQuery(&mDNSStorage, &question); + mDNS_Close(&mDNSStorage); + } + } + + if (status == mStatus_NoError) { + result = 0; + } else { + result = 2; + } + if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) { + fprintf(stderr, "%s: Finished with status %ld, result %d\n", gProgramName, status, result); + } + + return 0; +} diff --git a/mDNSPosix/ExampleClientApp.c b/mDNSPosix/ExampleClientApp.c new file mode 100644 index 0000000..a0531bc --- /dev/null +++ b/mDNSPosix/ExampleClientApp.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExampleClientApp.c,v $ +Revision 1.9 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.8 2003/07/02 21:19:58 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.7 2003/06/18 05:48:41 cheshire +Fix warnings + +Revision 1.6 2003/03/31 22:44:36 cheshire +Add log header + + */ + +#include // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For errno, EINTR +#include // For inet_addr() +#include // For INADDR_NONE +#include // For gethostbyname() +#include // For SIGINT, etc. + +#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform + +//******************************************************************************************* +// Main + +static volatile mDNSBool StopNow; + +mDNSlocal void HandleSIG(int signal) + { + (void)signal; // Unused + debugf(""); + debugf("HandleSIG"); + StopNow = mDNStrue; + } + +mDNSexport void ExampleClientEventLoop(mDNS *const m) + { + signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C + signal(SIGTERM, HandleSIG); + + while (!StopNow) + { + int nfds = 0; + fd_set readfds; + struct timeval timeout; + int result; + + // 1. Set up the fd_set as usual here. + // This example client has no file descriptors of its own, + // but a real application would call FD_SET to add them to the set here + FD_ZERO(&readfds); + + // 2. Set up the timeout. + // This example client has no other work it needs to be doing, + // so we set an effectively infinite timeout + timeout.tv_sec = 0x3FFFFFFF; + timeout.tv_usec = 0; + + // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout + mDNSPosixGetFDSet(m, &nfds, &readfds, &timeout); + + // 4. Call select as normal + verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); + result = select(nfds, &readfds, NULL, NULL, &timeout); + + if (result < 0) + { + verbosedebugf("select() returned %d errno %d", result, errno); + if (errno != EINTR) StopNow = mDNStrue; + } + else + { + // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work + mDNSPosixProcessFDSet(m, &readfds); + + // 6. This example client has no other work it needs to be doing, + // but a real client would do its work here + // ... (do work) ... + } + } + + debugf("Exiting"); + } diff --git a/mDNSPosix/ExampleClientApp.h b/mDNSPosix/ExampleClientApp.h new file mode 100644 index 0000000..4274a46 --- /dev/null +++ b/mDNSPosix/ExampleClientApp.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExampleClientApp.h,v $ +Revision 1.5 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.4 2003/07/02 21:19:58 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.3 2003/03/31 22:49:35 cheshire +Add "$Log" header + + */ + +extern void ExampleClientEventLoop(mDNS *const m); diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c new file mode 100644 index 0000000..228f0a6 --- /dev/null +++ b/mDNSPosix/Identify.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * Formatting notes: + * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion + * on C indentation can be found on the web, such as , + * but for the sake of brevity here I will say just this: Curly braces are not syntactially + * part of an "if" statement; they are the beginning and ending markers of a compound statement; + * therefore common sense dictates that if they are part of a compound statement then they + * should be indented to the same level as everything else in that compound statement. + * Indenting curly braces at the same level as the "if" implies that curly braces are + * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" + * thinking that variables x and y are both of type "char*" -- and anyone who doesn't + * understand why variable y is not of type "char*" just proves the point that poor code + * layout leads people to unfortunate misunderstandings about how the C language really works.) + + Change History (most recent first): + +$Log: Identify.c,v $ +Revision 1.10 2003/09/02 20:38:57 cheshire +#include for Linux + +Revision 1.9 2003/08/14 23:57:46 cheshire +Report if there is no answer at all from the target host + +Revision 1.8 2003/08/14 02:19:55 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.7 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.6 2003/08/06 01:46:18 cheshire +Distinguish no answer from partial answer + +Revision 1.5 2003/08/05 23:56:26 cheshire +Update code to compile with the new mDNSCoreReceive() function that requires a TTL +(Right now mDNSPosix.c just reports 255 -- we should fix this) + +Revision 1.4 2003/08/04 17:24:48 cheshire +Combine the three separate A/AAAA/HINFO queries into a single qtype "ANY" query + +Revision 1.3 2003/08/04 17:14:08 cheshire +Do both AAAA queries in parallel + +Revision 1.2 2003/08/02 02:25:13 cheshire +Multiple improvements: Now displays host's name, and all v4 and v6 addresses, as well as HINFO record + +Revision 1.1 2003/08/01 02:20:02 cheshire +Add mDNSIdentify tool, used to discover what version of mDNSResponder a particular host is running + + */ + +//************************************************************************************************************* +// Incorporate mDNS.c functionality + +// We want to use the functionality provided by "mDNS.c", +// except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine +#define mDNSCoreReceive __MDNS__mDNSCoreReceive +#include "mDNS.c" +#undef mDNSCoreReceive + +//************************************************************************************************************* +// Headers + +#include +#include +#include +#include +#include +#include +#include // For n_long, required by below +#include // For IPTOS_LOWDELAY etc. +#include +#include + +#include "mDNSClientAPI.h"// Defines the interface to the mDNS core code +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include "ExampleClientApp.h" + +//************************************************************************************************************* +// Globals + +static mDNS mDNSStorage; // mDNS core uses this to store its globals +static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals +#define RR_CACHE_SIZE 500 +static CacheRecord gRRCache[RR_CACHE_SIZE]; + +static volatile int StopNow; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C +static volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO; +static char hostname[256], hardware[256], software[256]; +static mDNSOpaque16 lastid, id; + +//************************************************************************************************************* +// Utilities + +// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc. +mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +mDNSlocal mDNSu32 mprintf(const char *format, ...) + { + mDNSu32 length; + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr); + va_end(ptr); + printf("%s", buffer); + return(length); + } + +//************************************************************************************************************* +// Main code + +mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + { + // Snag copy of header ID, then call through + lastid = msg->h.id; + __MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, ttl); + } + +static void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + (void)m; // Unused + (void)question; // Unused + (void)AddRecord;// Unused + if (!id.NotAnInteger) id = lastid; + ConvertDomainNameToCString(&answer->rdata->u.name, hostname); + StopNow = 1; + mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.name.c); + } + +static void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + (void)m; // Unused + (void)question; // Unused + (void)AddRecord;// Unused + if (answer->rrtype == kDNSType_A) + { + if (!id.NotAnInteger) id = lastid; + NumAnswers++; + NumAddr++; + mprintf("%##s %s %.4a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ip); + } + else if (answer->rrtype == kDNSType_AAAA) + { + if (!id.NotAnInteger) id = lastid; + NumAnswers++; + NumAAAA++; + mprintf("%##s %s %.16a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6); + } + else if (answer->rrtype == kDNSType_HINFO) + { + mDNSu8 *p = answer->rdata->u.data; + strncpy(hardware, p+1, p[0]); + hardware[p[0]] = 0; + p += 1 + p[0]; + strncpy(software, p+1, p[0]); + software[p[0]] = 0; + NumAnswers++; + NumHINFO++; + } + } + +mDNSexport void WaitForAnswer(mDNS *const m, int seconds) + { + struct timeval end; + gettimeofday(&end, NULL); + end.tv_sec += seconds; + StopNow = 0; + NumAnswers = 0; + while (!StopNow) + { + int nfds = 0; + fd_set readfds; + struct timeval now, remain = end; + int result; + + FD_ZERO(&readfds); + gettimeofday(&now, NULL); + if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; } + if (remain.tv_sec < now.tv_sec) return; + remain.tv_usec -= now.tv_usec; + remain.tv_sec -= now.tv_sec; + mDNSPosixGetFDSet(m, &nfds, &readfds, &remain); + result = select(nfds, &readfds, NULL, NULL, &remain); + if (result >= 0) mDNSPosixProcessFDSet(m, &readfds); + else if (errno != EINTR) StopNow = 2; + } + } + +mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, mDNSQuestionCallback callback) + { + if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname); + + q->InterfaceID = mDNSInterface_Any; + q->qtype = qtype; + q->qclass = kDNSClass_IN; + q->QuestionCallback = callback; + q->QuestionContext = NULL; + + //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype)); + return(mDNS_StartQuery(&mDNSStorage, q)); + } + +mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, mDNSQuestionCallback callback) + { + mStatus status = StartQuery(q, qname, qtype, callback); + if (status != mStatus_NoError) + StopNow = 2; + else + { + WaitForAnswer(&mDNSStorage, 4); + mDNS_StopQuery(&mDNSStorage, q); + if (StopNow == 0 && NumAnswers == 0) + printf("%s %s *** No Answer ***\n", qname, DNSTypeName(qtype)); + } + return(StopNow); + } + +mDNSlocal void HandleSIG(int signal) + { + (void)signal; // Unused + debugf(""); + debugf("HandleSIG"); + StopNow = 2; + } + +mDNSexport int main(int argc, char **argv) + { + mStatus status; + + if (argc < 2) goto usage; + + // Initialise the mDNS core. + status = mDNS_Init(&mDNSStorage, &PlatformStorage, + gRRCache, RR_CACHE_SIZE, + mDNS_Init_DontAdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %ld\n", status); return(status); } + + signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C + signal(SIGTERM, HandleSIG); + + struct in_addr s4; + struct in6_addr s6; + + char buffer[256]; + DNSQuestion q; + + if (inet_pton(AF_INET, argv[1], &s4) == 1) + { + mDNSu8 *p = (mDNSu8 *)&s4; + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]); + printf("%s\n", buffer); + if (DoQuery(&q, buffer, kDNSType_PTR, NameCallback) != 1) goto exit; + } + else if (inet_pton(AF_INET6, argv[1], &s6) == 1) + { + DNSQuestion q1, q2; + int i; + mDNSu8 *p = (mDNSu8 *)&s6; + for (i = 0; i < 16; i++) + { + static const char hexValues[] = "0123456789ABCDEF"; + buffer[i * 4 ] = hexValues[p[15-i] & 0x0F]; + buffer[i * 4 + 1] = '.'; + buffer[i * 4 + 2] = hexValues[p[15-i] >> 4]; + buffer[i * 4 + 3] = '.'; + } + mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); + MakeDomainNameFromDNSNameString(&q1.qname, buffer); + mDNS_snprintf(&buffer[32], sizeof(buffer)-32, "ip6.arpa."); // Workaround for WWDC bug + MakeDomainNameFromDNSNameString(&q2.qname, buffer); + StartQuery(&q1, NULL, kDNSType_PTR, NameCallback); + StartQuery(&q2, NULL, kDNSType_PTR, NameCallback); + WaitForAnswer(&mDNSStorage, 4); + mDNS_StopQuery(&mDNSStorage, &q1); + mDNS_StopQuery(&mDNSStorage, &q2); + if (StopNow != 1) { mprintf("%##s %s *** No Answer ***\n", q1.qname.c, DNSTypeName(q1.qtype)); goto exit; } + } + else + strcpy(hostname, argv[1]); + + // Now we have the host name; get its A, AAAA, and HINFO + if (DoQuery(&q, hostname, kDNSQType_ANY, InfoCallback) == 2) goto exit; // Interrupted with Ctrl-C + + if (hardware[0] || software[0]) + { + printf("HINFO Hardware: %s\n", hardware); + printf("HINFO Software: %s\n", software); + } + else if (NumAnswers) + { + printf("Host has no HINFO record; Best guess is "); + if (id.b[1]) printf("mDNSResponder-%d\n", id.b[1]); + else if (NumAAAA) printf("very early Panther build (mDNSResponder-33 or earlier)\n"); + else printf("Jaguar version of mDNSResponder with no IPv6 support\n"); + } + else + printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n"); + +exit: + mDNS_Close(&mDNSStorage); + return(0); + +usage: + fprintf(stderr, "%s or or \n", argv[0]); + return(-1); + } diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile new file mode 100755 index 0000000..1801a93 --- /dev/null +++ b/mDNSPosix/Makefile @@ -0,0 +1,120 @@ +# $Log: Makefile,v $ +# Revision 1.13 2003/08/06 18:20:51 cheshire +# Makefile cleanup +# +# Revision 1.12 2003/08/01 02:20:02 cheshire +# Add mDNSIdentify tool, used to discover what version of mDNSResponder a particular host is running +# +# Revision 1.11 2003/07/14 18:11:54 cheshire +# Fix stricter compiler warnings +# +# Revision 1.10 2003/06/18 05:47:41 cheshire +# Enable stricter warnings on Jaguar and Panther builds +# +# Revision 1.9 2003/06/04 18:34:45 ksekar +# Bug #: : mDNSPosix does not build on Panther that has socklen_t +# Changed build targets "osx10.2" and "osx10.3" to "jaguar" and "panther". +# +# Revision 1.8 2003/06/04 00:23:12 ksekar +# Bug #: : mDNSPosix does not build on Panther that has socklen_t +# Created separate target OS's for 10.2 and 10.3. +# +# Revision 1.7 2003/04/16 02:11:37 cheshire +# Remove unnecessary $(CFLAGS) from linking rules +# +# Revision 1.6 2003/04/04 01:37:14 cheshire +# Added NetMonitor.c +# + +# I assume that cc will be in your path. If not, you have to change the following to point to it. +CC = cc +CFLAGS_COMMON = -g -I../mDNSCore -I. -DMDNS_DEBUGMSGS=2 + +ifeq ($(os),solaris) +CFLAGS_OS = -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME -lsocket -lnsl +else +ifeq ($(os),linux) +CFLAGS_OS = -DNOT_HAVE_SA_LEN -W -Wall +else +ifeq ($(os),netbsd) +CFLAGS_OS = +else +ifeq ($(os),freebsd) +CFLAGS_OS = +else +ifeq ($(os),openbsd) +CFLAGS_OS = -DHAVE_BROKEN_RECVDSTADDR +else +ifeq ($(os),jaguar) +CFLAGS_OS = -DHAVE_IPV6 -W -Wall -no-cpp-precomp -DNOT_HAVE_SOCKLEN_T +else +ifeq ($(os),panther) +CFLAGS_OS = -DHAVE_IPV6 -W -Wall -no-cpp-precomp +else +cantbuild: + @echo "Error: Must specify target OS on command-line, e.g. \"make os=panther\" or \"make os=jaguar\" or \"make os=linux\"" +endif +endif +endif +endif +endif +endif +endif +CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) + +COMMONOBJ = objects/mDNSPosix.c.o objects/mDNSUNP.c.o objects/ExampleClientApp.c.o + +HEADERS = Makefile mDNSUNP.h mDNSPosix.h \ +../mDNSCore/mDNSDebug.h \ +../mDNSCore/mDNSClientAPI.h \ +../mDNSCore/mDNSPlatformFunctions.h + +all: setup Client Responder ProxyResponder Identify NetMonitor + +setup: + if test ! -d objects ; then mkdir objects ; fi + if test ! -d build ; then mkdir build ; fi + +Client: setup build/mDNSClientPosix + @echo "Client done" + +Responder: setup build/mDNSResponderPosix + @echo "Responder done" + +ProxyResponder: setup build/mDNSProxyResponderPosix + @echo "ProxyResponder done" + +Identify: setup build/mDNSIdentify + @echo "Identify done" + +NetMonitor: setup build/mDNSNetMonitor + @echo "NetMonitor done" + +# $@ means "The file name of the target of the rule" +# $< means "The name of the first prerequisite" +# $+ means "The names of all the prerequisites, with spaces between them, exactly as given" +# For more magic automatic sariables, see +# +build/mDNSClientPosix: $(COMMONOBJ) objects/mDNS.c.o objects/Client.c.o + $(CC) $+ -o $@ + +build/mDNSResponderPosix: $(COMMONOBJ) objects/mDNS.c.o objects/Responder.c.o + $(CC) $+ -o $@ + +build/mDNSProxyResponderPosix: $(COMMONOBJ) objects/mDNS.c.o objects/ProxyResponder.c.o + $(CC) $+ -o $@ + +build/mDNSIdentify: $(COMMONOBJ) objects/Identify.c.o + $(CC) $+ -o $@ + +build/mDNSNetMonitor: $(COMMONOBJ) objects/NetMonitor.c.o + $(CC) $+ -o $@ + +objects/%.c.o: %.c ../mDNSCore/mDNS.c $(HEADERS) + $(CC) -c $(CFLAGS) $< -o $@ + +objects/mDNS.c.o: ../mDNSCore/mDNS.c $(HEADERS) + $(CC) -c $(CFLAGS) $< -o $@ + +clean: + -rm -rf objects build .gdb_history diff --git a/mDNSPosix/NetMonitor.c b/mDNSPosix/NetMonitor.c new file mode 100644 index 0000000..b495d9a --- /dev/null +++ b/mDNSPosix/NetMonitor.c @@ -0,0 +1,928 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * Formatting notes: + * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion + * on C indentation can be found on the web, such as , + * but for the sake of brevity here I will say just this: Curly braces are not syntactially + * part of an "if" statement; they are the beginning and ending markers of a compound statement; + * therefore common sense dictates that if they are part of a compound statement then they + * should be indented to the same level as everything else in that compound statement. + * Indenting curly braces at the same level as the "if" implies that curly braces are + * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" + * thinking that variables x and y are both of type "char*" -- and anyone who doesn't + * understand why variable y is not of type "char*" just proves the point that poor code + * layout leads people to unfortunate misunderstandings about how the C language really works.) + + Change History (most recent first): + +$Log: NetMonitor.c,v $ +Revision 1.47 2003/09/05 18:49:57 cheshire +Add total packet size to display + +Revision 1.46 2003/09/05 02:33:48 cheshire +Set output to be line buffered, so you can redirect to a file and "tail -f" the file in another window + +Revision 1.45 2003/09/04 00:16:20 cheshire +Only show count of unique source addresses seen on network if we're not filtering + +Revision 1.44 2003/09/02 22:13:28 cheshire +Show total host count in final summary table + +Revision 1.43 2003/09/02 21:42:52 cheshire +Improved alignment of final summary table with v6 addresses + +Revision 1.42 2003/09/02 20:59:24 cheshire +Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields. + +Revision 1.41 2003/08/29 22:05:44 cheshire +Also count subsequent KA packets for the purposes of statistics counting + +Revision 1.40 2003/08/29 16:43:24 cheshire +Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host + +Revision 1.39 2003/08/28 02:07:48 vlubet +Added "per hosts" statistics + +Revision 1.38 2003/08/20 22:41:42 cheshire +Also display total multicast packet count + +Revision 1.37 2003/08/20 22:32:08 cheshire +Error in DisplayQuery: Authorities come *after* Answers, not before + +Revision 1.36 2003/08/18 23:20:10 cheshire +RDLength moved from the RData to the ResourceRecord object. + +Revision 1.35 2003/08/15 20:17:28 cheshire +"LargeResourceRecord" changed to "LargeCacheRecord" + +Revision 1.34 2003/08/14 02:19:55 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.33 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.32 2003/08/06 18:57:01 cheshire +Update comments + +Revision 1.31 2003/08/06 02:05:12 cheshire +Add ability to give a list of hosts to monitor + +Revision 1.30 2003/08/05 23:56:26 cheshire +Update code to compile with the new mDNSCoreReceive() function that requires a TTL +(Right now mDNSPosix.c just reports 255 -- we should fix this) + +Revision 1.29 2003/08/05 00:43:12 cheshire +Report errors encountered while processing authority section + +Revision 1.28 2003/07/29 22:51:08 cheshire +Added hexdump for packets we can't decode, so we can find out *why* we couldn't decode them + +Revision 1.27 2003/07/29 22:48:04 cheshire +Completed support for decoding packets containing oversized resource records + +Revision 1.26 2003/07/19 03:25:23 cheshire +Change to make use of new GetLargeResourceRecord() call, for handling larger records + +Revision 1.25 2003/07/18 00:13:23 cheshire +Remove erroneous trailing '\' from TXT record display + +Revision 1.24 2003/07/17 17:10:51 cheshire + Implement "unicast response" request, using top bit of qclass +Further work: distinguish between PM and PU + +Revision 1.23 2003/07/16 22:20:23 cheshire + Implement "unicast response" request, using top bit of qclass +Made mDNSNetMonitor distinguish between QM and QU in its logging output + +Revision 1.22 2003/07/02 21:19:58 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.21 2003/06/18 05:48:41 cheshire +Fix warnings + +Revision 1.20 2003/06/06 22:18:22 cheshire +Add extra space in Q output to line it up with RR output + +Revision 1.19 2003/06/06 21:05:04 cheshire +Display state of cache-flush bit on additional records + +Revision 1.18 2003/06/06 20:01:30 cheshire +For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass +(Global search-and-replace; no functional change to code execution.) + +Revision 1.17 2003/06/06 14:26:50 cheshire +Explicitly #include for the benefit of certain Linux distributions + +Revision 1.16 2003/05/29 21:56:36 cheshire +More improvements: +Distinguish modern multicast queries from legacy multicast queries +In addition to record counts, display packet counts of queries, legacy queries, and responses +Include TTL in RR display + +Revision 1.15 2003/05/29 20:03:57 cheshire +Various improvements: +Display start and end time, average rates in packets-per-minute, +show legacy queries as -LQ-, improve display of TXT and unknown records + +Revision 1.14 2003/05/26 04:45:42 cheshire +Limit line length when printing super-long TXT records + +Revision 1.13 2003/05/26 03:21:29 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.12 2003/05/26 03:01:28 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.11 2003/05/26 00:48:13 cheshire +If mDNS packet contains a non-zero message ID, then display it. + +Revision 1.10 2003/05/22 01:10:32 cheshire +Indicate when the TC bit is set on a query packet + +Revision 1.9 2003/05/21 03:56:00 cheshire +Improve display of Probe queries + +Revision 1.8 2003/05/09 21:41:56 cheshire +Track deletion/goodbye packets as separate category + +Revision 1.7 2003/05/07 00:16:01 cheshire +More detailed decoding of Resource Records + +Revision 1.6 2003/05/05 21:16:16 cheshire + Change timenow from a local variable to a structure member + +Revision 1.5 2003/04/19 01:16:22 cheshire +Add filter option, to monitor only packets from a single specified source address + +Revision 1.4 2003/04/18 00:45:21 cheshire +Distinguish announcements (AN) from deletions (DE) + +Revision 1.3 2003/04/15 18:26:01 cheshire +Added timestamps and help information + +Revision 1.2 2003/04/04 20:42:02 cheshire +Fix broken statistics counting + +Revision 1.1 2003/04/04 01:37:14 cheshire +Added NetMonitor.c + + */ + +//************************************************************************************************************* +// Incorporate mDNS.c functionality + +// We want to use much of the functionality provided by "mDNS.c", +// except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine +#define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__ +#include "mDNS.c" +#undef mDNSCoreReceive + +//************************************************************************************************************* +// Headers + +#include // For printf() +#include // For malloc() +#include // For bcopy() +#include // For "struct tm" etc. +#include // For gethostbyname() +#include // For AF_INET, AF_INET6, etc. +#include // For inet_addr() +#include // For INADDR_NONE + +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include "ExampleClientApp.h" + +//************************************************************************************************************* +// Types and structures + +enum + { + // Primitive operations + OP_probe = 0, + OP_goodbye = 1, + + // These are meta-categories; + // Query and Answer operations are actually subdivided into two classes: + // Browse query/answer and + // Resolve query/answer + OP_query = 2, + OP_answer = 3, + + // The "Browse" variants of query/answer + OP_browsegroup = 2, + OP_browseq = 2, + OP_browsea = 3, + + // The "Resolve" variants of query/answer + OP_resolvegroup = 4, + OP_resolveq = 4, + OP_resolvea = 5, + + OP_NumTypes = 6 + }; + +typedef struct ActivityStat_struct ActivityStat; +struct ActivityStat_struct + { + ActivityStat *next; + domainname srvtype; + int printed; + int totalops; + int stat[OP_NumTypes]; + }; + +typedef struct FilterList_struct FilterList; +struct FilterList_struct + { + FilterList *next; + mDNSAddr FilterAddr; + }; + +//************************************************************************************************************* +// Globals + +static mDNS mDNSStorage; // mDNS core uses this to store its globals +static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals + +struct timeval tv_start, tv_end, tv_interval; + +static FilterList *Filters; +#define ExactlyOneFilter (Filters && !Filters->next) + +static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad +static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals; + +static ActivityStat *stats; + +#define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA" + +//************************************************************************************************************* +// Utilities + +// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc. +mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +mDNSlocal mDNSu32 mprintf(const char *format, ...) + { + mDNSu32 length; + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr); + va_end(ptr); + printf("%s", buffer); + return(length); + } + +//************************************************************************************************************* +// Host Address List +// +// Would benefit from a hash + +typedef enum + { + HostPkt_Q = 0, // Query + HostPkt_L = 1, // Legacy Query + HostPkt_R = 2, // Response + HostPkt_B = 3, // Bad + HostPkt_NumTypes = 4, + } HostPkt_Type; + +typedef struct + { + mDNSAddr addr; + unsigned long pkts[HostPkt_NumTypes]; + unsigned long totalops; + unsigned long stat[OP_NumTypes]; + } HostEntry; + +#define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B]) + +typedef struct + { + long num; + long max; + HostEntry *hosts; + } HostList; + +static HostList IPv4HostList = { 0, 0, 0 }; +static HostList IPv6HostList = { 0, 0, 0 }; + +mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList* list) + { + long i; + + for (i = 0; i < list->num; i++) + { + HostEntry *entry = list->hosts + i; + if (mDNSSameAddress(addr, &entry->addr)) + return entry; + } + + return NULL; + } + +mDNSlocal HostEntry *AddHost(HostList* list) + { + HostEntry *entry; + if (list->num >= list->max) + { + long newMax = list->max + 64; + HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry)); + if (newHosts == NULL) + return NULL; + list->max = newMax; + list->hosts = newHosts; + } + entry = list->hosts + list->num++; + return(entry); + } + +mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t) + { + if (ExactlyOneFilter) return(NULL); + else + { + HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList; + HostEntry *entry = FindHost(addr, list); + if (!entry) + { + int i; + entry = AddHost(list); + if (!entry) return(NULL); + entry->addr = *addr; + for (i=0; ipkts[i] = 0; + entry->totalops = 0; + for (i=0; istat[i] = 0; + } + entry->pkts[t]++; + return(entry); + } + } + +mDNSlocal int CompareHosts(const void *p1, const void *p2) + { + return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1)); + } + +mDNSlocal void ShowSortedHostList(HostList *list, int max) + { + HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num]; + qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts); + if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, " Pkts Query LegacyQ Response"); + for (e = &list->hosts[0]; e < end; e++) + { + int len = mprintf("%#-25a", &e->addr); + if (len > 25) mprintf("\n%25s", ""); + mprintf("%8d %8d %8d %8d %8d %8d %8d", e->totalops, + e->stat[OP_probe], e->stat[OP_goodbye], + e->stat[OP_browseq], e->stat[OP_browsea], + e->stat[OP_resolveq], e->stat[OP_resolvea]); + mprintf(" %8lu %8lu %8lu %8lu", + HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]); + if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]); + mprintf("\n"); + } + } + +//************************************************************************************************************* +// Receive and process packets + +mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype) + { + int i, len; + const mDNSu8 *src = fqdn->c; + mDNSu8 *dst = srvtype->c; + + len = *src; + if (len == 0 || len >= 0x40) return(mDNSfalse); + if (src[1] != '_') src += 1 + len; + + len = *src; + if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse); + for (i=0; i<=len; i++) *dst++ = *src++; + + len = *src; + if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse); + for (i=0; i<=len; i++) *dst++ = *src++; + + *dst++ = 0; // Put the null root label on the end of the service type + + return(mDNStrue); + } + +mDNSlocal void recordstat(HostEntry *entry, domainname *fqdn, int op, mDNSu16 rrtype) + { + ActivityStat **s = &stats; + domainname srvtype; + + if (op != OP_probe) + { + if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup; + else if (rrtype != kDNSType_PTR) return; + } + + if (!ExtractServiceType(fqdn, &srvtype)) return; + + while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next; + if (!*s) + { + int i; + *s = malloc(sizeof(ActivityStat)); + if (!*s) exit(-1); + (*s)->next = NULL; + (*s)->srvtype = srvtype; + (*s)->printed = 0; + (*s)->totalops = 0; + for (i=0; istat[i] = 0; + } + + (*s)->totalops++; + (*s)->stat[op]++; + if (entry) + { + entry->totalops++; + entry->stat[op]++; + } + } + +mDNSlocal void printstats(int max) + { + int i; + for (i=0; inext) + if (!s->printed && max < s->totalops) + { m = s; max = s->totalops; } + if (!m) return; + m->printed = mDNStrue; + if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner); + mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", &m->srvtype, m->totalops, m->stat[OP_probe], + m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]); + } + } + +mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end, DNSQuestion *q, LargeCacheRecord *pkt) + { + int i; + for (i = 0; i < query->h.numAuthorities; i++) + { + const mDNSu8 *p2 = ptr; + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, pkt); + if (!ptr) break; + if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2); + } + return(mDNSNULL); + } + +mDNSlocal void DisplayTimestamp(void) + { + struct timeval tv; + struct tm tm; + gettimeofday(&tv, NULL); + localtime_r((time_t*)&tv.tv_sec, &tm); + mprintf("\n%d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec); + } + +mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport) + { + const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " : + (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-"; + + DisplayTimestamp(); + mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes", + srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg); + + if (msg->h.id.NotAnInteger) mprintf(" ID:%u", ((mDNSu16)msg->h.id.b[0])<<8 | msg->h.id.b[1]); + + if (msg->h.flags.b[0] & kDNSFlag0_TC) + { + if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated"); + else mprintf(" Truncated (KA list continues in next packet)"); + } + mprintf("\n"); + } + +mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr) + { + static const char hexchars[16] = "0123456789ABCDEF"; + #define MaxWidth 132 + char buffer[MaxWidth+8]; + char *p = buffer; + + RDataBody *rd = &pktrr->rdata->u; + mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; + mDNSu32 n = mprintf("%#-16a %-5s %-5s%5d %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name.c); + + switch(pktrr->rrtype) + { + case kDNSType_A: n += mprintf("%.4a", &rd->ip); break; + case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, &rd->name); break; + case kDNSType_HINFO:// same as kDNSType_TXT below + case kDNSType_TXT: { + mDNSu8 *t = rd->txt.c; + while (t < rdend && t[0] && p < buffer+MaxWidth) + { + int i; + for (i=1; i<=t[0] && p < buffer+MaxWidth; i++) + { + if (t[i] == '\\') *p++ = '\\'; + if (t[i] >= ' ') *p++ = t[i]; + else + { + *p++ = '\\'; + *p++ = '0'; + *p++ = 'x'; + *p++ = hexchars[t[i] >> 4]; + *p++ = hexchars[t[i] & 0xF]; + } + } + t += 1+t[0]; + if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; } + } + *p++ = 0; + n += mprintf("%.*s", MaxWidth - n, buffer); + } break; + case kDNSType_AAAA: n += mprintf("%.16a", &rd->ipv6); break; + case kDNSType_SRV: n += mprintf("%##s:%d", &rd->srv.target, ((mDNSu16)rd->srv.port.b[0] << 8) | rd->srv.port.b[1]); break; + default: { + mDNSu8 *s = rd->data; + while (s < rdend && p < buffer+MaxWidth) + { + if (*s == '\\') *p++ = '\\'; + if (*s >= ' ') *p++ = *s; + else + { + *p++ = '\\'; + *p++ = '0'; + *p++ = 'x'; + *p++ = hexchars[*s >> 4]; + *p++ = hexchars[*s & 0xF]; + } + s++; + } + *p++ = 0; + n += mprintf("%.*s", MaxWidth - n, buffer); + } break; + } + + mprintf("\n"); + } + +mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end) + { + while (ptr < end) + { + int i; + for (i=0; i<16; i++) + if (&ptr[i] < end) mprintf("%02X ", ptr[i]); + else mprintf(" "); + for (i=0; i<16; i++) + if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]); + ptr += 16; + mprintf("\n"); + } + } + +mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg) + { + mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg); + HexDump(ptr, end); + } + +mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID) + { + int i; + const mDNSu8 *ptr = msg->data; + const mDNSu8 *auth = LocateAuthorities(msg, end); + mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger); + HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L); + LargeCacheRecord pkt; + + DisplayPacketHeader(msg, end, srcaddr, srcport); + if (MQ) NumPktQ++; else NumPktL++; + + for (i=0; ih.numQuestions; i++) + { + DNSQuestion q; + mDNSu8 *p2; + const mDNSu8 *ep = ptr; + ptr = getQuestion(msg, ptr, end, InterfaceID, &q); + if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; } + mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse; + q.qclass &= ~kDNSQClass_UnicastResponse; + p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt); + if (p2) + { + NumProbes++; + DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec); + recordstat(entry, &q.qname, OP_probe, q.qtype); + p2 = (mDNSu8 *)skipDomainName(msg, p2, end); + // Having displayed this update record, clear type and class so we don't display the same one again. + p2[0] = p2[1] = p2[2] = p2[3] = 0; + } + else + { + const char *ptype = ucbit ? "(QU)" : "(QM)"; + if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++; + else { NumLegacy++; ptype = "(LQ)"; } + mprintf("%#-16a %-5s %-5s %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c); + recordstat(entry, &q.qname, OP_query, q.qtype); + } + } + + for (i=0; ih.numAnswers; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; } + DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec); + + // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet + // the same as a single query, to more accurately reflect the burden on the network + // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.) + if (msg->h.numQuestions == 0 && i == 0) + recordstat(entry, &pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype); + } + + for (i=0; ih.numAuthorities; i++) + { + const mDNSu8 *ep = ptr; + ptr = skipResourceRecord(msg, ptr, end); + if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } + } + } + +mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID) + { + int i; + const mDNSu8 *ptr = msg->data; + HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R); + LargeCacheRecord pkt; + + DisplayPacketHeader(msg, end, srcaddr, srcport); + NumPktR++; + + for (i=0; ih.numQuestions; i++) + { + DNSQuestion q; + const mDNSu8 *ep = ptr; + ptr = getQuestion(msg, ptr, end, InterfaceID, &q); + if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; } + mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c); + } + + for (i=0; ih.numAnswers; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; } + if (pkt.r.resrec.rroriginalttl) + { + NumAnswers++; + DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec); + recordstat(entry, &pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype); + } + else + { + NumGoodbyes++; + DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec); + recordstat(entry, &pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype); + } + } + + for (i=0; ih.numAuthorities; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } + mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n", + srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name.c); + } + + for (i=0; ih.numAdditionals; i++) + { + const mDNSu8 *ep = ptr; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } + NumAdditionals++; + DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec); + } + } + +mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr) + { + FilterList *f; + if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4); + for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue); + return(mDNSfalse); + } + +mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + { + const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; + const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + + (void)dstaddr; // Unused + (void)dstport; // Unused + + // Read the integer parts which are in IETF byte-order (MSB first, LSB second) + mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; + msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + + if (ttl < 254) + { + debugf("** Apparent spoof mDNS %s packet from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + (QR_OP == StdQ) ? "Query" : (QR_OP == StdR) ? "Response" : "Unkown", + srcaddr, dstaddr, ttl, InterfaceID, + msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", + msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", + msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); + } + + // For now we're only interested in monitoring IPv4 traffic. + // All IPv6 packets should just be duplicates of the v4 packets. + if (AddressMatchesFilterList(srcaddr)) + { + if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, InterfaceID); + else if (QR_OP == StdR) DisplayResponse(m, msg, end, srcaddr, srcport, InterfaceID); + else + { + debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]); + GotPacketFromHost(srcaddr, HostPkt_B); + NumPktB++; + } + } + } + +mDNSlocal mStatus mDNSNetMonitor(void) + { + struct tm tm; + int h, m, s, mul, div, TotPkt; + + mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage, + mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_DontAdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (status) return(status); + + gettimeofday(&tv_start, NULL); + ExampleClientEventLoop(&mDNSStorage); // Wait for user to hit Ctrl-C + + // Now display final summary + TotPkt = NumPktQ + NumPktL + NumPktR; + gettimeofday(&tv_end, NULL); + tv_interval = tv_end; + if (tv_start.tv_usec > tv_interval.tv_usec) + { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; } + tv_interval.tv_sec -= tv_start.tv_sec; + tv_interval.tv_usec -= tv_start.tv_usec; + h = (tv_interval.tv_sec / 3600); + m = (tv_interval.tv_sec % 3600) / 60; + s = (tv_interval.tv_sec % 60); + if (tv_interval.tv_sec > 10) + { + mul = 60; + div = tv_interval.tv_sec; + } + else + { + mul = 60000; + div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000; + if (div == 0) div=1; + } + + mprintf("\n\n"); + localtime_r((time_t*)&tv_start.tv_sec, &tm); + mprintf("Started %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec); + localtime_r((time_t*)&tv_end.tv_sec, &tm); + mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec); + mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec); + if (!Filters) mprintf("Unique source addresses seen on network: %d\n", IPv4HostList.num + IPv6HostList.num); + mprintf("\n"); + mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div); + mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div); + mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR, NumPktR * mul / div); + mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt, TotPkt * mul / div); + mprintf("\n"); + mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes, NumProbes * mul / div); + mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes, NumGoodbyes * mul / div); + mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions, NumQuestions * mul / div); + mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy, NumLegacy * mul / div); + mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers, NumAnswers * mul / div); + mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div); + mprintf("\n"); + printstats(15); + + if (!ExactlyOneFilter) + { + ShowSortedHostList(&IPv4HostList, 15); + ShowSortedHostList(&IPv6HostList, 15); + } + + mDNS_Close(&mDNSStorage); + return(0); + } + +mDNSexport int main(int argc, char **argv) + { + int i; + mStatus status; + + setlinebuf(stdout); // Want to see lines as they appear, not block buffered + + for (i=1; ih_addr; + else goto usage; + } + + FilterList *f = malloc(sizeof(*f)); + f->FilterAddr = a; + f->next = Filters; + Filters = f; + } + + status = mDNSNetMonitor(); + if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", argv[0], status); return(status); } + return(0); + +usage: + fprintf(stderr, "\nmDNS traffic monitor\n"); + fprintf(stderr, "Usage: %s ()\n", argv[0]); + fprintf(stderr, "Optional parameter displays only packets from that host\n"); + + fprintf(stderr, "\nPer-packet header output:\n"); + fprintf(stderr, "-Q- Multicast Query from mDNS client that accepts multicast responses\n"); + fprintf(stderr, "-R- Multicast Response packet containing answers/announcements\n"); + fprintf(stderr, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n"); + fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n"); + + fprintf(stderr, "\nPer-record display:\n"); + fprintf(stderr, "(PM) Probe Question (new service starting), requesting multicast response\n"); + fprintf(stderr, "(PU) Probe Question (new service starting), requesting unicast response\n"); + fprintf(stderr, "(DE) Deletion/Goodbye (service going away)\n"); + fprintf(stderr, "(LQ) Legacy Query Question\n"); + fprintf(stderr, "(QM) Query Question, requesting multicast response\n"); + fprintf(stderr, "(QU) Query Question, requesting unicast response\n"); + fprintf(stderr, "(KA) Known Answer (information querier already knows)\n"); + fprintf(stderr, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n"); + fprintf(stderr, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n"); + fprintf(stderr, "(AD) Unique Additional Record Set (entire RR Set)\n"); + fprintf(stderr, "(AD+) Additional records (add to existing RR Set members)\n"); + + fprintf(stderr, "\nFinal summary, sorted by service type:\n"); + fprintf(stderr, "Probe Probes for this service type starting up\n"); + fprintf(stderr, "Goodbye Goodbye (deletion) packets for this service type shutting down\n"); + fprintf(stderr, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n"); + fprintf(stderr, "BrowseA Browse answers/announcments advertising instances of this service\n"); + fprintf(stderr, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n"); + fprintf(stderr, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n"); + fprintf(stderr, "\n"); + return(-1); + } diff --git a/mDNSPosix/ProxyResponder.c b/mDNSPosix/ProxyResponder.c new file mode 100644 index 0000000..3bf4f11 --- /dev/null +++ b/mDNSPosix/ProxyResponder.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ProxyResponder.c,v $ +Revision 1.22 2003/08/14 02:19:55 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.21 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.20 2003/07/23 00:00:04 cheshire +Add comments + +Revision 1.19 2003/07/15 01:55:16 cheshire + Need to implement service registration with subtypes + +Revision 1.18 2003/07/02 21:19:58 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.17 2003/05/26 03:21:29 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.16 2003/05/26 03:01:28 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.15 2003/05/06 00:00:50 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.14 2003/04/25 01:45:57 cheshire + mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.13 2003/04/18 22:46:12 cheshire +Fix mistake in 1.8 -- INADDR_NONE is 0xFFFFFFFF, not 0 + +Revision 1.12 2003/04/16 02:11:07 cheshire +Fixed mDNS_RegisterNoSuchService non-existance function so that it works again + +Revision 1.11 2003/03/31 22:49:35 cheshire +Add "$Log" header + + */ + +#include // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For errno, EINTR +#include // For inet_addr() +#include // For INADDR_NONE +#include // For gethostbyname() + +#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include "ExampleClientApp.h" + +//************************************************************************************************************* +// Globals +static mDNS mDNSStorage; // mDNS core uses this to store its globals +static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals + +//************************************************************************************************************* +// Proxy Host Registration + +typedef struct + { + mDNSv4Addr ip; + domainlabel hostlabel; // Conforms to standard DNS letter-digit-hyphen host name rules + AuthRecord RR_A; // 'A' (address) record for our ".local" name + AuthRecord RR_PTR; // PTR (reverse lookup) record + } ProxyHost; + +mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + ProxyHost *f = (ProxyHost*)rr->RecordContext; + if (result == mStatus_NoError) + debugf("Host name successfully registered: %##s", &rr->resrec.name); + else + { + debugf("Host name conflict for %##s", &rr->resrec.name); + mDNS_Deregister(m, &f->RR_A); + mDNS_Deregister(m, &f->RR_PTR); + exit(-1); + } + } + +mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) + { + char buffer[32]; + + mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, p); + mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, HostNameCallback, p); + + p->RR_A.resrec.name.c[0] = 0; + AppendDomainLabel(&p->RR_A.resrec.name, &p->hostlabel); + AppendLiteralLabelString(&p->RR_A.resrec.name, "local"); + + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p->ip.b[3], p->ip.b[2], p->ip.b[1], p->ip.b[0]); + MakeDomainNameFromDNSNameString(&p->RR_PTR.resrec.name, buffer); + + p->RR_A. resrec.rdata->u.ip = p->ip; + p->RR_PTR.resrec.rdata->u.name = p->RR_A.resrec.name; + + mDNS_Register(m, &p->RR_A); + mDNS_Register(m, &p->RR_PTR); + + debugf("Made Proxy Host Records for %##s", &p->RR_A.resrec.name); + + return(mStatus_NoError); + } + +//************************************************************************************************************* +// Service Registration + +// This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new +// unique name for the service. For a device such as a printer, this may be appropriate. +// For a device with a user interface, and a screen, and a keyboard, the appropriate +// response may be to prompt the user and ask them to choose a new name for the service. +mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) + { + switch (result) + { + case mStatus_NoError: debugf("Callback: %##s Name Registered", &sr->RR_SRV.resrec.name); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", &sr->RR_SRV.resrec.name); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", &sr->RR_SRV.resrec.name); break; + default: debugf("Callback: %##s Unknown Result %d", &sr->RR_SRV.resrec.name, result); break; + } + + if (result == mStatus_NoError) + { + char buffer[256]; + ConvertDomainNameToCString_unescaped(&sr->RR_SRV.resrec.name, buffer); + printf("Service %s now registered and active\n", buffer); + } + + if (result == mStatus_NameConflict) + { + char buffer1[256], buffer2[256]; + ConvertDomainNameToCString_unescaped(&sr->RR_SRV.resrec.name, buffer1); + mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + ConvertDomainNameToCString_unescaped(&sr->RR_SRV.resrec.name, buffer2); + printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); + } + } + +// RegisterService() is a simple wrapper function which takes C string +// parameters, converts them to domainname parameters, and calls mDNS_RegisterService() +mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, + const char name[], const char type[], const char domain[], + const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv) + { + domainlabel n; + domainname t, d; + mDNSIPPort port; + unsigned char buffer[1024], *bptr = buffer; + + MakeDomainLabelFromLiteralString(&n, name); + MakeDomainNameFromDNSNameString(&t, type); + MakeDomainNameFromDNSNameString(&d, domain); + port.b[0] = (mDNSu8)(PortAsNumber >> 8); + port.b[1] = (mDNSu8)(PortAsNumber ); + while (argc) + { + int len = strlen(argv[0]); + printf("STR: %s\n", argv[0]); + bptr[0] = len; + strcpy(bptr+1, argv[0]); + bptr += 1 + len; + argc--; + argv++; + } + + mDNS_RegisterService(m, recordset, + &n, &t, &d, // Name, type, domain + host, port, // Host and port + buffer, bptr-buffer, // TXT data, length + mDNSNULL, 0, // Subtypes + mDNSInterface_Any, // Interace ID + ServiceCallback, mDNSNULL); // Callback and context + + ConvertDomainNameToCString_unescaped(&recordset->RR_SRV.resrec.name, buffer); + printf("Made Service Records for %s\n", buffer); + } + +//************************************************************************************************************* +// Service non-existence assertion +// (claiming a service name without actually providing a service at that name, to prevent anyone else using that name) +// This is useful to avoid confusion between similar services +// e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service, +// should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name, +// since it would be confusing to users to have two equivalent services with the same name. + +mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + domainname *proxyhostname = (domainname *)rr->RecordContext; + switch (result) + { + case mStatus_NoError: debugf("Callback: %##s Name Registered", &rr->resrec.name); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", &rr->resrec.name); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", &rr->resrec.name); break; + default: debugf("Callback: %##s Unknown Result %d", &rr->resrec.name, result); break; + } + + if (result == mStatus_NoError) + { + char buffer[256]; + ConvertDomainNameToCString_unescaped(&rr->resrec.name, buffer); + printf("Non-existence assertion %s now registered and active\n", buffer); + } + + if (result == mStatus_NameConflict) + { + domainlabel n; + domainname t, d; + char buffer1[256], buffer2[256]; + ConvertDomainNameToCString_unescaped(&rr->resrec.name, buffer1); + DeconstructServiceName(&rr->resrec.name, &n, &t, &d); + IncrementLabelSuffix(&n, mDNStrue); + mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL); + ConvertDomainNameToCString_unescaped(&rr->resrec.name, buffer2); + printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2); + } + } + +mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname, + const char name[], const char type[], const char domain[]) + { + domainlabel n; + domainname t, d; + unsigned char buffer[256]; + MakeDomainLabelFromLiteralString(&n, name); + MakeDomainNameFromDNSNameString(&t, type); + MakeDomainNameFromDNSNameString(&d, domain); + mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname); + ConvertDomainNameToCString_unescaped(&rr->resrec.name, buffer); + printf("Made Non-existence Record for %s\n", buffer); + } + +//************************************************************************************************************* +// Main + +mDNSexport int main(int argc, char **argv) + { + mStatus status; + + if (argc < 3) goto usage; + + status = mDNS_Init(&mDNSStorage, &PlatformStorage, + mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_DontAdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %ld\n", status); return(status); } + + if (!strcmp(argv[1], "-")) + { + domainname proxyhostname; + AuthRecord proxyrecord; + if (argc < 5) goto usage; + proxyhostname.c[0] = 0; + AppendLiteralLabelString(&proxyhostname, argv[2]); + AppendLiteralLabelString(&proxyhostname, "local"); + RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local."); + ExampleClientEventLoop(&mDNSStorage); + mDNS_Close(&mDNSStorage); + } + else + { + ProxyHost proxyhost; + ServiceRecordSet proxyservice; + + proxyhost.ip.NotAnInteger = inet_addr(argv[1]); + if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF + { + struct hostent *h = gethostbyname(argv[1]); + if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr; + } + if (proxyhost.ip.NotAnInteger == INADDR_NONE) // INADDR_NONE is 0xFFFFFFFF + { + fprintf(stderr, "%s is not valid host address\n", argv[1]); + return(-1); + } + + MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]); + + mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost); + + if (argc >=6) + RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.", + &proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]); + + ExampleClientEventLoop(&mDNSStorage); + mDNS_Close(&mDNSStorage); + } + + return(0); + +usage: + fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]); + fprintf(stderr, "ip Real IP address (or valid host name) of the host where the service actually resides\n"); + fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n"); + fprintf(stderr, "srvname Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n"); + fprintf(stderr, "srvtype IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n"); + fprintf(stderr, "port Port number where the service resides (1-65535)\n"); + fprintf(stderr, "txt Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n"); + fprintf(stderr, "e.g. %s 169.254.12.34 thehost (just create a dot-local host name)\n", argv[0]); + fprintf(stderr, "or %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]); + fprintf(stderr, "or %s - thehost \"My Printer\" _printer._tcp. (assertion of non-existence)\n", argv[0]); + return(-1); + } diff --git a/mDNSPosix/ReadMe.txt b/mDNSPosix/ReadMe.txt new file mode 100755 index 0000000..fc00dbd --- /dev/null +++ b/mDNSPosix/ReadMe.txt @@ -0,0 +1,310 @@ +ReadMe About mDNSPosix +---------------------- + +mDNSPosix is a port of Apple's core mDNS code to the Posix platform. +The sample shows how you might implement an mDNS responder inside an +embedded device, such as a printer or a web camera. + +mDNS is short for "multicast DNS", which is a technology that allows you +to register IP services and browse the network for those services. For +more information about mDNS, see the mDNS web site. + + + +mDNS is part of a family of technologies developed by the IETF zeroconf +working group. For information about other zeroconf technologies, see +the zeroconf web site. + + + +Apple uses the brand name "Rendezvous" to describe our implementation of +zeroconf technologies. This sample is designed to show how easy it is +to make a device "Rendezvous compatible". + +The code in this sample was compiled and tested on Mac OS X (10.1.x, +10.2), Solaris (SunOS 5.6), Linux (Redhat 2.4.9-21), and OpenBSD (2.9). +YMMV. + +IMPORTANT +This sample is not a full port of Apple's Rendezvous APIs to Posix. +Specifically, the sample includes a responder daemon that registers +entities based on its command line arguments (or a text file). This is +perfect for a embedded device, but is not suitable for a general purpose +computer. A real implementation of the Rendezvous APIs would require a +mDNS daemon, client libraries that applications link with, and some form +of RPC between them. Client libraries and client-to-daemon RPC are +beyond the scope of this sample, however, this would be a good place to +start if you were interested in implementing these facilities on your +platform. + + +Packing List +------------ +The sample includes the following files and directories: + +o ReadMe.txt -- This file. + +o mDNSCore -- A directory containing the core mDNS code. This code is + written in pure ANSI C and has proved to be very portable. + +o mDNSPosix.h -- The interface to the platform support code. + +o mDNSPosix.c -- The platform support code for the Posix platform. + This code glues the mDNS core to Posix. + +o mDNSUNP.h -- Interface to the code in "mDNSUNP.c". + +o mDNSUNP.c -- A few routines from the "Unix Network Programming" book + that I borrowed to make the port easier. The routines are slightly + modified from the originals to meet my specific needs. You can get the + originals at the URL below. + + + +o Client.c -- The main program for the sample mDNS client. + +o Responder.c -- The main program for the sample mDNS responder. + +o Services.txt -- A sample configuration file for the mDNS responder. + You can test with this file using the option "-f Services.txt". + +o ProxyResponder.c -- Another sample mDNS responder, this one intended + for creating proxy registrations for other network devices that don't + have their own mDNS responders. + +o ExampleClientApp.h +o ExampleClientApp.c -- shared code prioviding the + "ExampleClientEventLoop" used by Client.c and ProxyResponder.c. + +o Makefile -- A makefile for building on Mac OS X and other platforms. + + +Building the Sample +------------------- +The sample does not use autoconf technology, primarily because I didn't +want to delay shipping while I learnt how to use it. Thus the code +builds using a very simple make file. To build the sample you should +type "make os=myos", e.g. + + make os=osx + +For Linux you would change that to: + + make os=linux + +There are definitions for each of the platforms I ported to. If you're +porting to any other platform you'll have to add appropriate definitions +for it. + + +Using the Sample +---------------- +Once you've built the sample you can test it by first running the +client, as shown below. + + quinn% build/mDNSClientPosix + Hit ^C when you're bored waiting for responses. + +By default the client starts a search for AppleShare servers and then +sits and waits, printing a message when services appear and disappear. + +To continue with the test you should start the responder in another +shell window. + + quinn% build/mDNSResponderPosix -n Foo + +This will start the responder and tell it to advertise a AppleShare +service "Foo". In the client window you will see the client print out +the following as the service shows up on the network. + + quinn% build/mDNSClientPosix + Hit ^C when you're bored waiting for responses. + *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.' + +Back in the responder window you can quit the responder cleanly using +SIGINT (typically ^C). + + quinn% build/mDNSResponderPosix -n Foo + ^C + quinn% + +As the responder quits it will multicast that the "Foo" service is +disappearing and the client will see that notification and print a +message to that effect (shown below). Finally, when you're done with +the client you can use SIGINT to quit it. + + quinn% build/mDNSClientPosix + Hit ^C when you're bored waiting for responses. + *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.' + *** Lost name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.' + ^C + quinn% + +If things don't work, try starting each program in verbose mode (using +the "-v 1" option, or very verbose mode with "-v 2") to see if there's +an obvious cause. + +That's it for the core functionality. Each program supports a variety +of other options. For example, you can advertise and browse for a +different service type using the "-t type" option. Use the "-?" option +on each program for more user-level information. + + +How It Works +------------ +A typical mDNS program is divided into three sections. + + +----------------+ + | Application | + +----------------+ + | mDNS Core | + +----------------+ + | Posix Platform | + +----------------+ + +The mDNS core code comprises the files in the "mDNSCore" directory. +It's standard ANSI C that's very portable. It relies on the underlying +platform code for all external functionality. + +In this example the external platform code glues the mDNS core to a +POSIX-ish platform. This code is contained in the files: + +o mDNSPosix.h +o mDNSPosix.c +o mDNSUNP.h +o mDNSUNP.c + +The guts of the code is in "mDNSPosix.c". + +I should be clear that true POSIX isn't powerful enough to accomplish +the job, so this code doesn't compile with _POSIX_SOURCE defined and +there's a bunch of conditional code that does different things on +different Unixen. I've isolated the hairiest parts of this code in the +"mDNSUNP". + +Above the mDNS core code is the code that actually does +application-specific tasks. In this example I've supplied two +application programs: the responder (Responder.c) acts as a simple mDNS +responder, listening for mDNS service lookup requests and answering +them, and the client (Client.c), which is a simple mDNS browser, making +simple mDNS search queries. Both programs use the same mDNS core and +Posix platform code. + +A discussion of the mDNS protocol itself is beyond the scope of this +sample. Quite frankly, my goal here was to demonstrate how it easy it +is to use Apple's mDNS core without actually understanding mDNS, and +because I achieved that goal I never had to learn a lot about how the +mDNS core code works. It's just a black box that I call. If you want +to learn more about mDNS, see the references at the top of this file. + +The mDNS Posix platform code is actually pretty simple. mDNS core +requires six key features in its platform support. + +o the core calls the platformm at startup (mDNSPlatformInit) + and shutdown (mDNSPlatformClose) + +o the core calls the platform to send a UDP packet (mDNSPlatformSendUDP) + +o the core calls the platform to set a timer (mDNSPlatformScheduleTask) + +o the platform calls the core (mDNSCoreTask) when the timer expires + +o the platform calls the core (mDNSCoreReceive) when a UDP datagram arrives + +o the platform calls the core when network interfaces are + added (mDNS_RegisterInterface) or removed (mDNS_DeregisterInterface) + +All of these features are implemented in "mDNSPosix.c". + +The runtime behaviour of the code is as follows. + +1. The application calls mDNS_Init, which in turns calls the platform + (mDNSPlatformInit). + +2. mDNSPlatformInit gets a list of interfaces (get_ifi_info) and registers + each one with the core (mDNS_RegisterInterface). For each interface + it also creates a multicast socket (SetupSocket). + +3. The application then calls select() repeatedly to handle file descriptor + events. Before calling select() each time, the application calls + mDNSPosixGetFDSet() to give mDNSPosix.c a chance to add its own file + descriptors to the set, and then after select() returns, it calls + mDNSPosixProcessFDSet() to give mDNSPosix.c a chance to receive and + process any packets that may have arrived. + +4. When the core needs to send a UDP packet it calls + mDNSPlatformSendUDP. That routines finds the interface that + corresponds to the source address requested by the core, and + sends the datagram using the UDP socket created for the + interface. If the socket is flow send-side controlled it just + drops the packet. + +5. When SocketDataReady runs it uses a complex routine, + "recvfrom_flags", to actually receive the packet. This is required + because the core needs information about the packet that is + only available via the "recvmsg" call, and that call is complex + to implement in a portable way. I got my implementation of + "recvfrom_flags" from Stevens' "UNIX Network Programming", but + I had to modify it further to work with Linux. + +One thing to note is that the Posix platform code is very deliberately +not multi-threaded. I do everything from a main loop that calls +"select()". This is good because it avoids all the problems that often +accompany multi-threaded code. If you decide to use threads in your +platform, you will have to implement the mDNSPlatformLock() and +mDNSPlatformUnlock() calls which are no-ops in mDNSPosix.c. + + +Caveats +------- +Currently the program uses a simple make file. + +There are various problems with loopback-only self discovery. The code +will attempt service discovery on the loopback interface only if no +other interfaces are available. However, this exposes a number of +problems with the underlying network stack (at least on Mac OS X). + +o On Mac OS X 10.1.x the code fails to start on the loopback interface + because the IP_ADD_MEMBERSHIP option returns ENOBUFS. + +o On Mac OS X 10.2 the loopback-only case fails because + mDNSPlatformSendUDP's call to "sendto" fails with error EHOSTUNREACH + [Radar ID 3016042]. + +I haven't been able to test the loopback-only case on other platforms +because I don't have access to the physical machine. + + +Licencing +--------- +This code is distributed under the Apple Public Source License. +Information about the licence is included at the top of each source file. + + +Credits and Version History +--------------------------- +If you find any problems with this sample, mail and I +will try to fix them up. + +1.0a1 (Jul 2002) was a prerelease version that was distributed +internally at Apple. + +1.0a2 (Jul 2002) was a prerelease version that was distributed +internally at Apple. + +1.0a3 (Aug 2002) was the first shipping version. The core mDNS code is +the code from Mac OS 10.2 (Jaguar) GM. + +Share and Enjoy + +Apple Developer Technical Support +Networking, Communications, Hardware + +6 Aug 2002 + + +To Do List +---------- +Â¥ port to a System V that's not Solaris +Â¥ use sig_atomic_t for signal to main thread flags +Â¥ test and debug the daemon function, including properly logging diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c new file mode 100755 index 0000000..6130e0c --- /dev/null +++ b/mDNSPosix/Responder.c @@ -0,0 +1,923 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Responder.c,v $ +Revision 1.16 2003/08/14 02:19:55 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.15 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.14 2003/08/06 18:20:51 cheshire +Makefile cleanup + +Revision 1.13 2003/07/23 00:00:04 cheshire +Add comments + +Revision 1.12 2003/07/15 01:55:16 cheshire + Need to implement service registration with subtypes + +Revision 1.11 2003/07/14 18:11:54 cheshire +Fix stricter compiler warnings + +Revision 1.10 2003/07/10 20:27:31 cheshire + mDNSResponder Posix version is missing a 'b' in the getopt option string + +Revision 1.9 2003/07/02 21:19:59 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.8 2003/06/18 05:48:41 cheshire +Fix warnings + +Revision 1.7 2003/05/06 00:00:50 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.6 2003/03/08 00:35:56 cheshire +Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") + +Revision 1.5 2003/02/20 06:48:36 cheshire +Bug #: 3169535 Xserve RAID needs to do interface-specific registrations +Reviewed by: Josh Graessley, Bob Bradley + +Revision 1.4 2003/01/28 03:07:46 cheshire +Add extra parameter to mDNS_RenameAndReregisterService(), +and add support for specifying a domain other than dot-local. + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 04:20:44 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.1 2002/09/17 06:24:35 cheshire +First checkin + +*/ + +#include "mDNSClientAPI.h"// Defines the interface to the client layer above +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform + +#include +#include // For printf() +#include // For exit() etc. +#include // For strlen() etc. +#include // For select() +#include // For errno, EINTR +#include +#include + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Globals +#endif + +static mDNS mDNSStorage; // mDNS core uses this to store its globals +static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals + +static const char *gProgramName = "mDNSResponderPosix"; + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Signals +#endif + +static volatile mDNSBool gReceivedSigUsr1; +static volatile mDNSBool gReceivedSigHup; +static volatile mDNSBool gStopNow; + +// We support 4 signals. +// +// o SIGUSR1 toggles verbose mode on and off in debug builds +// o SIGHUP triggers the program to re-read its preferences. +// o SIGINT causes an orderly shutdown of the program. +// o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous) +// o SIGKILL kills us dead (easy to implement :-) +// +// There are fatal race conditions in our signal handling, but there's not much +// we can do about them while remaining within the Posix space. Specifically, +// if a signal arrives after we test the globals its sets but before we call +// select, the signal will be dropped. The user will have to send the signal +// again. Unfortunately, Posix does not have a "sigselect" to atomically +// modify the signal mask and start a select. + +static void HandleSigUsr1(int sigraised) + // If we get a SIGUSR1 we toggle the state of the + // verbose mode. +{ + assert(sigraised == SIGUSR1); + gReceivedSigUsr1 = mDNStrue; +} + +static void HandleSigHup(int sigraised) + // A handler for SIGHUP that causes us to break out of the + // main event loop when the user kill 1's us. This has the + // effect of triggered the main loop to deregister the + // current services and re-read the preferences. +{ + assert(sigraised == SIGHUP); + gReceivedSigHup = mDNStrue; +} + +static void HandleSigInt(int sigraised) + // A handler for SIGINT that causes us to break out of the + // main event loop when the user types ^C. This has the + // effect of quitting the program. +{ + assert(sigraised == SIGINT); + + if (gMDNSPlatformPosixVerboseLevel > 0) { + fprintf(stderr, "\nSIGINT\n"); + } + gStopNow = mDNStrue; +} + +static void HandleSigQuit(int sigraised) + // If we get a SIGQUIT the user is desperate and we + // just call mDNS_Close directly. This is definitely + // not safe (because it could reenter mDNS), but + // we presume that the user has already tried the safe + // alternatives. +{ + assert(sigraised == SIGQUIT); + + if (gMDNSPlatformPosixVerboseLevel > 0) { + fprintf(stderr, "\nSIGQUIT\n"); + } + mDNS_Close(&mDNSStorage); + exit(0); +} + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Parameter Checking +#endif + +static mDNSBool CheckThatRichTextHostNameIsUsable(const char *richTextHostName, mDNSBool printExplanation) + // Checks that richTextHostName is a reasonable host name + // label and, if it isn't and printExplanation is true, prints + // an explanation of why not. +{ + mDNSBool result; + domainlabel richLabel; + domainlabel poorLabel; + + result = mDNStrue; + if (result && strlen(richTextHostName) > 63) { + if (printExplanation) { + fprintf(stderr, + "%s: Host name is too long (must be 63 characters or less)\n", + gProgramName); + } + result = mDNSfalse; + } + if (result && richTextHostName[0] == 0) { + if (printExplanation) { + fprintf(stderr, "%s: Host name can't be empty\n", gProgramName); + } + result = mDNSfalse; + } + if (result) { + MakeDomainLabelFromLiteralString(&richLabel, richTextHostName); + ConvertUTF8PstringToRFC1034HostLabel(richLabel.c, &poorLabel); + if (poorLabel.c[0] == 0) { + if (printExplanation) { + fprintf(stderr, + "%s: Host name doesn't produce a usable RFC-1034 name\n", + gProgramName); + } + result = mDNSfalse; + } + } + return result; +} + +static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation) + // Checks that serviceType is a reasonable service type + // label and, if it isn't and printExplanation is true, prints + // an explanation of why not. +{ + mDNSBool result; + + result = mDNStrue; + if (result && strlen(serviceType) > 63) { + if (printExplanation) { + fprintf(stderr, + "%s: Service type is too long (must be 63 characters or less)\n", + gProgramName); + } + result = mDNSfalse; + } + if (result && serviceType[0] == 0) { + if (printExplanation) { + fprintf(stderr, + "%s: Service type can't be empty\n", + gProgramName); + } + result = mDNSfalse; + } + return result; +} + +static mDNSBool CheckThatServiceTextIsUsable(const char *serviceText, mDNSBool printExplanation, + mDNSu8 *pStringList, mDNSu16 *pStringListLen) + // Checks that serviceText is a reasonable service text record + // and, if it isn't and printExplanation is true, prints + // an explanation of why not. Also parse the text into + // the packed PString buffer denoted by pStringList and + // return the length of that buffer in *pStringListLen. + // Note that this routine assumes that the buffer is + // sizeof(RDataBody) bytes long. +{ + mDNSBool result; + size_t serviceTextLen; + + // Note that parsing a C string into a PString list always + // expands the data by one character, so the following + // compare is ">=", not ">". Here's the logic: + // + // #1 For a string with not ^A's, the PString length is one + // greater than the C string length because we add a length + // byte. + // #2 For every regular (not ^A) character you add to the C + // string, you add a regular character to the PString list. + // This does not affect the equivalence stated in #1. + // #3 For every ^A you add to the C string, you add a length + // byte to the PString list but you also eliminate the ^A, + // which again does not affect the equivalence stated in #1. + + result = mDNStrue; + serviceTextLen = strlen(serviceText); + if (result && strlen(serviceText) >= sizeof(RDataBody)) { + if (printExplanation) { + fprintf(stderr, + "%s: Service text record is too long (must be less than %d characters)\n", + gProgramName, + (int) sizeof(RDataBody) ); + } + result = mDNSfalse; + } + + // Now break the string up into PStrings delimited by ^A. + // We know the data will fit so we can ignore buffer overrun concerns. + // However, we still have to treat runs long than 255 characters as + // an error. + + if (result) { + int lastPStringOffset; + int i; + int thisPStringLen; + + // This algorithm is a little tricky. We start by copying + // the string directly into the output buffer, shifted up by + // one byte. We then fill in the first byte with a ^A. + // We then walk backwards through the buffer and, for each + // ^A that we find, we replace it with the difference between + // its offset and the offset of the last ^A that we found + // (ie lastPStringOffset). + + memcpy(&pStringList[1], serviceText, serviceTextLen); + pStringList[0] = 1; + lastPStringOffset = serviceTextLen + 1; + for (i = serviceTextLen; i >= 0; i--) { + if ( pStringList[i] == 1 ) { + thisPStringLen = (lastPStringOffset - i - 1); + assert(thisPStringLen >= 0); + if (thisPStringLen > 255) { + result = mDNSfalse; + if (printExplanation) { + fprintf(stderr, + "%s: Each component of the service text record must be 255 characters or less\n", + gProgramName); + } + break; + } else { + pStringList[i] = thisPStringLen; + lastPStringOffset = i; + } + } + } + + *pStringListLen = serviceTextLen + 1; + } + + return result; +} + +static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation) + // Checks that portNumber is a reasonable port number + // and, if it isn't and printExplanation is true, prints + // an explanation of why not. +{ + mDNSBool result; + + result = mDNStrue; + if (result && (portNumber <= 0 || portNumber > 65535)) { + if (printExplanation) { + fprintf(stderr, + "%s: Port number specified by -p must be in range 1..65535\n", + gProgramName); + } + result = mDNSfalse; + } + return result; +} + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Command Line Arguments +#endif + +static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid"; +static const char kDefaultServiceType[] = "_afpovertcp._tcp."; +static const char kDefaultServiceDomain[] = "local."; +enum { + kDefaultPortNumber = 548 +}; + +static void PrintUsage() +{ + fprintf(stderr, + "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-x TXT] [-p port] [-f file] [-b] [-P pidfile]\n", + gProgramName); + fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n"); + fprintf(stderr, " 0 = no debugging info (default)\n"); + fprintf(stderr, " 1 = standard debugging info\n"); + fprintf(stderr, " 2 = intense debugging info\n"); + fprintf(stderr, " can be cycled kill -USR1\n"); + fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n"); + fprintf(stderr, " -n uses 'name' as the host name (default is none)\n"); + fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType); + fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain); + fprintf(stderr, " -x uses 'TXT' as the service TXT record (default is empty)\n"); + fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber); + fprintf(stderr, " -f reads a service list from 'file'\n"); + fprintf(stderr, " -b forces daemon (background) mode\n"); + fprintf(stderr, " -P uses 'pidfile' as the PID file\n"); + fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile); + fprintf(stderr, " only meaningful if -b also specified\n"); +} + +static mDNSBool gAvoidPort53 = mDNStrue; +static const char *gRichTextHostName = ""; +static const char *gServiceType = kDefaultServiceType; +static const char *gServiceDomain = kDefaultServiceDomain; +static mDNSu8 gServiceText[sizeof(RDataBody)]; +static mDNSu16 gServiceTextLen = 0; +static int gPortNumber = kDefaultPortNumber; +static const char *gServiceFile = ""; +static mDNSBool gDaemon = mDNSfalse; +static const char *gPIDFile = kDefaultPIDFile; + +static void ParseArguments(int argc, char **argv) + // Parses our command line arguments into the global variables + // listed above. +{ + int ch; + + // Set gProgramName to the last path component of argv[0] + + gProgramName = strrchr(argv[0], '/'); + if (gProgramName == NULL) { + gProgramName = argv[0]; + } else { + gProgramName += 1; + } + + // Parse command line options using getopt. + + do { + ch = getopt(argc, argv, "v:rn:t:d:x:p:f:dPb"); + if (ch != -1) { + switch (ch) { + case 'v': + gMDNSPlatformPosixVerboseLevel = atoi(optarg); + if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { + fprintf(stderr, + "%s: Verbose mode must be in the range 0..2\n", + gProgramName); + exit(1); + } + break; + case 'r': + gAvoidPort53 = mDNSfalse; + break; + case 'n': + gRichTextHostName = optarg; + if ( ! CheckThatRichTextHostNameIsUsable(gRichTextHostName, mDNStrue) ) { + exit(1); + } + break; + case 't': + gServiceType = optarg; + if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { + exit(1); + } + break; + case 'd': + gServiceDomain = optarg; + break; + case 'x': + if ( ! CheckThatServiceTextIsUsable(optarg, mDNStrue, gServiceText, &gServiceTextLen) ) { + exit(1); + } + break; + case 'p': + gPortNumber = atol(optarg); + if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) { + exit(1); + } + break; + case 'f': + gServiceFile = optarg; + break; + case 'b': + gDaemon = mDNStrue; + break; + case 'P': + gPIDFile = optarg; + break; + case '?': + default: + PrintUsage(); + exit(1); + break; + } + } + } while (ch != -1); + + // Check for any left over command line arguments. + + if (optind != argc) { + PrintUsage(); + fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]); + exit(1); + } + + // Check for inconsistency between the arguments. + + if ( (gRichTextHostName[0] == 0) && (gServiceFile[0] == 0) ) { + PrintUsage(); + fprintf(stderr, "%s: You must specify a service to register (-n) or a service file (-f).\n", gProgramName); + exit(1); + } +} + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Registration +#endif + +typedef struct PosixService PosixService; + +struct PosixService { + ServiceRecordSet coreServ; + PosixService *next; + int serviceID; +}; + +static PosixService *gServiceList = NULL; + +static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status) + // mDNS core calls this routine to tell us about the status of + // our registration. The appropriate action to take depends + // entirely on the value of status. +{ + switch (status) { + + case mStatus_NoError: + debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name.c); + // Do nothing; our name was successfully registered. We may + // get more call backs in the future. + break; + + case mStatus_NameConflict: + debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name.c); + + // In the event of a conflict, this sample RegistrationCallback + // just calls mDNS_RenameAndReregisterService to automatically + // pick a new unique name for the service. For a device such as a + // printer, this may be appropriate. For a device with a user + // interface, and a screen, and a keyboard, the appropriate response + // may be to prompt the user and ask them to choose a new name for + // the service. + // + // Also, what do we do if mDNS_RenameAndReregisterService returns an + // error. Right now I have no place to send that error to. + + status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL); + assert(status == mStatus_NoError); + break; + + case mStatus_MemFree: + debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name.c); + + // When debugging is enabled, make sure that thisRegistration + // is not on our gServiceList. + + #if !defined(NDEBUG) + { + PosixService *cursor; + + cursor = gServiceList; + while (cursor != NULL) { + assert(&cursor->coreServ != thisRegistration); + cursor = cursor->next; + } + } + #endif + free(thisRegistration); + break; + + default: + debugf("Callback: %##s Unknown Status %d", thisRegistration->RR_SRV.resrec.name.c, status); + break; + } +} + +static int gServiceID = 0; + +static mStatus RegisterOneService(const char * richTextHostName, + const char * serviceType, + const char * serviceDomain, + const mDNSu8 text[], + mDNSu16 textLen, + long portNumber) +{ + mStatus status; + PosixService * thisServ; + mDNSOpaque16 port; + domainlabel name; + domainname type; + domainname domain; + + status = mStatus_NoError; + thisServ = (PosixService *) malloc(sizeof(*thisServ)); + if (thisServ == NULL) { + status = mStatus_NoMemoryErr; + } + if (status == mStatus_NoError) { + MakeDomainLabelFromLiteralString(&name, richTextHostName); + MakeDomainNameFromDNSNameString(&type, serviceType); + MakeDomainNameFromDNSNameString(&domain, serviceDomain); + port.b[0] = (portNumber >> 8) & 0x0FF; + port.b[1] = (portNumber >> 0) & 0x0FF;; + status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ, + &name, &type, &domain, // Name, type, domain + NULL, port, // Host and port + text, textLen, // TXT data, length + NULL, 0, // Subtypes + mDNSInterface_Any, // Interace ID + RegistrationCallback, thisServ); // Callback and context + } + if (status == mStatus_NoError) { + thisServ->serviceID = gServiceID; + gServiceID += 1; + + thisServ->next = gServiceList; + gServiceList = thisServ; + + if (gMDNSPlatformPosixVerboseLevel > 0) { + fprintf(stderr, + "%s: Registered service %d, name '%s', type '%s', port %ld\n", + gProgramName, + thisServ->serviceID, + richTextHostName, + serviceType, + portNumber); + } + } else { + if (thisServ != NULL) { + free(thisServ); + } + } + return status; +} + +static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp) +{ + mDNSBool good; + size_t len; + + good = (fgets(buf, bufSize, fp) != NULL); + if (good) { + len = strlen(buf); + good = (len > 0 && buf[len - 1] == '\n'); + } + if (good) { + buf[len - 1] = 0; + } + return good; +} + +static mStatus RegisterServicesInFile(const char *filePath) +{ + mStatus status; + FILE * fp; + int junk; + mDNSBool good; + int ch; + char name[256]; + char type[256]; + const char *dom = kDefaultServiceDomain; + char rawText[1024]; + mDNSu8 text[sizeof(RDataBody)]; + mDNSu16 textLen; + char port[256]; + + status = mStatus_NoError; + fp = fopen(filePath, "r"); + if (fp == NULL) { + status = mStatus_UnknownErr; + } + if (status == mStatus_NoError) { + good = mDNStrue; + do { + // Skip over any blank lines. + do { + ch = fgetc(fp); + } while ( ch == '\n' || ch == '\r' ); + if (ch != EOF) { + good = (ungetc(ch, fp) == ch); + } + + // Read three lines, check them for validity, and register the service. + if ( good && ! feof(fp) ) { + good = ReadALine(name, sizeof(name), fp); + if (good) { + good = ReadALine(type, sizeof(type), fp); + } + if (good) { + char *p = type; + while (*p && *p != ' ') p++; + if (*p) { + *p = 0; + dom = p+1; + } + } + if (good) { + good = ReadALine(rawText, sizeof(rawText), fp); + } + if (good) { + good = ReadALine(port, sizeof(port), fp); + } + if (good) { + good = CheckThatRichTextHostNameIsUsable(name, mDNSfalse) + && CheckThatServiceTypeIsUsable(type, mDNSfalse) + && CheckThatServiceTextIsUsable(rawText, mDNSfalse, text, &textLen) + && CheckThatPortNumberIsUsable(atol(port), mDNSfalse); + } + if (good) { + status = RegisterOneService(name, type, dom, text, textLen, atol(port)); + if (status != mStatus_NoError) { + fprintf(stderr, + "%s: Failed to register service, name = %s, type = %s, port = %s\n", + gProgramName, + name, + type, + port); + status = mStatus_NoError; // keep reading + } + } + } + } while (good && !feof(fp)); + + if ( ! good ) { + fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, gServiceFile); + } + } + + if (fp != NULL) { + junk = fclose(fp); + assert(junk == 0); + } + + return status; +} + +static mStatus RegisterOurServices(void) +{ + mStatus status; + + status = mStatus_NoError; + if (gRichTextHostName[0] != 0) { + status = RegisterOneService(gRichTextHostName, + gServiceType, + gServiceDomain, + gServiceText, gServiceTextLen, + gPortNumber); + } + if (status == mStatus_NoError && gServiceFile[0] != 0) { + status = RegisterServicesInFile(gServiceFile); + } + return status; +} + +static void DeregisterOurServices(void) +{ + PosixService *thisServ; + int thisServID; + + while (gServiceList != NULL) { + thisServ = gServiceList; + gServiceList = thisServ->next; + + thisServID = thisServ->serviceID; + + mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ); + + if (gMDNSPlatformPosixVerboseLevel > 0) { + fprintf(stderr, + "%s: Deregistered service %d\n", + gProgramName, + thisServ->serviceID); + } + } +} + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark **** Main +#endif + +#ifdef NOT_HAVE_DAEMON + + // The version of Solaris that I tested on didn't have the daemon + // call. This implementation was basically stolen from the + // Mac OS X standard C library. + + static int daemon(int nochdir, int noclose) + { + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = _open("/dev/null", O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)_close(fd); + } + return (0); + } + +#endif /* NOT_HAVE_DAEMON */ + +int main(int argc, char **argv) +{ + mStatus status; + int result; + + // Parse our command line arguments. This won't come back if there's an error. + + ParseArguments(argc, argv); + + // If we're told to run as a daemon, then do that straight away. + // Note that we don't treat the inability to create our PID + // file as an error. Also note that we assign getpid to a long + // because printf has no format specified for pid_t. + + if (gDaemon) { + if (gMDNSPlatformPosixVerboseLevel > 0) { + fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName); + } + daemon(0,0); + { + FILE *fp; + int junk; + + fp = fopen(gPIDFile, "w"); + if (fp != NULL) { + fprintf(fp, "%ld\n", (long) getpid()); + junk = fclose(fp); + assert(junk == 0); + } + } + } else { + if (gMDNSPlatformPosixVerboseLevel > 0) { + fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid()); + } + } + + status = mDNS_Init(&mDNSStorage, &PlatformStorage, + mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_AdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (status != mStatus_NoError) return(2); + + status = RegisterOurServices(); + if (status != mStatus_NoError) return(2); + + signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP + signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C + signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed) + signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 + + while (!gStopNow) + { + int nfds = 0; + fd_set readfds; + struct timeval timeout; + int result; + + // 1. Set up the fd_set as usual here. + // This example client has no file descriptors of its own, + // but a real application would call FD_SET to add them to the set here + FD_ZERO(&readfds); + + // 2. Set up the timeout. + // This example client has no other work it needs to be doing, + // so we set an effectively infinite timeout + timeout.tv_sec = 0x3FFFFFFF; + timeout.tv_usec = 0; + + // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout + mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout); + + // 4. Call select as normal + verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); + result = select(nfds, &readfds, NULL, NULL, &timeout); + + if (result < 0) + { + verbosedebugf("select() returned %d errno %d", result, errno); + if (errno != EINTR) gStopNow = mDNStrue; + else + { + if (gReceivedSigUsr1) + { + gReceivedSigUsr1 = mDNSfalse; + gMDNSPlatformPosixVerboseLevel += 1; + if (gMDNSPlatformPosixVerboseLevel > 2) + gMDNSPlatformPosixVerboseLevel = 0; + if ( gMDNSPlatformPosixVerboseLevel > 0 ) + fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel); + } + if (gReceivedSigHup) + { + if (gMDNSPlatformPosixVerboseLevel > 0) + fprintf(stderr, "\nSIGHUP\n"); + gReceivedSigHup = mDNSfalse; + DeregisterOurServices(); + status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage); + if (status != mStatus_NoError) break; + status = RegisterOurServices(); + if (status != mStatus_NoError) break; + } + } + } + else + { + // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work + mDNSPosixProcessFDSet(&mDNSStorage, &readfds); + + // 6. This example client has no other work it needs to be doing, + // but a real client would do its work here + // ... (do work) ... + } + } + + debugf("Exiting"); + + DeregisterOurServices(); + mDNS_Close(&mDNSStorage); + + if (status == mStatus_NoError) { + result = 0; + } else { + result = 2; + } + if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) { + fprintf(stderr, "%s: Finished with status %ld, result %d\n", gProgramName, status, result); + } + + return result; +} diff --git a/mDNSPosix/Services.txt b/mDNSPosix/Services.txt new file mode 100755 index 0000000..a7b73da --- /dev/null +++ b/mDNSPosix/Services.txt @@ -0,0 +1,14 @@ +Tweedlebug +_afpovertcp._tcp. +name=val1 +548 + +Tweedlebug2 +_afpovertcp._tcp. local. +name=val2name2=anotherattribute +548 + +Tweedlebug3 +_afpovertcp._tcp. apple.com. +name=val3 +548 diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c new file mode 100755 index 0000000..345b2f4 --- /dev/null +++ b/mDNSPosix/mDNSPosix.c @@ -0,0 +1,999 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * Formatting notes: + * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion + * on C indentation can be found on the web, such as , + * but for the sake of brevity here I will say just this: Curly braces are not syntactially + * part of an "if" statement; they are the beginning and ending markers of a compound statement; + * therefore common sense dictates that if they are part of a compound statement then they + * should be indented to the same level as everything else in that compound statement. + * Indenting curly braces at the same level as the "if" implies that curly braces are + * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" + * thinking that variables x and y are both of type "char*" -- and anyone who doesn't + * understand why variable y is not of type "char*" just proves the point that poor code + * layout leads people to unfortunate misunderstandings about how the C language really works.) + + Change History (most recent first): + +$Log: mDNSPosix.c,v $ +Revision 1.24 2003/08/18 23:12:23 cheshire + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.23 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.22 2003/08/06 18:46:15 cheshire +LogMsg() errors are serious -- always report them to stderr, regardless of debugging level + +Revision 1.21 2003/08/06 18:20:51 cheshire +Makefile cleanup + +Revision 1.20 2003/08/05 23:56:26 cheshire +Update code to compile with the new mDNSCoreReceive() function that requires a TTL +(Right now mDNSPosix.c just reports 255 -- we should fix this) + +Revision 1.19 2003/07/19 03:15:16 cheshire +Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, +and add the obvious trivial implementations to each platform support layer + +Revision 1.18 2003/07/14 18:11:54 cheshire +Fix stricter compiler warnings + +Revision 1.17 2003/07/13 01:08:38 cheshire +There's not much point running mDNS over a point-to-point link; exclude those + +Revision 1.16 2003/07/02 21:19:59 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.15 2003/06/18 05:48:41 cheshire +Fix warnings + +Revision 1.14 2003/05/26 03:21:30 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.13 2003/05/26 03:01:28 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.12 2003/05/21 03:49:18 cheshire +Fix warning + +Revision 1.11 2003/05/06 00:00:50 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.10 2003/04/25 01:45:57 cheshire + mDNS_RegisterNoSuchService needs to include a host name + +Revision 1.9 2003/03/20 21:10:31 cheshire +Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris + +Revision 1.8 2003/03/15 04:40:38 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.7 2003/03/13 03:46:21 cheshire +Fixes to make the code build on Linux + +Revision 1.6 2003/03/08 00:35:56 cheshire +Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") + +Revision 1.5 2002/12/23 22:13:31 jgraessl +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.4 2002/09/27 01:47:45 cheshire +Workaround for Linux 2.0 systems that don't have IP_PKTINFO + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 21:25:36 cheshire +mDNS_snprintf() doesn't need to be in a separate file + +Revision 1.1 2002/09/17 06:24:34 cheshire +First checkin +*/ + +#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above +#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mDNSUNP.h" + +// *************************************************************************** +// Structures + +// PosixNetworkInterface is a record extension of the core NetworkInterfaceInfo +// type that supports extra fields needed by the Posix platform. +// +// IMPORTANT: coreIntf must be the first field in the structure because +// we cast between pointers to the two different types regularly. + +typedef struct PosixNetworkInterface PosixNetworkInterface; + +struct PosixNetworkInterface + { + NetworkInterfaceInfo coreIntf; + const char * intfName; + PosixNetworkInterface * aliasIntf; + int index; + int multicastSocket; + int multicastSocketv6; + }; + +// *************************************************************************** +// Globals (for debugging) + +static int num_registered_interfaces = 0; +static int num_pkts_accepted = 0; +static int num_pkts_rejected = 0; + +// *************************************************************************** +// Functions + +int gMDNSPlatformPosixVerboseLevel = 0; + +// Note, this uses mDNS_vsnprintf instead of standard "vsnprintf", because mDNS_vsnprintf knows +// how to print special data types like IP addresses and length-prefixed domain names +mDNSexport void debugf_(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + if (gMDNSPlatformPosixVerboseLevel >= 1) + fprintf(stderr, "%s\n", buffer); + fflush(stderr); + } + +mDNSexport void verbosedebugf_(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + if (gMDNSPlatformPosixVerboseLevel >= 2) + fprintf(stderr, "%s\n", buffer); + fflush(stderr); + } + +mDNSexport void LogMsg(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + fprintf(stderr, "%s\n", buffer); + fflush(stderr); + } + +#define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr) + +static void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr, mDNSIPPort *ipPort) + { + switch (sa->sa_family) + { + case AF_INET: + { + struct sockaddr_in* sin = (struct sockaddr_in*)sa; + ipAddr->type = mDNSAddrType_IPv4; + ipAddr->ip.v4.NotAnInteger = sin->sin_addr.s_addr; + if (ipPort) ipPort->NotAnInteger = sin->sin_port; + break; + } + +#ifdef mDNSIPv6Support + case AF_INET6: + { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; + assert(sin6->sin6_len == sizeof(*sin6)); + ipAddr->type = mDNSAddrType_IPv6; + ipAddr->ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + if (ipPort) ipPort->NotAnInteger = sin6->sin6_port; + break; + } +#endif + + default: + verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family); + ipAddr->type = mDNSAddrType_None; + if (ipPort) ipPort->NotAnInteger = 0; + break; + } + } + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Send and Receive +#endif + +// mDNS core calls this routine when it needs to send a packet. +mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, + mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort) + { + int err; + struct sockaddr_storage to; + PosixNetworkInterface * thisIntf; + + assert(m != NULL); + assert(msg != NULL); + assert(end != NULL); + assert( (((char *) end) - ((char *) msg)) > 0 ); + assert(InterfaceID != 0); // Can't send from zero source address + assert(srcPort.NotAnInteger != 0); // Nor from a zero source port + assert(dstPort.NotAnInteger != 0); // Nor from a zero source port + + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin = (struct sockaddr_in*)&to; +#ifndef NOT_HAVE_SA_LEN + sin->sin_len = sizeof(*sin); +#endif + sin->sin_family = AF_INET; + sin->sin_port = dstPort.NotAnInteger; + sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + } + +#ifdef mDNSIPv6Support + else if (dst->type == mDNSAddrType_IPv6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to; + mDNSPlatformMemZero(sin6, sizeof(*sin6)); + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = dstPort.NotAnInteger; + sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6; + } +#endif + + err = 0; + thisIntf = (PosixNetworkInterface *)(InterfaceID); + if (dst->type == mDNSAddrType_IPv4) + err = sendto(thisIntf->multicastSocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); + +#ifdef mDNSIPv6Support + else if (dst->type == mDNSAddrType_IPv6) + err = sendto(thisIntf->multicastSocketv6, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to)); +#endif + + if (err > 0) err = 0; + else if (err < 0) + verbosedebugf("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d", + errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index); + + return PosixErrorToStatus(err); + } + +// This routine is called when the main loop detects that data is available on a socket. +static void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) + { + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort; + ssize_t packetLen; + DNSMessage packet; + struct my_in_pktinfo packetInfo; + struct sockaddr_storage from; + socklen_t fromLen; + int flags; + mDNSBool reject; + + assert(m != NULL); + assert(intf != NULL); + assert(skt >= 0); + + fromLen = sizeof(from); + flags = 0; + packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo); + + if (packetLen >= 0) + { + SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort); + SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL); + + // If we have broken IP_RECVDSTADDR functionality (so far + // I've only seen this on OpenBSD) then apply a hack to + // convince mDNS Core that this isn't a spoof packet. + // Basically what we do is check to see whether the + // packet arrived as a multicast and, if so, set its + // destAddr to the mDNS address. + // + // I must admit that I could just be doing something + // wrong on OpenBSD and hence triggering this problem + // but I'm at a loss as to how. + // + // If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have + // no way to tell the destination address or interface this packet arrived on, + // so all we can do is just assume it's a multicast + + #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) + if ( (destAddr.NotAnInteger == 0) && (flags & MSG_MCAST) ) + { + destAddr.type == senderAddr.type; + if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup; + else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroupv6; + } + #endif + + // We only accept the packet if the interface on which it came + // in matches the interface associated with this socket. + // We do this match by name or by index, depending on which + // information is available. recvfrom_flags sets the name + // to "" if the name isn't available, or the index to -1 + // if the index is available. This accomodates the various + // different capabilities of our target platforms. + + reject = mDNSfalse; + if ( packetInfo.ipi_ifname[0] != 0 ) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0); + else if ( packetInfo.ipi_ifindex != -1 ) reject = (packetInfo.ipi_ifindex != intf->index); + + if (reject) + { + verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d", + &senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex, + &intf->coreIntf.ip, intf->intfName, intf->index); + packetLen = -1; + num_pkts_rejected++; + if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2) + { + fprintf(stderr, + "*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n", + num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected); + num_pkts_accepted = 0; + num_pkts_rejected = 0; + } + } + else + { + verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d", + &senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index); + num_pkts_accepted++; + } + } + + if (packetLen >= 0 && packetLen < (ssize_t)sizeof(DNSMessageHeader)) + { + debugf("SocketDataReady packet length (%d) too short", packetLen); + packetLen = -1; + } + + if (packetLen >= 0) + mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, + &senderAddr, senderPort, &destAddr, MulticastDNSPort, intf->coreIntf.InterfaceID, 255); + } + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Init and Term +#endif + +// On OS X this gets the text of the field labelled "Computer Name" in the Sharing Prefs Control Panel +// Other platforms can either get the information from the appropriate place, +// or they can alternatively just require all registering services to provide an explicit name +mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) + { + MakeDomainLabelFromLiteralString(namelabel, "Fill in Default Service Name Here"); + } + +// This gets the current hostname, truncating it at the first dot if necessary +mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) + { + int len = 0; + gethostname(&namelabel->c[1], MAX_DOMAIN_LABEL); + while (len < MAX_DOMAIN_LABEL && namelabel->c[len+1] && namelabel->c[len+1] != '.') len++; + namelabel->c[0] = len; + } + +// Searches the interface list looking for the named interface. +// Returns a pointer to if it found, or NULL otherwise. +static PosixNetworkInterface *SearchForInterfaceByName(mDNS *const m, const char *intfName) + { + PosixNetworkInterface *intf; + + assert(m != NULL); + assert(intfName != NULL); + + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ( (intf != NULL) && (strcmp(intf->intfName, intfName) != 0) ) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); + + return intf; + } + +// Frees the specified PosixNetworkInterface structure. The underlying +// interface must have already been deregistered with the mDNS core. +static void FreePosixNetworkInterface(PosixNetworkInterface *intf) + { + assert(intf != NULL); + if (intf->intfName != NULL) free((void *)intf->intfName); + if (intf->multicastSocket != -1) assert(close(intf->multicastSocket) == 0); + if (intf->multicastSocketv6 != -1) assert(close(intf->multicastSocketv6) == 0); + free(intf); + } + +// Grab the first interface, deregister it, free it, and repeat until done. +static void ClearInterfaceList(mDNS *const m) + { + assert(m != NULL); + + while (m->HostInterfaces) + { + PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces); + mDNS_DeregisterInterface(m, &intf->coreIntf); + if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName); + FreePosixNetworkInterface(intf); + } + num_registered_interfaces = 0; + num_pkts_accepted = 0; + num_pkts_rejected = 0; + } + +// Sets up a multicast send/receive socket for the specified +// port on the interface specified by the IP addrelss intfAddr. +static int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr) + { + int err = 0; + static const int kOn = 1; + static const int kIntTwoFiveFive = 255; + static const unsigned char kByteTwoFiveFive = 255; + + (void) interfaceIndex; // Unused + assert(intfAddr != NULL); + assert(sktPtr != NULL); + assert(*sktPtr == -1); + + // Open the socket... + if (intfAddr->sa_family == AF_INET) *sktPtr = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); +#ifdef mDNSIPv6Support + else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); +#endif + else return EINVAL; + + if (*sktPtr < 0) { err = errno; perror("socket"); } + + // ... with a shared UDP port + if (err == 0) + { + #if defined(SO_REUSEPORT) + err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); + #elif defined(SO_REUSEADDR) + err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + #else + #error This platform has no way to avoid address busy errors on multicast. + #endif + if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); } + } + + // We want to receive destination addresses and interface identifiers. + if (intfAddr->sa_family == AF_INET) + { + struct ip_mreq imr; + struct sockaddr_in bindAddr; + if (err == 0) + { + #if defined(IP_PKTINFO) // Linux + err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); } + #elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF) // BSD and Solaris + #if defined(IP_RECVDSTADDR) + err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); } + #endif + #if defined(IP_RECVIF) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); } + } + #endif + #else + #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts + #endif + } + + // Add multicast group membership on this interface + if (err == 0) + { + imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; + imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; + err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); + if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); } + } + + // Specify outgoing interface too + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr)); + if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); } + } + + // Per the mDNS spec, send unicast packets with TTL 255 + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); } + } + + // and multicast packets with TTL 255 too + // There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); + if (err < 0 && errno == EINVAL) + err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); } + } + + // And start listening for packets + if (err == 0) + { + bindAddr.sin_family = AF_INET; + bindAddr.sin_port = port.NotAnInteger; + bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket + err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr)); + if (err < 0) { err = errno; perror("bind"); fflush(stderr); } + } + } // endif (intfAddr->sa_family == AF_INET) + +#ifdef mDNSIPv6Support + else if (intfAddr->sa_family == AF_INET6) + { + struct ipv6_mreq imr6; + struct sockaddr_in6 bindAddr6; + if (err == 0) + { + #if defined(IPV6_PKTINFO) + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_PKTINFO, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); } + #else + #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts + #endif + } + + // Add multicast group membership on this interface + if (err == 0) + { + imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroupv6; + imr6.ipv6mr_interface = interfaceIndex; + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6)); + if (err < 0) + { + err = errno; + verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface); + perror("setsockopt - IPV6_JOIN_GROUP"); + } + } + + // Specify outgoing interface too + if (err == 0) + { + u_int multicast_if = interfaceIndex; + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); } + } + + // We want to receive only IPv6 packets on this socket. + // Without this option, we may get IPv4 addresses as mapped addresses. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); } + } + + // Per the mDNS spec, send unicast packets with TTL 255 + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); } + } + + // and multicast packets with TTL 255 too + // There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both. + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive)); + if (err < 0 && errno == EINVAL) + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); } + } + + // And start listening for packets + if (err == 0) + { + mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6)); + bindAddr6.sin6_len = sizeof(bindAddr6); + bindAddr6.sin6_family = AF_INET6; + bindAddr6.sin6_port = port.NotAnInteger; + bindAddr6.sin6_flowinfo = 0; +// bindAddr6.sin6_addr.s_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket + bindAddr6.sin6_scope_id = 0; + err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6)); + if (err < 0) { err = errno; perror("bind"); fflush(stderr); } + } + } // endif (intfAddr->sa_family == AF_INET6) +#endif + + // Set the socket to non-blocking. + if (err == 0) + { + err = fcntl(*sktPtr, F_GETFL, 0); + if (err < 0) err = errno; + else + { + err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK); + if (err < 0) err = errno; + } + } + + // Clean up + if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; } + assert( (err == 0) == (*sktPtr != -1) ); + return err; + } + +// Creates a PosixNetworkInterface for the interface whose IP address is +// intfAddr and whose name is intfName and registers it with mDNS core. +static int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, const char *intfName) + { + int err = 0; + PosixNetworkInterface *intf; + PosixNetworkInterface *alias = NULL; + + assert(m != NULL); + assert(intfAddr != NULL); + assert(intfName != NULL); + + // Allocate the interface structure itself. + intf = malloc(sizeof(*intf)); + if (intf == NULL) { assert(0); err = ENOMEM; } + + // And make a copy of the intfName. + if (err == 0) + { + intf->intfName = strdup(intfName); + if (intf->intfName == NULL) { assert(0); err = ENOMEM; } + } + + if (err == 0) + { + // Set up the fields required by the mDNS core. + SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL); + intf->coreIntf.Advertise = m->AdvertiseLocalAddresses; + + // Set up the extra fields in PosixNetworkInterface. + assert(intf->intfName != NULL); // intf->intfName already set up above + intf->index = if_nametoindex(intf->intfName); + intf->multicastSocket = -1; + intf->multicastSocketv6 = -1; + alias = SearchForInterfaceByName(m, intf->intfName); + if (alias == NULL) alias = intf; + intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias; + + if (alias != intf) + debugf("SetupOneInterface: %s %#a is an alias of %#a", intfName, &intf->coreIntf.ip, &alias->coreIntf.ip); + } + + // Set up the multicast socket + if (err == 0) + { + if (alias->multicastSocket == -1 && intfAddr->sa_family == AF_INET) + err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket); +#ifdef mDNSIPv6Support + else if (alias->multicastSocketv6 == -1 && intfAddr->sa_family == AF_INET6) + err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocketv6); +#endif + } + + // The interface is all ready to go, let's register it with the mDNS core. + if (err == 0) + err = mDNS_RegisterInterface(m, &intf->coreIntf); + + // Clean up. + if (err == 0) + { + num_registered_interfaces++; + debugf("SetupOneInterface: %s %#a Registered", intf->intfName, &intf->coreIntf.ip); + if (gMDNSPlatformPosixVerboseLevel > 0) + fprintf(stderr, "Registered interface %s\n", intf->intfName); + } + else + { + // Use intfName instead of intf->intfName in the next line to avoid dereferencing NULL. + debugf("SetupOneInterface: %s %#a failed to register %d", intfName, &intf->coreIntf.ip, err); + if (intf) { FreePosixNetworkInterface(intf); intf = NULL; } + } + + assert( (err == 0) == (intf != NULL) ); + + return err; + } + +static int SetupInterfaceList(mDNS *const m) + { + mDNSBool foundav4 = mDNSfalse; + int err = 0; + struct ifi_info *intfList = get_ifi_info(AF_INET, mDNStrue); + struct ifi_info *firstLoopback = NULL; + + assert(m != NULL); + debugf("SetupInterfaceList"); + + if (intfList == NULL) err = ENOENT; + +#ifdef mDNSIPv6Support + if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */ + { + struct ifi_info **p = &intfList; + while (*p) p = &(*p)->ifi_next; + *p = get_ifi_info(AF_INET6, mDNStrue); + } +#endif + + if (err == 0) + { + struct ifi_info *i = intfList; + while (i) + { + if ( ((i->ifi_addr->sa_family == AF_INET) +#ifdef mDNSIPv6Support + || (i->ifi_addr->sa_family == AF_INET6) +#endif + ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT) ) + { + if (i->ifi_flags & IFF_LOOPBACK) + { + if (firstLoopback == NULL) + firstLoopback = i; + } + else + { + if (SetupOneInterface(m, i->ifi_addr, i->ifi_name) == 0) + if (i->ifi_addr->sa_family == AF_INET) + foundav4 = mDNStrue; + } + } + i = i->ifi_next; + } + + // If we found no normal interfaces but we did find a loopback interface, register the + // loopback interface. This allows self-discovery if no interfaces are configured. + // Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. + // In the interim, we skip loopback interface only if we found at least one v4 interface to use + // if ( (m->HostInterfaces == NULL) && (firstLoopback != NULL) ) + if ( !foundav4 && firstLoopback ) + (void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_name); + } + + // Clean up. + if (intfList != NULL) free_ifi_info(intfList); + return err; + } + +// mDNS core calls this routine to initialise the platform-specific data. +mDNSexport mStatus mDNSPlatformInit(mDNS *const m) + { + int err; + assert(m != NULL); + + // Tell mDNS core the names of this machine. + + // Set up the nice label + m->nicelabel.c[0] = 0; + GetUserSpecifiedFriendlyComputerName(&m->nicelabel); + if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); + + // Set up the RFC 1034-compliant label + m->hostlabel.c[0] = 0; + GetUserSpecifiedRFC1034ComputerName(&m->hostlabel); + if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh"); + + mDNS_GenerateFQDN(m); + + // Tell mDNS core about the network interfaces on this machine. + err = SetupInterfaceList(m); + + // We don't do asynchronous initialization on the Posix platform, so by the time + // we get here the setup will already have succeeded or failed. If it succeeded, + // we should just call mDNSCoreInitComplete() immediately. + if (err == 0) + mDNSCoreInitComplete(m, mStatus_NoError); + + return PosixErrorToStatus(err); + } + +// mDNS core calls this routine to clean up the platform-specific data. +// In our case all we need to do is to tear down every network interface. +mDNSexport void mDNSPlatformClose(mDNS *const m) + { + assert(m != NULL); + ClearInterfaceList(m); + } + +extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m) + { + int err; + ClearInterfaceList(m); + err = SetupInterfaceList(m); + return PosixErrorToStatus(err); + } + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Locking +#endif + +// On the Posix platform, locking is a no-op because we only ever enter +// mDNS core on the main thread. + +// mDNS core calls this routine when it wants to prevent +// the platform from reentering mDNS core code. +mDNSexport void mDNSPlatformLock (const mDNS *const m) + { + (void) m; // Unused + } + +// mDNS core calls this routine when it release the lock taken by +// mDNSPlatformLock and allow the platform to reenter mDNS core code. +mDNSexport void mDNSPlatformUnlock (const mDNS *const m) + { + (void) m; // Unused + } + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Strings +#endif + +// mDNS core calls this routine to copy C strings. +// On the Posix platform this maps directly to the ANSI C strcpy. +mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) + { + strcpy((char *)dst, (char *)src); + } + +// mDNS core calls this routine to get the length of a C string. +// On the Posix platform this maps directly to the ANSI C strlen. +mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) + { + return strlen((char*)src); + } + +// mDNS core calls this routine to copy memory. +// On the Posix platform this maps directly to the ANSI C memcpy. +mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, mDNSu32 len) + { + memcpy(dst, src, len); + } + +// mDNS core calls this routine to test whether blocks of memory are byte-for-byte +// identical. On the Posix platform this is a simple wrapper around ANSI C memcmp. +mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, mDNSu32 len) + { + return memcmp(dst, src, len) == 0; + } + +// mDNS core calls this routine to clear blocks of memory. +// On the Posix platform this is a simple wrapper around ANSI C memset. +mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) + { + memset(dst, 0, len); + } + +mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(malloc(len)); } +mDNSexport void mDNSPlatformMemFree (void *mem) { free(mem); } + +mDNSexport mDNSs32 mDNSPlatformOneSecond = 1024; + +mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) + { + // No special setup is required on Posix -- we just use gettimeofday(); + // This is not really safe, because gettimeofday can go backwards if the user manually changes the date or time + // We should find a better way to do this + *timenow = mDNSPlatformTimeNow(); + return(mStatus_NoError); + } + +mDNSexport mDNSs32 mDNSPlatformTimeNow() + { + struct timeval tv; + gettimeofday(&tv, NULL); + // tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time) + // tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999) + // We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result + // and we multiply tv.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits. + // This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second) + // and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days). + return( (tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625) ); + } + +mDNSexport void mDNSPosixGetFDSet(mDNS *const m, int *nfds, fd_set *readfds, struct timeval *timeout) + { + mDNSs32 ticks; + struct timeval interval; + + // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do + mDNSs32 nextevent = mDNS_Execute(m); + + // 2. Build our list of active file descriptors + PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces); + while (info) + { + if (info->multicastSocket != -1) + { + if (*nfds < info->multicastSocket + 1) + *nfds = info->multicastSocket + 1; + FD_SET(info->multicastSocket, readfds); + } + if (info->multicastSocketv6 != -1) + { + if (*nfds < info->multicastSocketv6 + 1) + *nfds = info->multicastSocketv6 + 1; + FD_SET(info->multicastSocketv6, readfds); + } + info = (PosixNetworkInterface *)(info->coreIntf.next); + } + + // 3. Calculate the time remaining to the next scheduled event (in struct timeval format) + ticks = nextevent - mDNSPlatformTimeNow(); + if (ticks < 1) ticks = 1; + interval.tv_sec = ticks >> 10; // The high 22 bits are seconds + interval.tv_usec = ((ticks & 0x3FF) * 15625) / 16; // The low 10 bits are 1024ths + + // 4. If client's proposed timeout is more than what we want, then reduce it + if (timeout->tv_sec > interval.tv_sec || + (timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec)) + *timeout = interval; + } + +mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds) + { + PosixNetworkInterface *info; + assert(m != NULL); + assert(readfds != NULL); + info = (PosixNetworkInterface *)(m->HostInterfaces); + while (info) + { + if (info->multicastSocket != -1 && FD_ISSET(info->multicastSocket, readfds)) + { + FD_CLR(info->multicastSocket, readfds); + SocketDataReady(m, info, info->multicastSocket); + } + if (info->multicastSocketv6 != -1 && FD_ISSET(info->multicastSocketv6, readfds)) + { + FD_CLR(info->multicastSocketv6, readfds); + SocketDataReady(m, info, info->multicastSocketv6); + } + info = (PosixNetworkInterface *)(info->coreIntf.next); + } + } diff --git a/mDNSPosix/mDNSPosix.h b/mDNSPosix/mDNSPosix.h new file mode 100755 index 0000000..53f7fe9 --- /dev/null +++ b/mDNSPosix/mDNSPosix.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSPosix.h,v $ +Revision 1.8 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.7 2003/07/02 21:19:59 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.6 2003/03/13 03:46:21 cheshire +Fixes to make the code build on Linux + +Revision 1.5 2003/03/08 00:35:56 cheshire +Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") + +Revision 1.4 2002/12/23 22:13:31 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 04:20:44 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.1 2002/09/17 06:24:34 cheshire +First checkin + +*/ + +#ifndef __mDNSPlatformPosix_h +#define __mDNSPlatformPosix_h + +#include + +#if HAVE_IPV6 +#define mDNSIPv6Support 1 +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +// This is a global because debugf_() needs to be able to check its value +extern int gMDNSPlatformPosixVerboseLevel; + +struct mDNS_PlatformSupport_struct + { + // No additional data required for Posix at this time + }; + +extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m); + // See comment in implementation. + +// Call mDNSPosixGetFDSet before calling select(), to update the parameters +// as may be necessary to meet the needs of the mDNSCore code. +// The timeout pointer MUST NOT be NULL. +// Set timeout->tv_sec to 0x3FFFFFFF if you want to have effectively no timeout +// After calling mDNSPosixGetFDSet(), call select(nfds, &readfds, NULL, NULL, &timeout); as usual +// After select() returns, call mDNSPosixProcessFDSet() to let mDNSCore do its work +extern void mDNSPosixGetFDSet(mDNS *const m, int *nfds, fd_set *readfds, struct timeval *timeout); +extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mDNSPosix/mDNSUNP.c b/mDNSPosix/mDNSUNP.c new file mode 100755 index 0000000..fa9bda0 --- /dev/null +++ b/mDNSPosix/mDNSUNP.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSUNP.c,v $ +Revision 1.12 2003/09/02 20:47:13 cheshire +Fix signed/unsigned warning + +Revision 1.11 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.10 2003/08/06 18:20:51 cheshire +Makefile cleanup + +Revision 1.9 2003/07/14 18:11:54 cheshire +Fix stricter compiler warnings + +Revision 1.8 2003/07/02 21:19:59 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.7 2003/03/20 21:10:31 cheshire +Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris + +Revision 1.6 2003/03/13 03:46:21 cheshire +Fixes to make the code build on Linux + +Revision 1.5 2003/02/07 03:02:02 cheshire +Submitted by: Mitsutaka Watanabe +The code saying "index += 1;" was effectively making up random interface index values. +The right way to find the correct interface index is if_nametoindex(); + +Revision 1.4 2002/12/23 22:13:31 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 04:20:44 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.1 2002/09/17 06:24:34 cheshire +First checkin + +*/ + +#include "mDNSUNP.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Solaris defined SIOCGIFCONF etc in but + other platforms don't even have that include file. So, + if we haven't yet got a definition, let's try to find + . +*/ + +#ifndef SIOCGIFCONF + #include +#endif + +/* sockaddr_dl is only referenced if we're using IP_RECVIF, + so only include the header in that case. +*/ + +#ifdef IP_RECVIF + #include +#endif + + +struct ifi_info *get_ifi_info(int family, int doaliases) +{ + int junk; + struct ifi_info *ifi, *ifihead, **ifipnext; + int sockfd, len, lastlen, flags, myflags; + char *ptr, *buf, lastname[IFNAMSIZ], *cptr; + struct ifconf ifc; + struct ifreq *ifr, ifrcopy; + struct sockaddr_in *sinptr; + +#if defined(AF_INET6) && defined(HAVE_IPV6) + struct sockaddr_in6 *sinptr6; +#endif + + sockfd = -1; + buf = NULL; + ifihead = NULL; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + goto gotError; + } + + lastlen = 0; + len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ + for ( ; ; ) { + buf = malloc(len); + if (buf == NULL) { + goto gotError; + } + ifc.ifc_len = len; + ifc.ifc_buf = buf; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { + if (errno != EINVAL || lastlen != 0) { + goto gotError; + } + } else { + if (ifc.ifc_len == lastlen) + break; /* success, len has not changed */ + lastlen = ifc.ifc_len; + } + len += 10 * sizeof(struct ifreq); /* increment */ + free(buf); + } + ifihead = NULL; + ifipnext = &ifihead; + lastname[0] = 0; +/* end get_ifi_info1 */ + +/* include get_ifi_info2 */ + for (ptr = buf; ptr < buf + ifc.ifc_len; ) { + ifr = (struct ifreq *) ptr; + + len = GET_SA_LEN(ifr->ifr_addr); + ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ + +// fprintf(stderr, "intf %d name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family); + + if (ifr->ifr_addr.sa_family != family) + continue; /* ignore if not desired address family */ + + myflags = 0; + if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) + *cptr = 0; /* replace colon will null */ + if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { + if (doaliases == 0) + continue; /* already processed this interface */ + myflags = IFI_ALIAS; + } + memcpy(lastname, ifr->ifr_name, IFNAMSIZ); + + ifrcopy = *ifr; + if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) { + goto gotError; + } + + flags = ifrcopy.ifr_flags; + if ((flags & IFF_UP) == 0) + continue; /* ignore if interface not up */ + + ifi = calloc(1, sizeof(struct ifi_info)); + if (ifi == NULL) { + goto gotError; + } + *ifipnext = ifi; /* prev points to this new one */ + ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ + + ifi->ifi_flags = flags; /* IFF_xxx values */ + ifi->ifi_myflags = myflags; /* IFI_xxx values */ + ifi->ifi_index = if_nametoindex(ifr->ifr_name); + memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME); + ifi->ifi_name[IFI_NAME-1] = '\0'; +/* end get_ifi_info2 */ +/* include get_ifi_info3 */ + switch (ifr->ifr_addr.sa_family) { + case AF_INET: + sinptr = (struct sockaddr_in *) &ifr->ifr_addr; + if (ifi->ifi_addr == NULL) { + ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); + +#ifdef SIOCGIFBRDADDR + if (flags & IFF_BROADCAST) { + if (ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy) < 0) { + goto gotError; + } + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; + ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in)); + if (ifi->ifi_brdaddr == NULL) { + goto gotError; + } + memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + +#ifdef SIOCGIFDSTADDR + if (flags & IFF_POINTOPOINT) { + if (ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy) < 0) { + goto gotError; + } + sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; + ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in)); + if (ifi->ifi_dstaddr == NULL) { + goto gotError; + } + memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in)); + } +#endif + } + break; + +#if defined(AF_INET6) && defined(HAVE_IPV6) + case AF_INET6: + sinptr6 = (struct sockaddr_in6 *) &ifr->ifr_addr; + if (ifi->ifi_addr == NULL) { + ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6)); + if (ifi->ifi_addr == NULL) { + goto gotError; + } + + /* Some platforms (*BSD) inject the prefix in IPv6LL addresses */ + /* We need to strip that out */ + if (IN6_IS_ADDR_LINKLOCAL(&sinptr6->sin6_addr)) + sinptr6->sin6_addr.__u6_addr.__u6_addr16[1] = 0; + memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6)); + } + break; +#endif + + default: + break; + } + } + goto done; + +gotError: + if (ifihead != NULL) { + free_ifi_info(ifihead); + ifihead = NULL; + } + +done: + if (buf != NULL) { + free(buf); + } + if (sockfd != -1) { + junk = close(sockfd); + assert(junk == 0); + } + return(ifihead); /* pointer to first structure in linked list */ +} +/* end get_ifi_info3 */ + +/* include free_ifi_info */ +void +free_ifi_info(struct ifi_info *ifihead) +{ + struct ifi_info *ifi, *ifinext; + + for (ifi = ifihead; ifi != NULL; ifi = ifinext) { + if (ifi->ifi_addr != NULL) + free(ifi->ifi_addr); + if (ifi->ifi_brdaddr != NULL) + free(ifi->ifi_brdaddr); + if (ifi->ifi_dstaddr != NULL) + free(ifi->ifi_dstaddr); + ifinext = ifi->ifi_next; /* can't fetch ifi_next after free() */ + free(ifi); /* the ifi_info{} itself */ + } +} +/* end free_ifi_info */ + +ssize_t +recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, + struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t n; + +#ifdef CMSG_FIRSTHDR + struct cmsghdr *cmptr; + union { + struct cmsghdr cm; + char control[1024]; + } control_un; + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + msg.msg_flags = 0; +#else + memset(&msg, 0, sizeof(msg)); /* make certain msg_accrightslen = 0 */ +#endif /* CMSG_FIRSTHDR */ + + msg.msg_name = (void *) sa; + msg.msg_namelen = *salenptr; + iov[0].iov_base = ptr; + iov[0].iov_len = nbytes; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if ( (n = recvmsg(fd, &msg, *flagsp)) < 0) + return(n); + + *salenptr = msg.msg_namelen; /* pass back results */ + if (pktp) { + /* 0.0.0.0, i/f = -1 */ + /* We set the interface to -1 so that the caller can + tell whether we returned a meaningful value or + just some default. Previously this code just + set the value to 0, but I'm concerned that 0 + might be a valid interface value. + */ + memset(pktp, 0, sizeof(struct my_in_pktinfo)); + pktp->ipi_ifindex = -1; + } +/* end recvfrom_flags1 */ + +/* include recvfrom_flags2 */ +#ifndef CMSG_FIRSTHDR + #warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc. + *flagsp = 0; /* pass back results */ + return(n); +#else + + *flagsp = msg.msg_flags; /* pass back results */ + if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) || + (msg.msg_flags & MSG_CTRUNC) || pktp == NULL) + return(n); + + for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; + cmptr = CMSG_NXTHDR(&msg, cmptr)) { + +#ifdef IP_PKTINFO +#if in_pktinfo_definition_is_missing +struct in_pktinfo +{ + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *tmp; + struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr; + + tmp = (struct in_pktinfo *) CMSG_DATA(cmptr); + sin->sin_family = AF_INET; + sin->sin_addr = tmp->ipi_addr; + sin->sin_port = 0; + pktp->ipi_ifindex = tmp->ipi_ifindex; + continue; + } +#endif + +#ifdef IP_RECVDSTADDR + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVDSTADDR) { + struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr; + + sin->sin_family = AF_INET; + sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr); + sin->sin_port = 0; + continue; + } +#endif + +#ifdef IP_RECVIF + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVIF) { + struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr); + int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1); + pktp->ipi_ifindex = sdl->sdl_index; +#ifndef HAVE_BROKEN_RECVIF_NAME + strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen); +#endif + assert(pktp->ipi_ifname[IFI_NAME - 1] == 0); + // null terminated because of memset above + continue; + } +#endif + +#if defined(IPV6_PKTINFO) && defined(HAVE_IPV6) + if (cmptr->cmsg_level == IPPROTO_IPV6 && + cmptr->cmsg_type == IPV6_PKTINFO) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr; + struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr); + + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_addr = ip6_info->ipi6_addr; + sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + sin6->sin6_port = 0; + pktp->ipi_ifindex = ip6_info->ipi6_ifindex; + continue; + } +#endif + assert(0); // unknown ancillary data + } + return(n); +#endif /* CMSG_FIRSTHDR */ +} diff --git a/mDNSPosix/mDNSUNP.h b/mDNSPosix/mDNSUNP.h new file mode 100755 index 0000000..21d13f1 --- /dev/null +++ b/mDNSPosix/mDNSUNP.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSUNP.h,v $ +Revision 1.8 2003/08/12 19:56:26 cheshire +Update to APSL 2.0 + +Revision 1.7 2003/08/06 18:20:51 cheshire +Makefile cleanup + +Revision 1.6 2003/07/02 21:19:59 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.5 2003/03/13 03:46:21 cheshire +Fixes to make the code build on Linux + +Revision 1.4 2002/12/23 22:13:32 jgraessl + +Reviewed by: Stuart Cheshire +Initial IPv6 support for mDNSResponder. + +Revision 1.3 2002/09/21 20:44:53 zarzycki +Added APSL info + +Revision 1.2 2002/09/19 04:20:44 cheshire +Remove high-ascii characters that confuse some systems + +Revision 1.1 2002/09/17 06:24:35 cheshire +First checkin + +*/ + +#ifndef __mDNSUNP_h +#define __mDNSUNP_h + +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef NOT_HAVE_SOCKLEN_T + typedef unsigned int socklen_t; +#endif + +#if !defined(_SS_MAXSIZE) + #define sockaddr_storage sockaddr +#endif + +#ifndef NOT_HAVE_SA_LEN +#define GET_SA_LEN(X) (sizeof(struct sockaddr) > ((struct sockaddr*)&(X))->sa_len ? \ + sizeof(struct sockaddr) : ((struct sockaddr*)&(X))->sa_len ) +#elif mDNSIPv6Support +#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : \ + ((struct sockaddr*)&(X))->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr)) +#else +#define GET_SA_LEN(X) ((X).sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr)) +#endif + +#define IFI_NAME 16 /* same as IFNAMSIZ in */ +#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */ + +// Renamed from my_in_pktinfo because in_pktinfo is used by Linux. + +struct my_in_pktinfo { + struct sockaddr_storage ipi_addr; + int ipi_ifindex; /* received interface index */ + char ipi_ifname[IFI_NAME]; /* received interface name */ +}; + +extern ssize_t recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, + struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp); + +struct ifi_info { + char ifi_name[IFI_NAME]; /* interface name, null terminated */ + u_char ifi_haddr[IFI_HADDR]; /* hardware address */ + u_short ifi_hlen; /* #bytes in hardware address: 0, 6, 8 */ + short ifi_flags; /* IFF_xxx constants from */ + short ifi_myflags; /* our own IFI_xxx flags */ + int ifi_index; /* interface index */ + struct sockaddr *ifi_addr; /* primary address */ + struct sockaddr *ifi_brdaddr;/* broadcast address */ + struct sockaddr *ifi_dstaddr;/* destination address */ + struct ifi_info *ifi_next; /* next of these structures */ +}; + +#define IFI_ALIAS 1 /* ifi_addr is an alias */ + +extern struct ifi_info *get_ifi_info(int family, int doaliases); +extern void free_ifi_info(struct ifi_info *); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mDNSVxWorks/README.txt b/mDNSVxWorks/README.txt new file mode 100644 index 0000000..f0ea01e --- /dev/null +++ b/mDNSVxWorks/README.txt @@ -0,0 +1,8 @@ +These are the platform support files for running mDNSCore on VxWorks. + +Please note, most of the developers working on the mDNSResponder code do +not have access to a VxWorks development environment, so they are not able +to personally verify that the VxWorks compiles and runs successfully after +every single change to the mDNSCore code. We do try to take care not to +make careless changes that would break the VxWorks build, but if you do +find that something is broken, let us know and we'll fix it. diff --git a/mDNSVxWorks/mDNSVxWorks.c b/mDNSVxWorks/mDNSVxWorks.c new file mode 100644 index 0000000..b482fb8 --- /dev/null +++ b/mDNSVxWorks/mDNSVxWorks.c @@ -0,0 +1,2097 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Contains: mDNS platform plugin for VxWorks. + + Copyright: Copyright (C) 2002-2003 Apple Computer, Inc., All Rights Reserved. + + Change History (most recent first): + +$Log: mDNSVxWorks.c,v $ +Revision 1.7 2003/08/20 05:58:54 bradley +Removed dependence on modified mDNSCore: define structures/prototypes locally. + +Revision 1.6 2003/08/18 23:19:05 cheshire + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.5 2003/08/15 00:05:04 bradley +Updated to use name/InterfaceID from new AuthRecord resrec field. Added output of new record sizes. + +Revision 1.4 2003/08/14 02:19:55 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.3 2003/08/12 19:56:27 cheshire +Update to APSL 2.0 + +Revision 1.2 2003/08/05 23:58:34 cheshire +Update code to compile with the new mDNSCoreReceive() function that requires a TTL +Right now this platform layer just reports 255 instead of returning the real value -- we should fix this + +Revision 1.1 2003/08/02 10:06:48 bradley +mDNS platform plugin for VxWorks. + + + Notes for non-Apple platforms: + + TARGET_NON_APPLE should be defined to 1 to avoid relying on Apple-only header files, macros, or functions. + + To Do: + + - Add support for IPv6 (needs VxWorks IPv6 support). +*/ + +// Set up the debug library to use the default category (see DebugServicesLite.h for details). + +#if( !TARGET_NON_APPLE ) + #define DEBUG_USE_DEFAULT_CATEGORY 1 +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vxWorks.h" +#include "ifLib.h" +#include "inetLib.h" +#include "pipeDrv.h" +#include "selectLib.h" +#include "semLib.h" +#include "sockLib.h" +#include "sysLib.h" +#include "taskLib.h" +#include "tickLib.h" + +#include "config.h" + +#if( !TARGET_NON_APPLE ) + #include "ACP/ACPUtilities.h" + #include "Support/DebugServicesLite.h" + #include "Support/MiscUtilities.h" +#endif + +#include "mDNSClientAPI.h" +#include "mDNSPlatformFunctions.h" + +#include "mDNSVxWorks.h" + +#if 0 +#pragma mark == Preprocessor == +#endif + +//=========================================================================================================================== +// Preprocessor +//=========================================================================================================================== + +#if( !TARGET_NON_APPLE ) + debug_log_new_default_category( mdns ); +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[mDNS] " + +#define kMDNSDefaultName "My-Device" + +#define kMDNSTaskName "tMDNS" +#define kMDNSTaskPriority 102 +#define kMDNSTaskStackSize 49152 + +#define kMDNSPipeName "/pipe/mDNS" +#define kMDNSPipeMessageQueueSize 32 +#define kMDNSPipeMessageSize 1 + +#define kInvalidSocketRef -1 + +typedef uint8_t MDNSPipeCommandCode; +enum +{ + kMDNSPipeCommandCodeInvalid = 0, + kMDNSPipeCommandCodeReschedule = 1, + kMDNSPipeCommandCodeReconfigure = 2, + kMDNSPipeCommandCodeQuit = 3 +}; + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +typedef int MDNSSocketRef; + +struct MDNSInterfaceItem +{ + MDNSInterfaceItem * next; + char name[ 32 ]; + MDNSSocketRef multicastSocketRef; + MDNSSocketRef unicastSocketRef; + MDNSSocketRef sendingSocketRef; + NetworkInterfaceInfo hostSet; + mDNSBool hostRegistered; + + int sendMulticastCounter; + int sendUnicastCounter; + int sendErrorCounter; + + int recvMulticastCounter; + int recvUnicastCounter; + int recvErrorCounter; + int recvLoopCounter; +}; + +#if 0 +#pragma mark == Macros == +#endif + +//=========================================================================================================================== +// Macros +//=========================================================================================================================== + +#if( TARGET_NON_APPLE ) + + // Do-nothing versions of the debugging macros for non-Apple platforms. + + #define check(assertion) + #define check_string( assertion, cstring ) + #define check_noerr(err) + #define check_noerr_string( error, cstring ) + #define check_errno( assertion, errno_value ) + #define debug_string( cstring ) + #define require( assertion, label ) do { if( !(assertion) ) goto label; } while(0) + #define require_string( assertion, label, string ) require(assertion, label) + #define require_quiet( assertion, label ) require( assertion, label ) + #define require_noerr( error, label ) do { if( (error) != 0 ) goto label; } while(0) + #define require_noerr_quiet( assertion, label ) require_noerr( assertion, label ) + #define require_noerr_action( error, label, action ) do { if( (error) != 0 ) { {action;}; goto label; } } while(0) + #define require_noerr_action_quiet( assertion, label, action ) require_noerr_action( assertion, label, action ) + #define require_action( assertion, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) + #define require_action_quiet( assertion, label, action ) require_action( assertion, label, action ) + #define require_action_string( assertion, label, action, cstring ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) + #define require_errno( assertion, errno_value, label ) do { if( !(assertion) ) goto label; } while(0) + #define require_errno_action( assertion, errno_value, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) + + #define dlog( ARGS... ) + + #define DEBUG_UNUSED( X ) (void)( X ) +#endif + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// ifIndexToIfp is in net/if.c, but not exported by net/if.h so provide it here. + +extern struct ifnet * ifIndexToIfp(int ifIndex); + +// Platform Internals + +mDNSlocal void SetupNames( mDNS * const inMDNS ); +mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ); +mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ); +mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inAddr, MDNSInterfaceItem **outItem ); +mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, MDNSInterfaceItem *inItem ); +mDNSlocal mStatus + SetupSocket( + mDNS * const inMDNS, + const struct ifaddrs * inAddr, + mDNSIPPort inPort, + MDNSSocketRef * outSocketRef ); + +// Commands + +mDNSlocal mStatus SetupCommandPipe( mDNS * const inMDNS ); +mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS ); +mDNSlocal mStatus SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode ); +mDNSlocal mStatus ProcessCommand( mDNS * const inMDNS ); +mDNSlocal void ProcessCommandReconfigure( mDNS *inMDNS ); + +// Threads + +mDNSlocal mStatus SetupTask( mDNS * const inMDNS ); +mDNSlocal mStatus TearDownTask( mDNS * const inMDNS ); +mDNSlocal void Task( mDNS *inMDNS ); +mDNSlocal mStatus TaskInit( mDNS *inMDNS ); +mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSocket ); +mDNSlocal void TaskSetupTimeout( mDNSs32 inNextTaskTime, struct timeval *outTimeout ); +mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSSocketRef inSocketRef ); + +// Utilities + +#if( TARGET_NON_APPLE ) + mDNSlocal void GenerateUniqueHostName( char *outName, long *ioSeed ); + mDNSlocal void GenerateUniqueDNSName( char *outName, long *ioSeed ); +#endif + +// Platform Accessors + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo; +struct mDNSPlatformInterfaceInfo +{ + const char * name; + mDNSAddr ip; +}; + +mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ); +mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ); + +#ifdef __cplusplus + } +#endif + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +mDNSlocal mDNS * gMDNSPtr = NULL; +mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport; +mDNSlocal mDNSs32 gMDNSTicksToMicrosecondsMultiplier = 0; + +// Platform support + +mDNSs32 mDNSPlatformOneSecond; + +#if 0 +#pragma mark - +#pragma mark == Public APIs == +#endif + +//=========================================================================================================================== +// mDNSReconfigure +//=========================================================================================================================== + +void mDNSReconfigure( void ) +{ + // Send a "reconfigure" command to the MDNS task. + + if( gMDNSPtr ) + { + SendCommand( gMDNSPtr, kMDNSPipeCommandCodeReconfigure ); + } +} + +#if 0 +#pragma mark - +#pragma mark == Platform Support == +#endif + +//=========================================================================================================================== +// mDNSPlatformInit +//=========================================================================================================================== + +mStatus mDNSPlatformInit( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelInfo, DEBUG_NAME "platform init\n" ); + + // Initialize variables. + + memset( &gMDNSPlatformSupport, 0, sizeof( gMDNSPlatformSupport ) ); + inMDNS->p = &gMDNSPlatformSupport; + inMDNS->p->commandPipe = ERROR; + inMDNS->p->task = ERROR; + inMDNS->p->rescheduled = 1; // Default to rescheduled until fully initialized. + mDNSPlatformOneSecond = sysClkRateGet(); + gMDNSTicksToMicrosecondsMultiplier = ( 1000000L / mDNSPlatformOneSecond ); + + // Allocate semaphores. + + inMDNS->p->lockID = semMCreate( SEM_Q_FIFO ); + require_action( inMDNS->p->lockID, exit, err = mStatus_NoMemoryErr ); + + inMDNS->p->readyEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY ); + require_action( inMDNS->p->readyEvent, exit, err = mStatus_NoMemoryErr ); + + inMDNS->p->quitEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY ); + require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr ); + + gMDNSPtr = inMDNS; + + // Set up the task and wait for it to initialize. Initialization is done from the task instead of here to avoid + // stack space issues. Some of the initialization may require a larger stack than the current task supports. + + err = SetupTask( inMDNS ); + require_noerr( err, exit ); + + err = semTake( inMDNS->p->readyEvent, WAIT_FOREVER ); + require_noerr( err, exit ); + err = inMDNS->p->taskInitErr; + require_noerr( err, exit ); + + mDNSCoreInitComplete( inMDNS, err ); + +exit: + if( err ) + { + mDNSPlatformClose( inMDNS ); + } + dlog( kDebugLevelInfo, DEBUG_NAME "platform init done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformClose +//=========================================================================================================================== + +void mDNSPlatformClose( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelInfo, DEBUG_NAME "platform close\n" ); + check( inMDNS ); + + // Tear everything down. + + err = TearDownTask( inMDNS ); + check_noerr( err ); + + err = TearDownInterfaceList( inMDNS ); + check_noerr( err ); + + err = TearDownCommandPipe( inMDNS ); + check_noerr( err ); + + gMDNSPtr = NULL; + + // Release semaphores. + + if( inMDNS->p->quitEvent ) + { + semDelete( inMDNS->p->quitEvent ); + inMDNS->p->quitEvent = 0; + } + if( inMDNS->p->readyEvent ) + { + semDelete( inMDNS->p->readyEvent ); + inMDNS->p->readyEvent = 0; + } + if( inMDNS->p->lockID ) + { + semDelete( inMDNS->p->lockID ); + inMDNS->p->lockID = 0; + } + + dlog( kDebugLevelInfo, DEBUG_NAME "platform close done\n" ); +} + +//=========================================================================================================================== +// mDNSPlatformSendUDP +//=========================================================================================================================== + +mStatus + mDNSPlatformSendUDP( + const mDNS * const inMDNS, + const DNSMessage * const inMsg, + const mDNSu8 * const inMsgEnd, + mDNSInterfaceID inInterfaceID, + mDNSIPPort inSrcPort, + const mDNSAddr * inDstIP, + mDNSIPPort inDstPort ) +{ + mStatus err; + MDNSInterfaceItem * item; + struct sockaddr_in addr; + int n; + + DEBUG_UNUSED( inSrcPort ); + + dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP\n" ); + + // Check parameters. + + check( inMDNS ); + check( inMsg ); + check( inMsgEnd ); + check( inInterfaceID ); + check( inDstIP ); + if( inDstIP->type != mDNSAddrType_IPv4 ) + { + err = mStatus_BadParamErr; + goto exit; + } + +#if( DEBUG ) + // Make sure the InterfaceID is valid. + + for( item = inMDNS->p->interfaceList; item; item = item->next ) + { + if( item == (MDNSInterfaceItem *) inInterfaceID ) + { + break; + } + } + require_action( item, exit, err = mStatus_NoSuchNameErr ); +#endif + + // Send the packet. + + item = (MDNSInterfaceItem *) inInterfaceID; + check( item->sendingSocketRef != kInvalidSocketRef ); + + memset( &addr, 0, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = inDstPort.NotAnInteger; + addr.sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger; + + n = inMsgEnd - ( (const mDNSu8 * const) inMsg ); + n = sendto( item->sendingSocketRef, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) ); + check_errno( n, errno ); + + item->sendErrorCounter += ( n < 0 ); + item->sendMulticastCounter += ( inDstPort.NotAnInteger == MulticastDNSPort.NotAnInteger ); + item->sendUnicastCounter += ( inDstPort.NotAnInteger != MulticastDNSPort.NotAnInteger ); + + dlog( kDebugLevelChatty, DEBUG_NAME "sent (to=%u.%u.%u.%u:%hu)\n", + inDstIP->ip.v4.b[ 0 ], inDstIP->ip.v4.b[ 1 ], inDstIP->ip.v4.b[ 2 ], inDstIP->ip.v4.b[ 3 ], + htons( inDstPort.NotAnInteger ) ); + err = mStatus_NoError; + +exit: + dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP done\n" ); + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformLock +//=========================================================================================================================== + +void mDNSPlatformLock( const mDNS * const inMDNS ) +{ + check( inMDNS->p->lockID ); + + if( inMDNS->p->lockID ) + { + #if( TARGET_NON_APPLE ) + semTake( inMDNS->p->lockID, WAIT_FOREVER ); + #else + semTakeDeadlockDetect( inMDNS->p->lockID, WAIT_FOREVER ); + #endif + } +} + +//=========================================================================================================================== +// mDNSPlatformUnlock +//=========================================================================================================================== + +void mDNSPlatformUnlock( const mDNS * const inMDNS ) +{ + check( inMDNS ); + check( inMDNS->p ); + check( inMDNS->p->lockID ); + check_string( inMDNS->p->task != ERROR, "mDNS task not started" ); + + // When an API routine is called, "m->NextScheduledEvent" is reset to "timenow" before calling mDNSPlatformUnlock() + // Since our main mDNS_Execute() loop is on a different thread, we need to wake up that thread to: + // (a) handle immediate work (if any) resulting from this API call + // (b) calculate the next sleep time between now and the next interesting event + + if( ( mDNSPlatformTimeNow() - inMDNS->NextScheduledEvent ) >= 0 ) + { + // We only need to send the reschedule event when called from a task other than the mDNS task since if we are + // called from mDNS task, we'll loop back and call mDNS_Execute. This avoids filling up the command queue. + + if( ( inMDNS->p->rescheduled++ == 0 ) && ( taskIdSelf() != inMDNS->p->task ) ) + { + SendCommand( inMDNS, kMDNSPipeCommandCodeReschedule ); + } + } + + if( inMDNS->p->lockID ) + { + semGive( inMDNS->p->lockID ); + } +} + +//=========================================================================================================================== +// mDNSPlatformStrLen +//=========================================================================================================================== + +mDNSu32 mDNSPlatformStrLen( const void *inSrc ) +{ + check( inSrc ); + + return( (mDNSu32) strlen( (const char *) inSrc ) ); +} + +//=========================================================================================================================== +// mDNSPlatformStrCopy +//=========================================================================================================================== + +void mDNSPlatformStrCopy( const void *inSrc, void *inDst ) +{ + check( inSrc ); + check( inDst ); + + strcpy( (char *) inDst, (const char*) inSrc ); +} + +//=========================================================================================================================== +// mDNSPlatformMemCopy +//=========================================================================================================================== + +void mDNSPlatformMemCopy( const void *inSrc, void *inDst, mDNSu32 inSize ) +{ + check( inSrc ); + check( inDst ); + + memcpy( inDst, inSrc, inSize ); +} + +//=========================================================================================================================== +// mDNSPlatformMemSame +//=========================================================================================================================== + +mDNSBool mDNSPlatformMemSame( const void *inSrc, const void *inDst, mDNSu32 inSize ) +{ + check( inSrc ); + check( inDst ); + + return( memcmp( inSrc, inDst, inSize ) == 0 ); +} + +//=========================================================================================================================== +// mDNSPlatformMemZero +//=========================================================================================================================== + +void mDNSPlatformMemZero( void *inDst, mDNSu32 inSize ) +{ + check( inDst ); + + memset( inDst, 0, inSize ); +} + +//=========================================================================================================================== +// mDNSPlatformMemAllocate +//=========================================================================================================================== + +mDNSexport void * mDNSPlatformMemAllocate( mDNSu32 inSize ) +{ + void * mem; + + check( inSize > 0 ); + + mem = malloc( inSize ); + check( mem ); + + return( mem ); +} + +//=========================================================================================================================== +// mDNSPlatformMemFree +//=========================================================================================================================== + +mDNSexport void mDNSPlatformMemFree( void *inMem ) +{ + check( inMem ); + + free( inMem ); +} + +//=========================================================================================================================== +// mDNSPlatformTimeInit +//=========================================================================================================================== + +mDNSexport mStatus mDNSPlatformTimeInit( mDNSs32 *outTimeNow ) +{ + check( outTimeNow ); + + // No special setup is required on VxWorks -- we just use tickGet(). + + *outTimeNow = mDNSPlatformTimeNow(); + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// mDNSPlatformTimeNow +//=========================================================================================================================== + +mDNSs32 mDNSPlatformTimeNow( void ) +{ + return( (mDNSs32) tickGet() ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceNameToID +//=========================================================================================================================== + +mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ) +{ + mStatus err; + MDNSInterfaceItem * ifd; + + check( inMDNS ); + check( inMDNS->p ); + check( inName ); + + // Search for an interface with the specified name, + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( strcmp( ifd->name, inName ) == 0 ) + { + break; + } + } + if( !ifd ) + { + err = mStatus_NoSuchNameErr; + goto exit; + } + + // Success! + + if( outID ) + { + *outID = (mDNSInterfaceID) ifd; + } + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceIDToInfo +//=========================================================================================================================== + +mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ) +{ + mStatus err; + MDNSInterfaceItem * ifd; + + check( inMDNS ); + check( inID ); + check( outInfo ); + + // Search for an interface with the specified ID, + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( ifd == (MDNSInterfaceItem *) inID ) + { + break; + } + } + if( !ifd ) + { + err = mStatus_NoSuchNameErr; + goto exit; + } + + // Success! + + outInfo->name = ifd->name; + outInfo->ip = ifd->hostSet.ip; + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// debugf_ +//=========================================================================================================================== + +#if( MDNS_DEBUGMSGS ) +mDNSexport void debugf_( const char *format, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, format ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), format, args ); + va_end( args ); + + dlog( kDebugLevelInfo, "%s\n", buffer ); +} +#endif + +//=========================================================================================================================== +// verbosedebugf_ +//=========================================================================================================================== + +#if( MDNS_DEBUGMSGS > 1 ) +mDNSexport void verbosedebugf_( const char *format, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, format ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), format, args ); + va_end( args ); + + dlog( kDebugLevelVerbose, "%s\n", buffer ); +} +#endif + +//=========================================================================================================================== +// LogMsg +//=========================================================================================================================== + +void LogMsg( const char *inFormat, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, inFormat ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); + va_end( args ); + + dlog( kDebugLevelWarning, "%s\n", buffer ); +} + +#if 0 +#pragma mark - +#pragma mark == Platform Internals == +#endif + +//=========================================================================================================================== +// SetupNames +//=========================================================================================================================== + +mDNSlocal void SetupNames( mDNS * const inMDNS ) +{ + char tempCString[ 128 ]; + mDNSu8 tempPString[ 128 ]; + mDNSu8 * namePtr; + + // Set up the host name. + + tempCString[ 0 ] = '\0'; + GenerateUniqueHostName( tempCString, NULL ); + check( tempCString[ 0 ] != '\0' ); + if( tempCString[ 0 ] == '\0' ) + { + // No name so use the default. + + strcpy( tempCString, kMDNSDefaultName ); + } + inMDNS->nicelabel.c[ 0 ] = strlen( tempCString ); + memcpy( &inMDNS->nicelabel.c[ 1 ], tempCString, inMDNS->nicelabel.c[ 0 ] ); + check( inMDNS->nicelabel.c[ 0 ] > 0 ); + + // Set up the DNS name. + + tempCString[ 0 ] = '\0'; + GenerateUniqueDNSName( tempCString, NULL ); + if( tempCString[ 0 ] != '\0' ) + { + tempPString[ 0 ] = strlen( tempCString ); + memcpy( &tempPString[ 1 ], tempCString, tempPString[ 0 ] ); + namePtr = tempPString; + } + else + { + // No DNS name so use the host name. + + namePtr = inMDNS->nicelabel.c; + } + ConvertUTF8PstringToRFC1034HostLabel( namePtr, &inMDNS->hostlabel ); + if( inMDNS->hostlabel.c[ 0 ] == 0 ) + { + // Nice name has no characters that are representable as an RFC 1034 name (e.g. Japanese) so use the default. + + MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName ); + } + check( inMDNS->hostlabel.c[ 0 ] > 0 ); + + mDNS_GenerateFQDN( inMDNS ); + + dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] ); + dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] ); +} + +//=========================================================================================================================== +// SetupInterfaceList +//=========================================================================================================================== + +mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) +{ + mStatus err; + struct ifaddrs * addrs; + struct ifaddrs * p; + uint32_t flagMask; + uint32_t flagTest; + MDNSInterfaceItem ** next; + MDNSInterfaceItem * item; + + addrs = NULL; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list\n" ); + check( inMDNS ); + + // Tear down any existing interfaces that may be set up. + + TearDownInterfaceList( inMDNS ); + inMDNS->p->interfaceList = NULL; + next = &inMDNS->p->interfaceList; + + // Set up each interface that is active, multicast-capable, and not the loopback interface or point-to-point. + + flagMask = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTOPOINT; + flagTest = IFF_UP | IFF_MULTICAST; + + err = getifaddrs( &addrs ); + require_noerr( err, exit ); + + for( p = addrs; p; p = p->ifa_next ) + { + if( ( p->ifa_flags & flagMask ) == flagTest ) + { + err = SetupInterface( inMDNS, p, &item ); + require_noerr( err, exit ); + + *next = item; + next = &item->next; + } + } + err = mStatus_NoError; + +exit: + if( addrs ) + { + freeifaddrs( addrs ); + } + if( err ) + { + TearDownInterfaceList( inMDNS ); + } + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownInterfaceList +//=========================================================================================================================== + +mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ) +{ + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list\n" ); + check( inMDNS ); + + // Tear down all the interfaces. + + while( inMDNS->p->interfaceList ) + { + MDNSInterfaceItem * item; + + item = inMDNS->p->interfaceList; + inMDNS->p->interfaceList = item->next; + + TearDownInterface( inMDNS, item ); + } + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list done\n" ); + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// SetupInterface +//=========================================================================================================================== + +mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inAddr, MDNSInterfaceItem **outItem ) +{ + mStatus err; + MDNSInterfaceItem * item; + MDNSSocketRef socketRef; + const struct sockaddr_in * ipv4; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface (name=%s)\n", inAddr->ifa_name ); + check( inMDNS ); + check( inAddr ); + check( inAddr->ifa_addr ); + ipv4 = (const struct sockaddr_in *) inAddr->ifa_addr; + check( outItem ); + + // Allocate memory for the info item. + + item = (MDNSInterfaceItem *) calloc( 1, sizeof( *item ) ); + require_action( item, exit, err = mStatus_NoMemoryErr ); + strcpy( item->name, inAddr->ifa_name ); + item->multicastSocketRef = kInvalidSocketRef; + item->unicastSocketRef = kInvalidSocketRef; + item->sendingSocketRef = kInvalidSocketRef; + + // Set up the multicast DNS (port 5353) socket for this interface. + + err = SetupSocket( inMDNS, inAddr, MulticastDNSPort, &socketRef ); + require_noerr( err, exit ); + item->multicastSocketRef = socketRef; + + // Set up the unicast DNS (port 53) socket for this interface (to handle normal DNS requests). + + err = SetupSocket( inMDNS, inAddr, UnicastDNSPort, &socketRef ); + require_noerr( err, exit ); + item->unicastSocketRef = socketRef; + + // Set up the sending socket for this interface. + + err = SetupSocket( inMDNS, inAddr, zeroIPPort, &socketRef ); + require_noerr( err, exit ); + item->sendingSocketRef = socketRef; + + // Register this interface with mDNS. + + item->hostSet.InterfaceID = (mDNSInterfaceID) item; + item->hostSet.ip.type = mDNSAddrType_IPv4; + item->hostSet.ip.ip.v4.NotAnInteger = ipv4->sin_addr.s_addr; + item->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses; + + err = mDNS_RegisterInterface( inMDNS, &item->hostSet ); + require_noerr( err, exit ); + item->hostRegistered = mDNStrue; + + dlog( kDebugLevelInfo, DEBUG_NAME "Registered IP address: %u.%u.%u.%u\n", + item->hostSet.ip.ip.v4.b[ 0 ], item->hostSet.ip.ip.v4.b[ 1 ], + item->hostSet.ip.ip.v4.b[ 2 ], item->hostSet.ip.ip.v4.b[ 3 ] ); + + // Success! + + *outItem = item; + item = NULL; + +exit: + if( item ) + { + TearDownInterface( inMDNS, item ); + } + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface done (name=%s, err=%ld)\n", inAddr->ifa_name, err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownInterface +//=========================================================================================================================== + +mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, MDNSInterfaceItem *inItem ) +{ + MDNSSocketRef socketRef; + + check( inMDNS ); + check( inItem ); + + // Deregister this interface with mDNS. + + dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering IP address: %u.%u.%u.%u\n", + inItem->hostSet.ip.ip.v4.b[ 0 ], inItem->hostSet.ip.ip.v4.b[ 1 ], + inItem->hostSet.ip.ip.v4.b[ 2 ], inItem->hostSet.ip.ip.v4.b[ 3 ] ); + + if( inItem->hostRegistered ) + { + inItem->hostRegistered = mDNSfalse; + mDNS_DeregisterInterface( inMDNS, &inItem->hostSet ); + } + + // Close the multicast socket. + + socketRef = inItem->multicastSocketRef; + inItem->multicastSocketRef = kInvalidSocketRef; + if( socketRef != kInvalidSocketRef ) + { + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down multicast socket %d\n", socketRef ); + close( socketRef ); + } + + // Close the unicast socket. + + socketRef = inItem->unicastSocketRef; + inItem->unicastSocketRef = kInvalidSocketRef; + if( socketRef != kInvalidSocketRef ) + { + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down unicast socket %d\n", socketRef ); + close( socketRef ); + } + + // Close the sending socket. + + socketRef = inItem->sendingSocketRef; + inItem->sendingSocketRef = kInvalidSocketRef; + if( socketRef != kInvalidSocketRef ) + { + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down sending socket %d\n", socketRef ); + close( socketRef ); + } + + // Free the memory used by the interface info. + + free( inItem ); + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// SetupSocket +//=========================================================================================================================== + +mDNSlocal mStatus + SetupSocket( + mDNS * const inMDNS, + const struct ifaddrs * inAddr, + mDNSIPPort inPort, + MDNSSocketRef * outSocketRef ) +{ + mStatus err; + MDNSSocketRef socketRef; + int option; + unsigned char optionByte; + struct ip_mreq mreq; + const struct sockaddr_in * ipv4; + struct sockaddr_in addr; + mDNSv4Addr ip; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done\n" ); + check( inMDNS ); + check( inAddr ); + check( inAddr->ifa_addr ); + ipv4 = (const struct sockaddr_in *) inAddr->ifa_addr; + check( outSocketRef ); + + // Set up a UDP socket for multicast DNS. + + socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + require_errno_action( socketRef, errno, exit, err = mStatus_UnknownErr ); + + // A port of zero means this socket is for sending and should be set up for sending. Otherwise, it is for receiving + // and should be set up for receiving. The reason for separate sending vs receiving sockets to workaround problems + // with VxWorks IP stack when using dynamic IP configuration such as DHCP (problems binding to wildcard IP when the + // IP address later changes). Since we have to bind the Multicast DNS address to workaround these issues we have to + // use a separate sending socket since it is illegal to send a packet with a multicast source address (RFC 1122). + + if( inPort.NotAnInteger != zeroIPPort.NotAnInteger ) + { + // Turn on reuse port option so multiple servers can listen for Multicast DNS packets. + + option = 1; + err = setsockopt( socketRef, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) ); + check_errno( err, errno ); + + // Join the all-DNS multicast group so we receive Multicast DNS packets. + + ip.NotAnInteger = ipv4->sin_addr.s_addr; + mreq.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; + mreq.imr_interface.s_addr = ip.NotAnInteger; + err = setsockopt( socketRef, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof( mreq ) ); + check_errno( err, errno ); + + // Bind to the multicast DNS address and specified port (53 for unicast or 5353 for multicast). + + memset( &addr, 0, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = inPort.NotAnInteger; + addr.sin_addr.s_addr = AllDNSLinkGroup.NotAnInteger; + err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) ); + check_errno( err, errno ); + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done (%s, %u.%u.%u.%u:%u, %d)\n", + inAddr->ifa_name, ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], ntohs( inPort.NotAnInteger ), socketRef ); + } + else + { + // Bind to the interface address and multicast DNS port. + + ip.NotAnInteger = ipv4->sin_addr.s_addr; + memset( &addr, 0, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = MulticastDNSPort.NotAnInteger; + addr.sin_addr.s_addr = ip.NotAnInteger; + err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) ); + check_errno( err, errno ); + + // Direct multicast packets to the specified interface. + + addr.sin_addr.s_addr = ip.NotAnInteger; + err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr.sin_addr, sizeof( addr.sin_addr ) ); + check_errno( err, errno ); + + // Set the TTL of outgoing unicast packets to 255 (helps against spoofing). + + option = 255; + err = setsockopt( socketRef, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) ); + check_errno( err, errno ); + + // Set the TTL of outgoing multicast packets to 255 (helps against spoofing). + + optionByte = 255; + err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &optionByte, sizeof( optionByte ) ); + check_errno( err, errno ); + + // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to maximize 802.11 multicast rate. + + option = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; + err = setsockopt( socketRef, IPPROTO_IP, IP_TOS, (char *) &option, sizeof( option ) ); + check_errno( err, errno ); + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up sending socket done (%s, %u.%u.%u.%u, %d)\n", + inAddr->ifa_name, ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], socketRef ); + } + + // Success! + + *outSocketRef = socketRef; + socketRef = kInvalidSocketRef; + err = mStatus_NoError; + +exit: + if( socketRef != kInvalidSocketRef ) + { + close( socketRef ); + } + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Commands == +#endif + +//=========================================================================================================================== +// SetupCommandPipe +//=========================================================================================================================== + +mDNSlocal mStatus SetupCommandPipe( mDNS * const inMDNS ) +{ + mStatus err; + + // Clean up any leftover command pipe. + + TearDownCommandPipe( inMDNS ); + + // Create the pipe device and open it. + + pipeDevCreate( kMDNSPipeName, kMDNSPipeMessageQueueSize, kMDNSPipeMessageSize ); + + inMDNS->p->commandPipe = open( kMDNSPipeName, O_RDWR, 0 ); + require_errno_action( inMDNS->p->commandPipe, errno, exit, err = mStatus_UnsupportedErr ); + + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// TearDownCommandPipe +//=========================================================================================================================== + +mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS ) +{ + if( inMDNS->p->commandPipe != ERROR ) + { + close( inMDNS->p->commandPipe ); + inMDNS->p->commandPipe = ERROR; + } + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// SendCommand +//=========================================================================================================================== + +mDNSlocal mStatus SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode ) +{ + mStatus err; + + require_action( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr ); + + err = write( inMDNS->p->commandPipe, &inCommandCode, sizeof( inCommandCode ) ); + require_errno( err, errno, exit ); + + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// ProcessCommand +//=========================================================================================================================== + +mDNSlocal mStatus ProcessCommand( mDNS * const inMDNS ) +{ + mStatus err; + MDNSPipeCommandCode commandCode; + + require_action( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr ); + + // Read the command code from the pipe and dispatch it. + + err = read( inMDNS->p->commandPipe, &commandCode, sizeof( commandCode ) ); + require_errno( err, errno, exit ); + + switch( commandCode ) + { + case kMDNSPipeCommandCodeReschedule: + + // Reschedule event. Do nothing here, but this will cause mDNS_Execute to run before waiting again. + + dlog( kDebugLevelChatty, DEBUG_NAME "reschedule\n" ); + break; + + case kMDNSPipeCommandCodeReconfigure: + ProcessCommandReconfigure( inMDNS ); + break; + + case kMDNSPipeCommandCodeQuit: + + // Quit requested. Set quit flag and bump the config ID to let the thread exit normally. + + dlog( kDebugLevelVerbose, DEBUG_NAME "processing pipe quit command\n" ); + inMDNS->p->quit = mDNStrue; + ++inMDNS->p->configID; + break; + + default: + dlog( kDebugLevelError, DEBUG_NAME "unknown pipe command code (code=0x%08X)\n", commandCode ); + err = mStatus_BadParamErr; + goto exit; + break; + } + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// ProcessCommandReconfigure +//=========================================================================================================================== + +mDNSlocal void ProcessCommandReconfigure( mDNS *inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelVerbose, DEBUG_NAME "processing pipe reconfigure command\n" ); + + // Tear down the existing interfaces and set up new ones using the new IP info. + + mDNSPlatformLock( inMDNS ); + + err = TearDownInterfaceList( inMDNS ); + check_noerr( err ); + + err = SetupInterfaceList( inMDNS ); + check_noerr( err ); + + mDNSPlatformUnlock( inMDNS ); + + // Inform clients of the change. + + if( inMDNS->MainCallback ) + { + inMDNS->MainCallback( inMDNS, mStatus_ConfigChanged ); + } + + // Force mDNS to update. + + mDNSCoreMachineSleep( inMDNS, mDNSfalse ); + + // Bump the config ID so the main processing loop detects the configuration change. + + ++inMDNS->p->configID; +} + +#if 0 +#pragma mark - +#pragma mark == Threads == +#endif + +//=========================================================================================================================== +// SetupTask +//=========================================================================================================================== + +mDNSlocal mStatus SetupTask( mDNS * const inMDNS ) +{ + mStatus err; + int task; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread\n" ); + check( inMDNS ); + + // Create our main thread. Note: The task will save off its ID in the globals. We cannot do it here because the + // task invokes code that needs it and the task may begin execution before taskSpawn returns the task ID. + // This also means code in this thread context cannot rely on the task ID until the task has fully initialized. + + task = taskSpawn( kMDNSTaskName, kMDNSTaskPriority, 0, kMDNSTaskStackSize, (FUNCPTR) Task, + (int) inMDNS, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + require_action( task != ERROR, exit, err = mStatus_NoMemoryErr ); + + err = mStatus_NoError; + +exit: + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread done (err=%ld, id=%d)\n", err, task ); + return( err ); +} + +//=========================================================================================================================== +// TearDownTask +//=========================================================================================================================== + +mDNSlocal mStatus TearDownTask( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down thread\n" ); + check( inMDNS ); + + // Send a quit command to cause the thread to exit. + + SendCommand( inMDNS, kMDNSPipeCommandCodeQuit ); + + // Wait for the thread to signal it has exited. Timeout in 10 seconds to handle a hung thread. + + if( inMDNS->p->quitEvent ) + { + err = semTake( inMDNS->p->quitEvent, sysClkRateGet() * 10 ); + check_noerr( err ); + } + err = mStatus_NoError; + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down thread done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// Task +//=========================================================================================================================== + +mDNSlocal void Task( mDNS *inMDNS ) +{ + mStatus err; + fd_set allReadSet; + MDNSInterfaceItem * item; + int maxSocket; + long configID; + struct timeval timeout; + + dlog( kDebugLevelVerbose, DEBUG_NAME "task starting\n" ); + check( inMDNS ); + + // Set up everything up. + + err = TaskInit( inMDNS ); + require_noerr( err, exit ); + + // Main Processing Loop. + + while( !inMDNS->p->quit ) + { + // Set up the read set here to avoid the overhead of setting it up each iteration of the main processing loop. + // If the configuration changes, the server ID will be bumped, causing this code to set up the read set again. + + TaskSetupReadSet( inMDNS, &allReadSet, &maxSocket ); + configID = inMDNS->p->configID; + dlog( kDebugLevelVerbose, DEBUG_NAME "task starting processing loop (configID=%ld)\n", configID ); + + while( configID == inMDNS->p->configID ) + { + mDNSs32 nextTaskTime; + fd_set readSet; + int n; + + // Give the mDNS core a chance to do its work. Reset the rescheduled flag before calling mDNS_Execute + // so anything that needs processing during or after causes a re-schedule to wake up the thread. The + // reschedule flag is set to 1 after processing a waking up to prevent redundant reschedules while + // processing packets. This introduces a window for a race condition because the thread wake-up and + // reschedule set are not atomic, but this would be benign. Even if the reschedule flag is "corrupted" + // like this, it would only result in a redundant reschedule since it will loop back to mDNS_Execute. + + inMDNS->p->rescheduled = 0; + nextTaskTime = mDNS_Execute( inMDNS ); + TaskSetupTimeout( nextTaskTime, &timeout ); + + // Wait until something occurs (e.g. command, incoming packet, or timeout). + + readSet = allReadSet; + n = select( maxSocket + 1, &readSet, NULL, NULL, &timeout ); + inMDNS->p->rescheduled = 1; + check_errno( n, errno ); + dlog( kDebugLevelChatty - 1, DEBUG_NAME "task select result = %d\n", n ); + if( n == 0 ) + { + // Next task timeout occurred. Loop back up to give mDNS core a chance to work. + + dlog( kDebugLevelChatty, DEBUG_NAME "next task timeout occurred (%ld)\n", mDNSPlatformTimeNow() ); + continue; + } + + // Scan the read set to determine if any sockets have something pending and process them. + + n = 0; + for( item = inMDNS->p->interfaceList; item; item = item->next ) + { + if( FD_ISSET( item->multicastSocketRef, &readSet ) ) + { + TaskProcessPacket( inMDNS, item, item->multicastSocketRef ); + ++n; + } + if( FD_ISSET( item->unicastSocketRef, &readSet ) ) + { + TaskProcessPacket( inMDNS, item, item->unicastSocketRef ); + ++n; + } + } + + // Check for a pending command and process it. + + if( FD_ISSET( inMDNS->p->commandPipe, &readSet ) ) + { + ProcessCommand( inMDNS ); + ++n; + } + check( n > 0 ); + } + } + +exit: + // Signal we've quit. + + check( inMDNS->p->quitEvent ); + semGive( inMDNS->p->quitEvent ); + + dlog( kDebugLevelInfo, DEBUG_NAME "task ended\n" ); +} + +//=========================================================================================================================== +// TaskInit +//=========================================================================================================================== + +mDNSlocal mStatus TaskInit( mDNS *inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelVerbose, DEBUG_NAME "task init\n" ); + check( inMDNS->p->readyEvent ); + + inMDNS->p->task = taskIdSelf(); + + err = SetupCommandPipe( inMDNS ); + require_noerr( err, exit ); + + SetupNames( inMDNS ); + + err = SetupInterfaceList( inMDNS ); + require_noerr( err, exit ); + +exit: + // Signal the "ready" semaphore to indicate the task initialization code has completed (success or not). + + inMDNS->p->taskInitErr = err; + semGive( inMDNS->p->readyEvent ); + + dlog( kDebugLevelVerbose, DEBUG_NAME "task init done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// TaskSetupReadSet +//=========================================================================================================================== + +mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSocket ) +{ + MDNSInterfaceItem * item; + int maxSocket; + + dlog( kDebugLevelVerbose, DEBUG_NAME "task setting up read set\n" ); + check( inMDNS ); + check( outReadSet ); + check( outMaxSocket ); + + // Initialize the read set. Default the max socket to -1 so "maxSocket + 1" (as needed by select) is zero. This + // should never happen since we should always have at least one interface, but it's just to be safe. + + FD_ZERO( outReadSet ); + maxSocket = -1; + + // Add all the receiving sockets to the read set. + + for( item = inMDNS->p->interfaceList; item; item = item->next ) + { + FD_SET( item->multicastSocketRef, outReadSet ); + FD_SET( item->unicastSocketRef, outReadSet ); + if( item->multicastSocketRef > maxSocket ) + { + maxSocket = item->multicastSocketRef; + } + if( item->unicastSocketRef > maxSocket ) + { + maxSocket = item->unicastSocketRef; + } + } + + // Add the command pipe to the read set. + + FD_SET( inMDNS->p->commandPipe, outReadSet ); + if( inMDNS->p->commandPipe > maxSocket ) + { + maxSocket = inMDNS->p->commandPipe; + } + check( maxSocket > 0 ); + *outMaxSocket = maxSocket; + + dlog( kDebugLevelVerbose, DEBUG_NAME "task setting up read set done (maxSocket=%d)\n", maxSocket ); +} + +//=========================================================================================================================== +// TaskSetupTimeout +//=========================================================================================================================== + +mDNSlocal void TaskSetupTimeout( mDNSs32 inNextTaskTime, struct timeval *outTimeout ) +{ + mDNSs32 delta; + + // Calculate how long to wait before performing idle processing. + + delta = inNextTaskTime - mDNSPlatformTimeNow(); + if( delta <= 0 ) + { + // The next task time is now or in the past. Set the timeout to fire immediately. + + outTimeout->tv_sec = 0; + outTimeout->tv_usec = 0; + } + else + { + // Calculate the seconds and microseconds until the timeout should occur. Add one to the ticks remainder + // before multiplying to account for integer rounding error and avoid firing the timeout too early. + + outTimeout->tv_sec = delta / mDNSPlatformOneSecond; + outTimeout->tv_usec = ( ( delta % mDNSPlatformOneSecond ) + 1 ) * gMDNSTicksToMicrosecondsMultiplier; + + // Check if the microseconds is more than 1 second. If so, bump the seconds instead. + + if( outTimeout->tv_usec >= 1000000L ) + { + outTimeout->tv_sec += 1; + outTimeout->tv_usec = 0; + } + } + + dlog( kDebugLevelChatty, DEBUG_NAME "next task in %ld:%ld seconds (%ld)\n", + outTimeout->tv_sec, outTimeout->tv_usec, inNextTaskTime ); +} +//=========================================================================================================================== +// TaskProcessPacket +//=========================================================================================================================== + +mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSSocketRef inSocketRef ) +{ + int n; + mDNSBool isMulticast; + DNSMessage packet; + struct sockaddr_in addr; + int addrSize; + mDNSu8 * packetEndPtr; + mDNSAddr srcAddr; + mDNSIPPort srcPort; + mDNSAddr dstAddr; + mDNSIPPort dstPort; + + isMulticast = ( inSocketRef == inItem->multicastSocketRef ); + + // Receive the packet. + + addrSize = sizeof( addr ); + n = recvfrom( inSocketRef, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize ); + check( n >= 0 ); + if( n >= 0 ) + { + // Set up the src/dst/interface info. + + srcAddr.type = mDNSAddrType_IPv4; + srcAddr.ip.v4.NotAnInteger = addr.sin_addr.s_addr; + srcPort.NotAnInteger = addr.sin_port; + dstAddr.type = mDNSAddrType_IPv4; + dstAddr.ip.v4 = isMulticast ? AllDNSLinkGroup : inItem->hostSet.ip.ip.v4; + dstPort = isMulticast ? MulticastDNSPort : UnicastDNSPort; + + dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" ); + dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", n ); + dlog( kDebugLevelChatty, DEBUG_NAME " src = %u.%u.%u.%u:%hu\n", + srcAddr.ip.v4.b[ 0 ], srcAddr.ip.v4.b[ 1 ], srcAddr.ip.v4.b[ 2 ], srcAddr.ip.v4.b[ 3 ], + ntohs( srcPort.NotAnInteger ) ); + dlog( kDebugLevelChatty, DEBUG_NAME " dst = %u.%u.%u.%u:%hu\n", + dstAddr.ip.v4.b[ 0 ], dstAddr.ip.v4.b[ 1 ], dstAddr.ip.v4.b[ 2 ], dstAddr.ip.v4.b[ 3 ], + ntohs( dstPort.NotAnInteger ) ); + dlog( kDebugLevelChatty, DEBUG_NAME " interface = 0x%08X\n", (int) inItem->hostSet.InterfaceID ); + dlog( kDebugLevelChatty, DEBUG_NAME "--\n" ); + + // Dispatch the packet to mDNS. + + packetEndPtr = ( (mDNSu8 *) &packet ) + n; + mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inItem->hostSet.InterfaceID, 255 ); + } + + // Update counters. + + inItem->recvMulticastCounter += isMulticast; + inItem->recvUnicastCounter += !isMulticast; + inItem->recvErrorCounter += ( n < 0 ); +} + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +#if( TARGET_NON_APPLE ) +//=========================================================================================================================== +// GenerateUniqueHostName +// +// Non-Apple platform stub routine to generate a unique name for the device. Should be implemented to return a unique name. +//=========================================================================================================================== + +mDNSlocal void GenerateUniqueHostName( char *outName, long *ioSeed ) +{ + DEBUG_UNUSED( ioSeed ); + + // $$$ Non-Apple Platforms: Fill in appropriate name for device. + + mDNSPlatformStrCopy( kMDNSDefaultName, outName ); +} + +//=========================================================================================================================== +// GenerateUniqueDNSName +// +// Non-Apple platform stub routine to generate a unique RFC 1034-compatible DNS name for the device. Should be +// implemented to return a unique name. +//=========================================================================================================================== + +mDNSlocal void GenerateUniqueDNSName( char *outName, long *ioSeed ) +{ + DEBUG_UNUSED( ioSeed ); + + // $$$ Non-Apple Platforms: Fill in appropriate DNS name for device. + + mDNSPlatformStrCopy( kMDNSDefaultName, outName ); +} +#endif + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// getifaddrs +//=========================================================================================================================== + +int getifaddrs( struct ifaddrs **outAddrs ) +{ + int err; + struct ifaddrs * head; + struct ifaddrs ** next; + struct ifaddrs * ifa; + int i; + struct ifnet * ifp; + char ipString[ INET_ADDR_LEN ]; + int n; + + head = NULL; + next = &head; + + i = 1; + for( ;; ) + { + ifp = ifIndexToIfp( i ); + if( !ifp ) + { + break; + } + ++i; + + // Allocate and initialize the ifaddrs structure and attach it to the linked list. + + ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); + require_action( ifa, exit, err = ENOMEM ); + + *next = ifa; + next = &ifa->ifa_next; + + // Fetch the name. + + ifa->ifa_name = (char *) malloc( 16 ); + require_action( ifa->ifa_name, exit, err = ENOMEM ); + + n = sprintf( ifa->ifa_name, "%s%d", ifp->if_name, ifp->if_unit ); + require_action( n < 16, exit, err = ENOBUFS ); + + // Fetch the address. + + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( struct sockaddr_in ) ); + require_action( ifa->ifa_addr, exit, err = ENOMEM ); + + ipString[ 0 ] = '\0'; + #if( TARGET_NON_APPLE ) + err = ifAddrGet( ifa->ifa_name, ipString ); + require_noerr( err, exit ); + #else + err = ifAddrGetNonAlias( ifa->ifa_name, ipString ); + require_noerr( err, exit ); + #endif + + err = sock_pton( ipString, AF_INET, ifa->ifa_addr, 0, NULL ); + require_noerr( err, exit ); + + // Fetch flags. + + ifa->ifa_flags = ifp->if_flags; + } + + // Success! + + if( outAddrs ) + { + *outAddrs = head; + head = NULL; + } + err = 0; + +exit: + if( head ) + { + freeifaddrs( head ); + } + return( err ); +} + +//=========================================================================================================================== +// freeifaddrs +//=========================================================================================================================== + +void freeifaddrs( struct ifaddrs *inAddrs ) +{ + struct ifaddrs * p; + struct ifaddrs * q; + + // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields. + + for( p = inAddrs; p; p = q ) + { + q = p->ifa_next; + + if( p->ifa_name ) + { + free( p->ifa_name ); + p->ifa_name = NULL; + } + if( p->ifa_addr ) + { + free( p->ifa_addr ); + p->ifa_addr = NULL; + } + if( p->ifa_netmask ) + { + free( p->ifa_netmask ); + p->ifa_netmask = NULL; + } + if( p->ifa_dstaddr ) + { + free( p->ifa_dstaddr ); + p->ifa_dstaddr = NULL; + } + if( p->ifa_data ) + { + free( p->ifa_data ); + p->ifa_data = NULL; + } + free( p ); + } +} + +//=========================================================================================================================== +// sock_pton +//=========================================================================================================================== + +int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ) +{ + int err; + + if( inFamily == AF_INET ) + { + struct sockaddr_in * ipv4; + + if( inAddrSize == 0 ) + { + inAddrSize = sizeof( struct sockaddr_in ); + } + if( inAddrSize < sizeof( struct sockaddr_in ) ) + { + err = EINVAL; + goto exit; + } + + ipv4 = (struct sockaddr_in *) outAddr; + err = inet_aton( (char *) inString, &ipv4->sin_addr ); + if( err == 0 ) + { + ipv4->sin_family = AF_INET; + if( outAddrSize ) + { + *outAddrSize = sizeof( struct sockaddr_in ); + } + } + } +#if( defined( AF_INET6 ) ) + else if( inFamily == AF_INET6 ) // $$$ TO DO: Add IPv6 support. + { + err = EAFNOSUPPORT; + goto exit; + } +#endif + else + { + err = EAFNOSUPPORT; + goto exit; + } + +exit: + return( err ); +} + +//=========================================================================================================================== +// sock_ntop +//=========================================================================================================================== + +char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ) +{ + const struct sockaddr * addr; + + addr = (const struct sockaddr *) inAddr; + if( addr->sa_family == AF_INET ) + { + struct sockaddr_in * ipv4; + + if( inAddrSize == 0 ) + { + inAddrSize = sizeof( struct sockaddr_in ); + } + if( inAddrSize < sizeof( struct sockaddr_in ) ) + { + errno = EINVAL; + inBuffer = NULL; + goto exit; + } + if( inBufferSize < 16 ) + { + errno = ENOBUFS; + inBuffer = NULL; + goto exit; + } + + ipv4 = (struct sockaddr_in *) addr; + inet_ntoa_b( ipv4->sin_addr, inBuffer ); + } +#if( defined( AF_INET6 ) ) + else if( addr->sa_family == AF_INET6 ) // $$$ TO DO: Add IPv6 support. + { + errno = EAFNOSUPPORT; + inBuffer = NULL; + goto exit; + } +#endif + else + { + errno = EAFNOSUPPORT; + inBuffer = NULL; + goto exit; + } + +exit: + return( inBuffer ); +} + +#if 0 +#pragma mark - +#pragma mark == Debugging == +#endif + +#if( DEBUG ) + +void mDNSShow( BOOL inShowRecords ); +void mDNSShowRecords( void ); +void mDNSShowTXT( const void *inTXT, size_t inTXTSize ); + +//=========================================================================================================================== +// mDNSShow +//=========================================================================================================================== + +void mDNSShow( BOOL inShowRecords ) +{ + MDNSInterfaceItem * item; + mDNSAddr ip; + int n; + + if( !gMDNSPtr ) + { + printf( "### mDNS not initialized\n" ); + return; + } + + // Globals + + printf( "\n-- mDNS globals --\n" ); + printf( " sizeof( mDNS ) = %d\n", (int) sizeof( mDNS ) ); + printf( " sizeof( ResourceRecord ) = %d\n", (int) sizeof( ResourceRecord ) ); + printf( " sizeof( AuthRecord ) = %d\n", (int) sizeof( AuthRecord ) ); + printf( " sizeof( CacheRecord ) = %d\n", (int) sizeof( CacheRecord ) ); + printf( " gMDNSPtr = 0x%08lX\n", (unsigned long) gMDNSPtr ); + printf( " gMDNSTicksToMicrosecondsMultiplier = %ld\n", gMDNSTicksToMicrosecondsMultiplier ); + printf( " lockID = 0x%08lX\n", (unsigned long) gMDNSPtr->p->lockID ); + printf( " readyEvent = 0x%08lX\n", (unsigned long) gMDNSPtr->p->readyEvent ); + printf( " taskInitErr = %ld\n", gMDNSPtr->p->taskInitErr ); + printf( " quitEvent = 0x%08lX\n", (unsigned long) gMDNSPtr->p->quitEvent ); + printf( " commandPipe = %d\n", gMDNSPtr->p->commandPipe ); + printf( " task = 0x%08lX\n", (unsigned long) gMDNSPtr->p->task ); + printf( " quit = %d\n", gMDNSPtr->p->quit ); + printf( " configID = %ld\n", gMDNSPtr->p->configID ); + printf( " rescheduled = %d\n", gMDNSPtr->p->rescheduled ); + printf( " nicelabel = \"%.*s\"\n", gMDNSPtr->nicelabel.c[ 0 ], (char *) &gMDNSPtr->nicelabel.c[ 1 ] ); + printf( " hostLabel = \"%.*s\"\n", gMDNSPtr->hostlabel.c[ 0 ], (char *) &gMDNSPtr->hostlabel.c[ 1 ] ); + printf( "\n"); + + // Interfaces + + printf( "\n-- mDNS interfaces --\n" ); + n = 1; + for( item = gMDNSPtr->p->interfaceList; item; item = item->next ) + { + printf( " -- interface %u --\n", n ); + printf( " name = \"%s\"\n", item->name ); + printf( " multicastSocketRef = %d\n", item->multicastSocketRef ); + printf( " unicastSocketRef = %d\n", item->unicastSocketRef ); + printf( " sendingSocketRef = %d\n", item->sendingSocketRef ); + ip = item->hostSet.ip; + printf( " hostSet.ip = %u.%u.%u.%u\n", ip.ip.v4.b[ 0 ], ip.ip.v4.b[ 1 ], + ip.ip.v4.b[ 2 ], ip.ip.v4.b[ 3 ] ); + printf( " hostSet.advertise = %s\n", item->hostSet.Advertise ? "YES" : "NO" ); + printf( " hostRegistered = %s\n", item->hostRegistered ? "YES" : "NO" ); + printf( " --\n" ); + printf( " sendMulticastCounter = %d\n", item->sendMulticastCounter ); + printf( " sendUnicastCounter = %d\n", item->sendUnicastCounter ); + printf( " sendErrorCounter = %d\n", item->sendErrorCounter ); + printf( " recvMulticastCounter = %d\n", item->recvMulticastCounter ); + printf( " recvUnicastCounter = %d\n", item->recvUnicastCounter ); + printf( " recvErrorCounter = %d\n", item->recvErrorCounter ); + printf( " recvLoopCounter = %d\n", item->recvLoopCounter ); + printf( "\n" ); + ++n; + } + + // Resource Records + + if( inShowRecords ) + { + mDNSShowRecords(); + } +} + +//=========================================================================================================================== +// mDNSShowRecords +//=========================================================================================================================== + +void mDNSShowRecords( void ) +{ + MDNSInterfaceItem * item; + int n; + AuthRecord * record; + char name[ 512 ]; + + printf( "\n-- mDNS resource records --\n" ); + n = 1; + for( record = gMDNSPtr->ResourceRecords; record; record = record->next ) + { + item = (MDNSInterfaceItem *) record->resrec.InterfaceID; + ConvertDomainNameToCString( &record->resrec.name, name ); + printf( " -- record %d --\n", n ); + printf( " interface = 0x%08X (%s)\n", (int) item, item ? item->name : "" ); + printf( " name = \"%s\"\n", name ); + printf( "\n" ); + ++n; + } + printf( "\n"); +} + +//=========================================================================================================================== +// mDNSShowTXT +//=========================================================================================================================== + +void mDNSShowTXT( const void *inTXT, size_t inTXTSize ) +{ + const mDNSu8 * p; + const mDNSu8 * end; + int i; + mDNSu8 size; + + printf( "\nTXT record (%u bytes):\n\n", inTXTSize ); + + p = (const mDNSu8 *) inTXT; + end = p + inTXTSize; + i = 0; + + while( p < end ) + { + size = *p++; + if( ( p + size ) > end ) + { + printf( "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" ); + break; + } + printf( "%2d (%3d bytes): \"%.*s\"\n", i, size, size, p ); + p += size; + ++i; + } + printf( "\n" ); +} +#endif // DEBUG diff --git a/mDNSVxWorks/mDNSVxWorks.h b/mDNSVxWorks/mDNSVxWorks.h new file mode 100644 index 0000000..de026a4 --- /dev/null +++ b/mDNSVxWorks/mDNSVxWorks.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Contains: mDNS platform plugin for VxWorks. + + Copyright: Copyright (C) 2002-2003 Apple Computer, Inc., All Rights Reserved. + + Change History (most recent first): + +$Log: mDNSVxWorks.h,v $ +Revision 1.2 2003/08/12 19:56:27 cheshire +Update to APSL 2.0 + +Revision 1.1 2003/08/02 10:06:49 bradley +mDNS platform plugin for VxWorks. + +*/ + +#ifndef __MDNS_VXWORKS__ +#define __MDNS_VXWORKS__ + +#include "vxWorks.h" +#include "semLib.h" + +#include "mDNSClientAPI.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// Forward Declarations + +typedef struct MDNSInterfaceItem MDNSInterfaceItem; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct mDNS_PlatformSupport_struct + + @abstract Structure containing platform-specific data. +*/ + +struct mDNS_PlatformSupport_struct +{ + SEM_ID lockID; + SEM_ID readyEvent; + mStatus taskInitErr; + SEM_ID quitEvent; + MDNSInterfaceItem * interfaceList; + int commandPipe; + int task; + mDNSBool quit; + long configID; + int rescheduled; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function mDNSReconfigure + + @abstract Tell mDNS that the configuration has changed. Call when IP address changes, link goes up after being down, etc. + + @discussion + + VxWorks does not provide a generic mechanism for getting notified when network interfaces change so this routines + provides a way for BSP-specific code to signal mDNS that something has changed and it should re-build its interfaces. +*/ + +void mDNSReconfigure( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct ifaddrs + + @abstract Interface information +*/ + +struct ifaddrs +{ + struct ifaddrs * ifa_next; + char * ifa_name; + u_int ifa_flags; + struct sockaddr * ifa_addr; + struct sockaddr * ifa_netmask; + struct sockaddr * ifa_dstaddr; + void * ifa_data; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function getifaddrs + + @abstract Builds a linked list of interfaces. Caller must free using freeifaddrs if successful. +*/ + +int getifaddrs( struct ifaddrs **outAddrs ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function freeifaddrs + + @abstract Frees a linked list of interfaces built with getifaddrs. +*/ + +void freeifaddrs( struct ifaddrs *inAddrs ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function sock_pton + + @abstract Converts a 'p'resentation address string into a 'n'umeric sockaddr structure. + + @result 0 if successful or an error code on failure. +*/ + +int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function sock_ntop + + @abstract Converts a 'n'umeric sockaddr structure into a 'p'resentation address string. + + @result Ptr to 'p'resentation address string buffer if successful or NULL on failure. +*/ + +char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ); + +#ifdef __cplusplus + } +#endif + +#endif // __MDNS_VXWORKS__ diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.sln b/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.sln new file mode 100644 index 0000000..f588af6 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Application", "Application.vcproj", "{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Debug.ActiveCfg = Debug|Win32 + {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Debug.Build.0 = Debug|Win32 + {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Release.ActiveCfg = Release|Win32 + {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.vcproj b/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.vcproj new file mode 100644 index 0000000..3f64b2a --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Application.vcproj @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico new file mode 100644 index 0000000000000000000000000000000000000000..a992327973c4f716e13ba43b7803f59885b99069 GIT binary patch literal 2238 zcmZvcF=!k|6o%hLf>9QO;znRwAjRQKj8$nWR}L=JL?XtAYj@k7ud#88fX^7*CagfP zsazx>Dbl#U;@rd#54f|N7)%5)=w{hn?ZTc(@Z_6W=`4KSyEnV@&&+$@ym`AK9e(TU z60TpDL{@nE5?jZZvMVpdStEbE z$mPYGH}b=8zY|DfF_vPc?P@fQrqQ(5TstL3kI`fF7(GV!%Tn|dJ*AVPr|8_opc{09 zZV(K*qwnZD6C!I`O?-_b%_1#MLUpRvL%@VV!=Pc%FlZP8 zIt&^H4TFY3!=N1+92ySoIh+d_K;iCxDP}Nqzhzirg2x1kaT>!C(!gNMLkvp{OGpbG zF&r@*F&r@*Aysh1FvKv#F!JO4xYPP$BnO6g z1~vnSAznCI&p{3h9L967v2ffhGO!p}3=oEZp#)$tEHq3q2nH7Z7=Abt1B>zb2rRxD z{3)Qp;9zhtI2arZ4u()pFc=sd3=ReZ18y+p!{A_WFgO@+jiJNfU~n)v037iR&KwIJ zlVW_t^kX<&@a^Zs^11vhH*emQyLa!(!-o%LZ*NbYJ$ojPA3v6tFJH>5SFdD$e_sv` z4x}tgnNFut{OjbWM~`@qhh*2k1OA4;(DQzM*GV+H>OFS~p_E}Ug~o@6$kpRnG$)9aoq zl(Jd!5eLQDt-q z%I7zzPL8Yj@tX0v;;QR9Qlt02+NY|tlS1W9lO#<(%8TmcAyuP1%ZiDr4ol|bq{n8t znx&vj?1Y5&nxaxPTE2^^i%!H>Uh&R3v1h)S-qW;(A;K| z_Pn$9)GF)Qs=v2;y-PW#VY8#m6k#Ar+PG??*W1{3#e{nB?zzH9VOwv5jh&s1Zat!+ zjx#%I>Zs};wWm?XcA|{Ul>Ps!uM%O z@lK*>YcLo@<@=KG$J_2G Z3_DBMaAEw#P!(#tJpE?6_8%Xh{{c@pFlhh) literal 0 HcmV?d00001 diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc new file mode 100644 index 0000000..1d9c2eb --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc @@ -0,0 +1,281 @@ +// Microsoft Visual C++ generated resource script. +// +#include "Resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "Resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#include ""Resources\\Application.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAIN_ICON ICON "Application.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CHOOSER_DIALOG DIALOGEX 0, 0, 332, 252 +STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | + WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +CAPTION "Rendezvous Browser for Windows" +MENU IDR_CHOOSER_DIALOG_MENU +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL "",IDC_CHOOSER_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | + LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,116,8,208,136 + CONTROL "",IDC_SERVICE_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | + LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,8,8,100,136 + CONTROL "",IDC_DOMAIN_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | + LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,8,152,100,92 + GROUPBOX "Information",IDC_STATIC,116,148,208,96 + RTEXT "Name:",IDC_STATIC,122,164,38,8 + LTEXT "My Device",IDC_INFO_NAME_TEXT,164,164,152,10,SS_SUNKEN + RTEXT "Text:",IDC_STATIC,122,203,38,8 + LTEXT "Information About My Device",IDC_INFO_TEXT_TEXT,164,203, + 152,34,SS_NOPREFIX | SS_SUNKEN + RTEXT "IP address:",IDC_STATIC,122,177,38,8 + LTEXT "123.124.125.126:1234",IDC_INFO_IP_TEXT,164,177,152,10, + SS_SUNKEN + RTEXT "Interface:",IDC_STATIC,122,190,38,8 + LTEXT "123.124.125.126",IDC_INFO_INTERFACE_TEXT,164,190,152,10, + SS_SUNKEN +END + +IDD_ABOUT_DIALOG DIALOGEX 0, 0, 244, 73 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "About Rendezvous Browser" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + ICON IDR_MAIN_ICON,IDC_ABOUT_APP_ICON,12,12,20,20 + LTEXT "Rendezvous Browser for Windows",IDC_ABOUT_APP_NAME_TEXT, + 44,11,192,12 + LTEXT "Version 1.0d2",IDC_ABOUT_APP_VERSION_TEXT,44,25,192,8 + LTEXT "Copyright (C) 2002-2003 Apple Computer, Inc.", + IDC_ABOUT_COPYRIGHT_TEXT,4,60,156,8 + DEFPUSHBUTTON "OK",IDOK,192,52,44,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "Quick & Dirty 1 day hack by Bob Bradley" + VALUE "CompanyName", "Apple Computer, Inc." + VALUE "FileDescription", "Rendezvous Browser for Windows" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "Rendezvous Browser for Windows" + VALUE "LegalCopyright", "Copyright (C) Apple Computer, Inc. 2001" + VALUE "OriginalFilename", "RendezvousBrowser.exe" + VALUE "ProductName", "Rendezvous Browser for Windows" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_CHOOSER_DIALOG_MENU MENU +BEGIN + POPUP "File" + BEGIN + MENUITEM "Close &Window\tCtrl+W", ID_FILE_CLOSE + MENUITEM SEPARATOR + MENUITEM "Exit", ID_FILE_EXIT + END + POPUP "Help" + BEGIN + MENUITEM "About Rendezvous Browser...", ID_HELP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ACCELERATORS +BEGIN + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "W", ID_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 RT_MANIFEST +BEGIN + 0x3f3c, 0x6d78, 0x206c, 0x6576, 0x7372, 0x6f69, 0x3d6e, 0x3122, 0x302e, + 0x2022, 0x6e65, 0x6f63, 0x6964, 0x676e, 0x223d, 0x5455, 0x2d46, 0x2238, + 0x7320, 0x6174, 0x646e, 0x6c61, 0x6e6f, 0x3d65, 0x7922, 0x7365, 0x3f22, + 0x203e, 0x0a0d, 0x613c, 0x7373, 0x6d65, 0x6c62, 0x2079, 0x0a0d, 0x2020, + 0x7820, 0x6c6d, 0x736e, 0x223d, 0x7275, 0x3a6e, 0x6373, 0x6568, 0x616d, + 0x2d73, 0x696d, 0x7263, 0x736f, 0x666f, 0x2d74, 0x6f63, 0x3a6d, 0x7361, + 0x2e6d, 0x3176, 0x2022, 0x0a0d, 0x2020, 0x6d20, 0x6e61, 0x6669, 0x7365, + 0x5674, 0x7265, 0x6973, 0x6e6f, 0x223d, 0x2e31, 0x2230, 0x0d3e, 0x3c0a, + 0x7361, 0x6573, 0x626d, 0x796c, 0x6449, 0x6e65, 0x6974, 0x7974, 0x0d20, + 0x200a, 0x2020, 0x7020, 0x6f72, 0x6563, 0x7373, 0x726f, 0x7241, 0x6863, + 0x7469, 0x6365, 0x7574, 0x6572, 0x223d, 0x3878, 0x2236, 0x0d20, 0x200a, + 0x2020, 0x7620, 0x7265, 0x6973, 0x6e6f, 0x223d, 0x2e35, 0x2e31, 0x2e30, + 0x2230, 0x0a0d, 0x2020, 0x2020, 0x7974, 0x6570, 0x223d, 0x6977, 0x336e, + 0x2232, 0x0a0d, 0x2020, 0x2020, 0x616e, 0x656d, 0x223d, 0x7041, 0x2e70, + 0x7865, 0x2265, 0x3e2f, 0x0a0d, 0x2020, 0x2020, 0x643c, 0x7365, 0x7263, + 0x7069, 0x6974, 0x6e6f, 0x413e, 0x7269, 0x6f50, 0x7472, 0x4120, 0x6d64, + 0x6e69, 0x5520, 0x6974, 0x696c, 0x7974, 0x2f3c, 0x6564, 0x6373, 0x6972, + 0x7470, 0x6f69, 0x3e6e, 0x0a0d, 0x2020, 0x2020, 0x643c, 0x7065, 0x6e65, + 0x6564, 0x636e, 0x3e79, 0x0a0d, 0x2020, 0x2020, 0x643c, 0x7065, 0x6e65, + 0x6564, 0x746e, 0x7341, 0x6573, 0x626d, 0x796c, 0x0d3e, 0x200a, 0x2020, + 0x3c20, 0x7361, 0x6573, 0x626d, 0x796c, 0x6449, 0x6e65, 0x6974, 0x7974, + 0x0a0d, 0x2020, 0x2020, 0x2020, 0x2020, 0x7420, 0x7079, 0x3d65, 0x7722, + 0x6e69, 0x3233, 0x0d22, 0x200a, 0x2020, 0x2020, 0x2020, 0x2020, 0x616e, + 0x656d, 0x223d, 0x694d, 0x7263, 0x736f, 0x666f, 0x2e74, 0x6957, 0x646e, + 0x776f, 0x2e73, 0x6f43, 0x6d6d, 0x6e6f, 0x432d, 0x6e6f, 0x7274, 0x6c6f, + 0x2273, 0x0a0d, 0x2020, 0x2020, 0x2020, 0x2020, 0x7620, 0x7265, 0x6973, + 0x6e6f, 0x223d, 0x2e36, 0x2e30, 0x2e30, 0x2230, 0x0a0d, 0x2020, 0x2020, + 0x2020, 0x2020, 0x7020, 0x6275, 0x696c, 0x4b63, 0x7965, 0x6f54, 0x656b, + 0x3d6e, 0x3622, 0x3935, 0x6235, 0x3436, 0x3431, 0x6334, 0x6663, 0x6431, + 0x2266, 0x0a0d, 0x2020, 0x2020, 0x2020, 0x2020, 0x6c20, 0x6e61, 0x7567, + 0x6761, 0x3d65, 0x2a22, 0x0d22, 0x200a, 0x2020, 0x2020, 0x2020, 0x2020, + 0x7270, 0x636f, 0x7365, 0x6f73, 0x4172, 0x6372, 0x6968, 0x6574, 0x7463, + 0x7275, 0x3d65, 0x7822, 0x3638, 0x2f22, 0x0d3e, 0x200a, 0x2020, 0x3c20, + 0x642f, 0x7065, 0x6e65, 0x6564, 0x746e, 0x7341, 0x6573, 0x626d, 0x796c, + 0x0d3e, 0x200a, 0x2020, 0x3c20, 0x642f, 0x7065, 0x6e65, 0x6564, 0x636e, + 0x3e79, 0x0a0d, 0x2f3c, 0x7361, 0x6573, 0x626d, 0x796c, 0x0d3e, "\012" +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ABOUTBOX "&About Rendezvous Browser" + IDS_CHOOSER_DOMAIN_COLUMN_NAME "Domains" + IDP_SOCKETS_INIT_FAILED "Windows sockets initialization failed." + IDS_CHOOSER_SERVICE_COLUMN_NAME "Services" + IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME "Name" + IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME "IP Address" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +#include "Resources\Application.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc2 b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc2 new file mode 100644 index 0000000..bf1983f --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc2 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Application.rc2,v $ +Revision 1.1 2003/08/21 02:06:46 bradley +Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. + +Revision 1.2 2003/08/20 07:06:34 bradley +Update to APSL 2.0. Updated change history to match other mDNSResponder files. + +Revision 1.1 2002/09/20 06:12:48 bradley +Rendezvous Browser for Windows + +*/ + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Resource.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Resource.h new file mode 100644 index 0000000..5329fc1 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Resource.h @@ -0,0 +1,45 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Application.rc +// +#define IDS_ABOUTBOX 101 +#define IDS_CHOOSER_DOMAIN_COLUMN_NAME 102 +#define IDP_SOCKETS_INIT_FAILED 103 +#define IDS_CHOOSER_SERVICE_COLUMN_NAME 104 +#define IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME 105 +#define IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME 106 +#define IDC_NAME_TEXT2 124 +#define IDC_INFO_NAME_TEXT 124 +#define IDC_DESCRIPTION_TEXT2 125 +#define IDC_INFO_TEXT_TEXT 125 +#define IDC_IP_TEXT2 126 +#define IDC_INFO_IP_TEXT 126 +#define IDC_IP_TEXT3 127 +#define IDC_INFO_INTERFACE_TEXT 127 +#define IDR_MAIN_ICON 128 +#define IDR_CHOOSER_DIALOG_MENU 136 +#define IDD_CHOOSER_DIALOG 143 +#define IDD_ABOUT_DIALOG 144 +#define IDR_CHOOSER_DIALOG_MENU_ACCELERATORS 146 +#define IDC_CHOOSER_LIST 1000 +#define IDC_SERVICE_LIST2 1001 +#define IDC_SERVICE_LIST 1001 +#define IDC_SERVICE_LIST3 1002 +#define IDC_DOMAIN_LIST 1002 +#define IDC_ABOUT_APP_NAME_TEXT 1105 +#define IDC_ABOUT_APP_VERSION_TEXT 1106 +#define IDC_ABOUT_COPYRIGHT_TEXT 1107 +#define IDC_ABOUT_APP_ICON 1108 +#define ID_FILE_EXIT 32771 +#define ID_HELP_ABOUT 32806 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 164 +#define _APS_NEXT_COMMAND_VALUE 32809 +#define _APS_NEXT_CONTROL_VALUE 1182 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.cpp b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.cpp new file mode 100644 index 0000000..cd8cafb --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: AboutDialog.cpp,v $ +Revision 1.1 2003/08/21 02:06:47 bradley +Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. + +Revision 1.4 2003/08/12 19:56:28 cheshire +Update to APSL 2.0 + +Revision 1.3 2003/07/02 21:20:06 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.2 2002/09/21 20:44:55 zarzycki +Added APSL info + +Revision 1.1 2002/09/20 06:12:49 bradley +Rendezvous Browser for Windows + +*/ + +#include + +#include "stdafx.h" + +#include "AboutDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP(AboutDialog, CDialog) + //{{AFX_MSG_MAP(AboutDialog) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +//=========================================================================================================================== +// AboutDialog +//=========================================================================================================================== + +AboutDialog::AboutDialog(CWnd* pParent /*=NULL*/) + : CDialog(AboutDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(AboutDialog) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + +//=========================================================================================================================== +// OnInitDialog +//=========================================================================================================================== + +BOOL AboutDialog::OnInitDialog() +{ + CDialog::OnInitDialog(); + return( true ); +} + +//=========================================================================================================================== +// DoDataExchange +//=========================================================================================================================== + +void AboutDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(AboutDialog) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.h new file mode 100644 index 0000000..11c9149 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: AboutDialog.h,v $ +Revision 1.1 2003/08/21 02:06:47 bradley +Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. + +Revision 1.4 2003/08/12 19:56:28 cheshire +Update to APSL 2.0 + +Revision 1.3 2003/07/02 21:20:06 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.2 2002/09/21 20:44:55 zarzycki +Added APSL info + +Revision 1.1 2002/09/20 06:12:50 bradley +Rendezvous Browser for Windows + +*/ + +#if !defined(AFX_ABOUTDIALOG_H__4B8A04B2_9735_4F4A_AFCA_15F85FB3D763__INCLUDED_) +#define AFX_ABOUTDIALOG_H__4B8A04B2_9735_4F4A_AFCA_15F85FB3D763__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "Resource.h" + +//=========================================================================================================================== +// AboutDialog +//=========================================================================================================================== + +class AboutDialog : public CDialog +{ + public: + + // Creation/Deletion + + AboutDialog(CWnd* pParent = NULL); // standard constructor + + //{{AFX_DATA(AboutDialog) + enum { IDD = IDD_ABOUT_DIALOG }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(AboutDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + protected: + + // Generated message map functions + //{{AFX_MSG(AboutDialog) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ABOUTDIALOG_H__4B8A04B2_9735_4F4A_AFCA_15F85FB3D763__INCLUDED_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.cpp b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.cpp new file mode 100644 index 0000000..396b7e9 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Application.cpp,v $ +Revision 1.1 2003/08/21 02:06:47 bradley +Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. + +Revision 1.5 2003/08/12 19:56:28 cheshire +Update to APSL 2.0 + +Revision 1.4 2003/07/02 21:20:06 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.3 2002/09/21 20:44:55 zarzycki +Added APSL info + +Revision 1.2 2002/09/20 08:37:34 bradley +Increased the DNS record cache from the default of 64 to 512 entries for larger networks. + +Revision 1.1 2002/09/20 06:12:51 bradley +Rendezvous Browser for Windows + +*/ + +#include + +#include "DNSServices.h" + +#include "Application.h" + +#include "ChooserDialog.h" + +#include "stdafx.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP(Application, CWinApp) + //{{AFX_MSG_MAP(Application) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG + ON_COMMAND(ID_HELP, CWinApp::OnHelp) +END_MESSAGE_MAP() + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +Application gApp; + +//=========================================================================================================================== +// Application +//=========================================================================================================================== + +Application::Application( void ) +{ + // +} + +//=========================================================================================================================== +// InitInstance +//=========================================================================================================================== + +BOOL Application::InitInstance() +{ + DNSStatus err; + + // WinSock initialization. + + if( !AfxSocketInit() ) + { + AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); + return( FALSE ); + } + + // Standard MFC initialization. + +#if( !defined( AFX_DEPRECATED ) ) + #ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL + #else + Enable3dControlsStatic(); // Call this when linking to MFC statically + #endif +#endif + + InitCommonControls(); + + // Set up DNS Services. + + err = DNSServicesInitialize( 0, 512 ); + assert( err == kDNSNoErr ); + + // Create the chooser dialog. + + ChooserDialog * dialog; + + m_pMainWnd = NULL; + dialog = new ChooserDialog; + dialog->Create( IDD_CHOOSER_DIALOG ); + m_pMainWnd = dialog; + dialog->ShowWindow( SW_SHOW ); + + return( true ); +} + +//=========================================================================================================================== +// ExitInstance +//=========================================================================================================================== + +int Application::ExitInstance( void ) +{ + // Clean up DNS Services. + + DNSServicesFinalize(); + return( 0 ); +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.h new file mode 100644 index 0000000..e174e4f --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Application.h,v $ +Revision 1.1 2003/08/21 02:06:47 bradley +Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. + +Revision 1.4 2003/08/12 19:56:28 cheshire +Update to APSL 2.0 + +Revision 1.3 2003/07/02 21:20:06 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.2 2002/09/21 20:44:55 zarzycki +Added APSL info + +Revision 1.1 2002/09/20 06:12:51 bradley +Rendezvous Browser for Windows + +*/ + +#if !defined(AFX_ADMIN_H__8663733F_6A15_439F_B568_F5A0125CD572__INCLUDED_) +#define AFX_ADMIN_H__8663733F_6A15_439F_B568_F5A0125CD572__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "stdafx.h" + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "Resource.h" + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +extern class Application gApp; + +//=========================================================================================================================== +// Application +//=========================================================================================================================== + +class Application : public CWinApp +{ + public: + + // Creation/Deletion + + Application(); + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(Application) + public: + virtual BOOL InitInstance(); + virtual int ExitInstance( void ); + //}}AFX_VIRTUAL + + //{{AFX_MSG(Application) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ADMIN_H__8663733F_6A15_439F_B568_F5A0125CD572__INCLUDED_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.cpp b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.cpp new file mode 100644 index 0000000..ddd81f9 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.cpp @@ -0,0 +1,1171 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ChooserDialog.cpp,v $ +Revision 1.1 2003/08/21 02:06:47 bradley +Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. + +Revision 1.7 2003/08/20 06:45:56 bradley +Updated for IP address changes in DNSServices. Added support for browsing for Xserve RAID. + +Revision 1.6 2003/08/12 19:56:28 cheshire +Update to APSL 2.0 + +Revision 1.5 2003/07/13 01:03:55 cheshire +Diffs provided by Bob Bradley to provide provide proper display of Unicode names + +Revision 1.4 2003/07/02 21:20:06 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.3 2002/09/21 20:44:55 zarzycki +Added APSL info + +Revision 1.2 2002/09/20 08:39:21 bradley +Make sure each resolved item matches the selected service type to handle resolved that may have +been queued up on the Windows Message Loop. Reduce column to fit when scrollbar is present. + +Revision 1.1 2002/09/20 06:12:52 bradley +Rendezvous Browser for Windows + +*/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "stdafx.h" + +#include "DNSServices.h" + +#include "Application.h" +#include "AboutDialog.h" +#include "Resource.h" + +#include "ChooserDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +// Menus + +enum +{ + kChooserMenuIndexFile = 0, + kChooserMenuIndexHelp = 1 +}; + +// Domain List + +#define kDomainListDefaultDomainColumnIndex 0 +#define kDomainListDefaultDomainColumnWidth 140 + +// Service List + +#define kServiceListDefaultServiceColumnIndex 0 +#define kServiceListDefaultServiceColumnWidth 140 + +// Chooser List + +#define kChooserListDefaultNameColumnIndex 0 +#define kChooserListDefaultNameColumnWidth 162 + +#define kChooserListDefaultIPColumnIndex 1 +#define kChooserListDefaultIPColumnWidth 126 + +// Windows User Messages + +#define WM_USER_DOMAIN_ADD ( WM_USER + 0x100 ) +#define WM_USER_DOMAIN_REMOVE ( WM_USER + 0x101 ) +#define WM_USER_SERVICE_ADD ( WM_USER + 0x102 ) +#define WM_USER_SERVICE_REMOVE ( WM_USER + 0x103 ) +#define WM_USER_RESOLVE ( WM_USER + 0x104 ) + +#if 0 +#pragma mark == Constants - Service Table == +#endif + +//=========================================================================================================================== +// Constants - Service Table +//=========================================================================================================================== + +struct KnownServiceEntry +{ + const char * serviceType; + const char * description; + const char * urlScheme; + bool useText; +}; + +static const KnownServiceEntry kKnownServiceTable[] = +{ + { "_airport._tcp.", "AirPort Base Station", "acp://", false }, + { "_afpovertcp._tcp.", "AppleShare Server", "afp://", false }, + { "_ftp._tcp.", "File Transfer (FTP)", "ftp://", false }, + { "_ichat._tcp.", "iChat", "ichat://", false }, + { "_printer._tcp.", "Printer (LPD)", "ldp://", false }, + { "_eppc._tcp.", "Remote AppleEvents", "eppc://", false }, + { "_ssh._tcp.", "Secure Shell (SSH)", "ssh://", false }, + { "_tftp._tcp.", "Trivial File Transfer (TFTP)", "tftp://", false }, + { "_http._tcp.", "Web Server (HTTP)", "http://", true }, + { "_smb._tcp.", "Windows File Sharing", "smb://", false }, + { "_xserveraid._tcp.", "Xserve RAID", "xsr://", false }, + { NULL, NULL, NULL, false }, +}; + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +struct DomainEventInfo +{ + DNSBrowserEventType eventType; + CString domain; + DNSNetworkAddress ifIP; +}; + +struct ServiceEventInfo +{ + DNSBrowserEventType eventType; + std::string name; + std::string type; + std::string domain; + DNSNetworkAddress ifIP; +}; + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +static void + BrowserCallBack( + void * inContext, + DNSBrowserRef inRef, + DNSStatus inStatusCode, + const DNSBrowserEvent * inEvent ); + +static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString ); + +static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject ); + +#if 0 +#pragma mark == Message Map == +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP(ChooserDialog, CDialog) + //{{AFX_MSG_MAP(ChooserDialog) + ON_WM_SYSCOMMAND() + ON_NOTIFY(LVN_ITEMCHANGED, IDC_DOMAIN_LIST, OnDomainListChanged) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_SERVICE_LIST, OnServiceListChanged) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHOOSER_LIST, OnChooserListChanged) + ON_NOTIFY(NM_DBLCLK, IDC_CHOOSER_LIST, OnChooserListDoubleClick) + ON_COMMAND(ID_HELP_ABOUT, OnAbout) + ON_WM_INITMENUPOPUP() + ON_WM_ACTIVATE() + ON_COMMAND(ID_FILE_CLOSE, OnFileClose) + ON_COMMAND(ID_FILE_EXIT, OnExit) + ON_WM_CLOSE() + ON_WM_NCDESTROY() + //}}AFX_MSG_MAP + ON_MESSAGE( WM_USER_DOMAIN_ADD, OnDomainAdd ) + ON_MESSAGE( WM_USER_DOMAIN_REMOVE, OnDomainRemove ) + ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd ) + ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove ) + ON_MESSAGE( WM_USER_RESOLVE, OnResolve ) +END_MESSAGE_MAP() + +#if 0 +#pragma mark == Routines == +#endif + +//=========================================================================================================================== +// ChooserDialog +//=========================================================================================================================== + +ChooserDialog::ChooserDialog( CWnd *inParent ) + : CDialog( ChooserDialog::IDD, inParent) +{ + //{{AFX_DATA_INIT(ChooserDialog) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + + // Load menu accelerator table. + + mMenuAcceleratorTable = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ) ); + assert( mMenuAcceleratorTable ); + + mBrowser = NULL; + mIsServiceBrowsing = false; +} + +//=========================================================================================================================== +// ~ChooserDialog +//=========================================================================================================================== + +ChooserDialog::~ChooserDialog( void ) +{ + if( mBrowser ) + { + DNSStatus err; + + err = DNSBrowserRelease( mBrowser, 0 ); + assert( err == kDNSNoErr ); + } +} + +//=========================================================================================================================== +// DoDataExchange +//=========================================================================================================================== + +void ChooserDialog::DoDataExchange( CDataExchange *pDX ) +{ + CDialog::DoDataExchange(pDX); + + //{{AFX_DATA_MAP(ChooserDialog) + DDX_Control(pDX, IDC_SERVICE_LIST, mServiceList); + DDX_Control(pDX, IDC_DOMAIN_LIST, mDomainList); + DDX_Control(pDX, IDC_CHOOSER_LIST, mChooserList); + //}}AFX_DATA_MAP +} + +//=========================================================================================================================== +// OnInitDialog +//=========================================================================================================================== + +BOOL ChooserDialog::OnInitDialog( void ) +{ + BOOL result; + CString tempString; + DNSStatus err; + + // Initialize our parent. + + CDialog::OnInitDialog(); + + // Set up the Domain List. + + result = tempString.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME ); + assert( result ); + mDomainList.InsertColumn( 0, tempString, LVCFMT_LEFT, kDomainListDefaultDomainColumnWidth ); + + // Set up the Service List. + + result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_NAME ); + assert( result ); + mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnWidth ); + + PopulateServicesList(); + + // Set up the Chooser List. + + result = tempString.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME ); + assert( result ); + mChooserList.InsertColumn( 0, tempString, LVCFMT_LEFT, kChooserListDefaultNameColumnWidth ); + + result = tempString.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME ); + assert( result ); + mChooserList.InsertColumn( 1, tempString, LVCFMT_LEFT, kChooserListDefaultIPColumnWidth ); + + // Set up the other controls. + + UpdateInfoDisplay(); + + // Start browsing for domains. + + err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser ); + assert( err == kDNSNoErr ); + + err = DNSBrowserStartDomainSearch( mBrowser, 0 ); + assert( err == kDNSNoErr ); + + return( true ); +} + +//=========================================================================================================================== +// OnFileClose +//=========================================================================================================================== + +void ChooserDialog::OnFileClose() +{ + OnClose(); +} + +//=========================================================================================================================== +// OnActivate +//=========================================================================================================================== + +void ChooserDialog::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized ) +{ + // Always make the active window the "main" window so modal dialogs work better and the app quits after closing + // the last window. + + gApp.m_pMainWnd = this; + + CDialog::OnActivate(nState, pWndOther, bMinimized); +} + +//=========================================================================================================================== +// PostNcDestroy +//=========================================================================================================================== + +void ChooserDialog::PostNcDestroy() +{ + // Call the base class to do the normal cleanup. + + delete this; +} + +//=========================================================================================================================== +// PreTranslateMessage +//=========================================================================================================================== + +BOOL ChooserDialog::PreTranslateMessage(MSG* pMsg) +{ + BOOL result; + + result = false; + assert( mMenuAcceleratorTable ); + if( mMenuAcceleratorTable ) + { + result = ::TranslateAccelerator( m_hWnd, mMenuAcceleratorTable, pMsg ); + } + if( !result ) + { + result = CDialog::PreTranslateMessage( pMsg ); + } + return( result ); +} + +//=========================================================================================================================== +// OnInitMenuPopup +//=========================================================================================================================== + +void ChooserDialog::OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu ) +{ + CDialog::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu ); + + switch( nIndex ) + { + case kChooserMenuIndexFile: + break; + + case kChooserMenuIndexHelp: + break; + + default: + break; + } +} + +//=========================================================================================================================== +// OnExit +//=========================================================================================================================== + +void ChooserDialog::OnExit() +{ + AfxPostQuitMessage( 0 ); +} + +//=========================================================================================================================== +// OnAbout +//=========================================================================================================================== + +void ChooserDialog::OnAbout() +{ + AboutDialog dialog; + + dialog.DoModal(); +} + +//=========================================================================================================================== +// OnSysCommand +//=========================================================================================================================== + +void ChooserDialog::OnSysCommand( UINT inID, LPARAM inParam ) +{ + CDialog::OnSysCommand( inID, inParam ); +} + +//=========================================================================================================================== +// OnClose +//=========================================================================================================================== + +void ChooserDialog::OnClose() +{ + StopBrowsing(); + + gApp.m_pMainWnd = this; + DestroyWindow(); +} + +//=========================================================================================================================== +// OnNcDestroy +//=========================================================================================================================== + +void ChooserDialog::OnNcDestroy() +{ + gApp.m_pMainWnd = this; + + CDialog::OnNcDestroy(); +} + +//=========================================================================================================================== +// OnDomainListChanged +//=========================================================================================================================== + +void ChooserDialog::OnDomainListChanged( NMHDR *pNMHDR, LRESULT *pResult ) +{ + UNUSED_ALWAYS( pNMHDR ); + + // Domain list changes have similar effects to service list changes so reuse that code path by calling it here. + + OnServiceListChanged( NULL, NULL ); + + *pResult = 0; +} + +//=========================================================================================================================== +// OnServiceListChanged +//=========================================================================================================================== + +void ChooserDialog::OnServiceListChanged( NMHDR *pNMHDR, LRESULT *pResult ) +{ + int selectedType; + int selectedDomain; + + UNUSED_ALWAYS( pNMHDR ); + + // Stop any existing service search. + + StopBrowsing(); + + // If a domain and service type are selected, start searching for the service type on the domain. + + selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED ); + selectedDomain = mDomainList.GetNextItem( -1, LVNI_SELECTED ); + + if( ( selectedType >= 0 ) && ( selectedDomain >= 0 ) ) + { + CString type; + CString domain; + + type = mServiceTypes[ selectedType ].serviceType.c_str(); + domain = mDomainList.GetItemText( selectedDomain, 0 ); + + StartBrowsing( type, domain ); + } + + if( pResult ) + { + *pResult = 0; + } +} + +//=========================================================================================================================== +// OnChooserListChanged +//=========================================================================================================================== + +void ChooserDialog::OnChooserListChanged( NMHDR *pNMHDR, LRESULT *pResult ) +{ + UNUSED_ALWAYS( pNMHDR ); + + UpdateInfoDisplay(); + *pResult = 0; +} + +//=========================================================================================================================== +// OnChooserListDoubleClick +//=========================================================================================================================== + +void ChooserDialog::OnChooserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult ) +{ + int selectedItem; + + UNUSED_ALWAYS( pNMHDR ); + + // Display the service instance if it is selected. Otherwise, clear all the info. + + selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED ); + if( selectedItem >= 0 ) + { + ServiceInstanceInfo * p; + CString url; + const KnownServiceEntry * service; + + assert( selectedItem < (int) mServiceInstances.size() ); + p = &mServiceInstances[ selectedItem ]; + + // Search for a known service type entry that matches. + + for( service = kKnownServiceTable; service->serviceType; ++service ) + { + if( p->type == service->serviceType ) + { + break; + } + } + if( service->serviceType ) + { + // Create a URL representing the service instance. Special case for SMB (no port number). + + if( strcmp( service->serviceType, "_smb._tcp" ) == 0 ) + { + url.Format( "%s%s/", service->urlScheme, (const char *) p->ip.c_str() ); + } + else + { + const char * text; + + text = service->useText ? p->text.c_str() : ""; + url.Format( "%s%s/%s", service->urlScheme, (const char *) p->ip.c_str(), text ); + } + + // Let the system open the URL in the correct app. + + ShellExecute( NULL, "open", url, "", "c:\\", SW_SHOWNORMAL ); + } + } + *pResult = 0; +} + +//=========================================================================================================================== +// OnCancel +//=========================================================================================================================== + +void ChooserDialog::OnCancel() +{ + // Do nothing. +} + +//=========================================================================================================================== +// PopulateServicesList +//=========================================================================================================================== + +void ChooserDialog::PopulateServicesList( void ) +{ + ServiceTypeVector::iterator i; + CString name; + + // Add a fixed list of known services. + + if( mServiceTypes.empty() ) + { + const KnownServiceEntry * service; + + for( service = kKnownServiceTable; service->serviceType; ++service ) + { + ServiceTypeInfo info; + + info.serviceType = service->serviceType; + info.description = service->description; + info.urlScheme = service->urlScheme; + mServiceTypes.push_back( info ); + } + } + + // Add each service to the list. + + for( i = mServiceTypes.begin(); i != mServiceTypes.end(); ++i ) + { + UTF8StringToStringObject( ( *i ).description.c_str(), name ); + mServiceList.InsertItem( mServiceList.GetItemCount(), name ); + } + + // Select the first service type by default. + + if( !mServiceTypes.empty() ) + { + mServiceList.SetItemState( 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); + } +} + +//=========================================================================================================================== +// UpdateInfoDisplay +//=========================================================================================================================== + +void ChooserDialog::UpdateInfoDisplay( void ) +{ + int selectedItem; + std::string name; + CString s; + std::string ip; + std::string ifIP; + std::string text; + CWnd * item; + + // Display the service instance if it is selected. Otherwise, clear all the info. + + selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED ); + if( selectedItem >= 0 ) + { + ServiceInstanceInfo * p; + + assert( selectedItem < (int) mServiceInstances.size() ); + p = &mServiceInstances[ selectedItem ]; + + name = p->name; + ip = p->ip; + ifIP = p->ifIP; + text = p->text; + + // Sync up the list items with the actual data (IP address may change). + + mChooserList.SetItemText( selectedItem, 1, ip.c_str() ); + } + + // Name + + item = (CWnd *) this->GetDlgItem( IDC_INFO_NAME_TEXT ); + assert( item ); + UTF8StringToStringObject( name.c_str(), s ); + item->SetWindowText( s ); + + // IP + + item = (CWnd *) this->GetDlgItem( IDC_INFO_IP_TEXT ); + assert( item ); + item->SetWindowText( ip.c_str() ); + + // Interface + + item = (CWnd *) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT ); + assert( item ); + item->SetWindowText( ifIP.c_str() ); + + // Text + + if( text.size() > 255 ) + { + text.resize( 255 ); + } + item = (CWnd *) this->GetDlgItem( IDC_INFO_TEXT_TEXT ); + assert( item ); + item->SetWindowText( text.c_str() ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// OnDomainAdd +//=========================================================================================================================== + +LONG ChooserDialog::OnDomainAdd( WPARAM inWParam, LPARAM inLParam ) +{ + DomainEventInfo * p; + std::auto_ptr < DomainEventInfo > pAutoPtr; + int n; + int i; + CString domain; + CString s; + bool found; + + UNUSED_ALWAYS( inWParam ); + + assert( inLParam ); + p = reinterpret_cast ( inLParam ); + pAutoPtr.reset( p ); + + // Search to see if we already know about this domain. If not, add it to the list. + + found = false; + domain = p->domain; + n = mDomainList.GetItemCount(); + for( i = 0; i < n; ++i ) + { + s = mDomainList.GetItemText( i, 0 ); + if( s == domain ) + { + found = true; + break; + } + } + if( !found ) + { + int selectedItem; + + mDomainList.InsertItem( n, domain ); + + // If no domains are selected and the domain being added is a default domain, select it. + + selectedItem = mDomainList.GetNextItem( -1, LVNI_SELECTED ); + if( ( selectedItem < 0 ) && ( p->eventType == kDNSBrowserEventTypeAddDefaultDomain ) ) + { + mDomainList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); + } + } + return( 0 ); +} + +//=========================================================================================================================== +// OnDomainRemove +//=========================================================================================================================== + +LONG ChooserDialog::OnDomainRemove( WPARAM inWParam, LPARAM inLParam ) +{ + DomainEventInfo * p; + std::auto_ptr < DomainEventInfo > pAutoPtr; + int n; + int i; + CString domain; + CString s; + bool found; + + UNUSED_ALWAYS( inWParam ); + + assert( inLParam ); + p = reinterpret_cast ( inLParam ); + pAutoPtr.reset( p ); + + // Search to see if we know about this domain. If so, remove it from the list. + + found = false; + domain = p->domain; + n = mDomainList.GetItemCount(); + for( i = 0; i < n; ++i ) + { + s = mDomainList.GetItemText( i, 0 ); + if( s == domain ) + { + found = true; + break; + } + } + if( found ) + { + mDomainList.DeleteItem( i ); + } + return( 0 ); +} + +//=========================================================================================================================== +// OnServiceAdd +//=========================================================================================================================== + +LONG ChooserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam ) +{ + ServiceEventInfo * p; + std::auto_ptr < ServiceEventInfo > pAutoPtr; + + UNUSED_ALWAYS( inWParam ); + + assert( inLParam ); + p = reinterpret_cast ( inLParam ); + pAutoPtr.reset( p ); + + return( 0 ); +} + +//=========================================================================================================================== +// OnServiceRemove +//=========================================================================================================================== + +LONG ChooserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam ) +{ + ServiceEventInfo * p; + std::auto_ptr < ServiceEventInfo > pAutoPtr; + bool found; + int n; + int i; + + UNUSED_ALWAYS( inWParam ); + + assert( inLParam ); + p = reinterpret_cast ( inLParam ); + pAutoPtr.reset( p ); + + // Search to see if we know about this service instance. If so, remove it from the list. + + found = false; + n = (int) mServiceInstances.size(); + for( i = 0; i < n; ++i ) + { + ServiceInstanceInfo * q; + + // If the name, type, domain, and interface match, treat it as the same service instance. + + q = &mServiceInstances[ i ]; + if( ( p->name == q->name ) && + ( p->type == q->type ) && + ( p->domain == q->domain ) ) + { + found = true; + break; + } + } + if( found ) + { + mChooserList.DeleteItem( i ); + assert( i < (int) mServiceInstances.size() ); + mServiceInstances.erase( mServiceInstances.begin() + i ); + } + return( 0 ); +} + +//=========================================================================================================================== +// OnResolve +//=========================================================================================================================== + +LONG ChooserDialog::OnResolve( WPARAM inWParam, LPARAM inLParam ) +{ + ServiceInstanceInfo * p; + std::auto_ptr < ServiceInstanceInfo > pAutoPtr; + int selectedType; + int n; + int i; + bool found; + + UNUSED_ALWAYS( inWParam ); + + assert( inLParam ); + p = reinterpret_cast ( inLParam ); + pAutoPtr.reset( p ); + + // Make sure it is for an item of the correct type. This handles any resolves that may have been queued up. + + selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED ); + assert( selectedType >= 0 ); + if( selectedType >= 0 ) + { + assert( selectedType <= (int) mServiceTypes.size() ); + if( p->type != mServiceTypes[ selectedType ].serviceType ) + { + goto exit; + } + } + + // Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list. + + found = false; + n = (int) mServiceInstances.size(); + for( i = 0; i < n; ++i ) + { + ServiceInstanceInfo * q; + + // If the name, type, domain, and interface matches, treat it as the same service instance. + + q = &mServiceInstances[ i ]; + if( ( p->name == q->name ) && + ( p->type == q->type ) && + ( p->domain == q->domain ) && + ( p->ifIP == q->ifIP ) ) + { + found = true; + break; + } + } + if( found ) + { + mServiceInstances[ i ] = *p; + } + else + { + CString s; + + mServiceInstances.push_back( *p ); + UTF8StringToStringObject( p->name.c_str(), s ); + mChooserList.InsertItem( n, s ); + mChooserList.SetItemText( n, 1, p->ip.c_str() ); + + // If this is the only item, select it. + + if( n == 0 ) + { + mChooserList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); + } + } + UpdateInfoDisplay(); + +exit: + return( 0 ); +} + +//=========================================================================================================================== +// StartBrowsing +//=========================================================================================================================== + +void ChooserDialog::StartBrowsing( const char *inType, const char *inDomain ) +{ + DNSStatus err; + + assert( mServiceInstances.empty() ); + assert( mChooserList.GetItemCount() == 0 ); + assert( !mIsServiceBrowsing ); + + mChooserList.DeleteAllItems(); + mServiceInstances.clear(); + + mIsServiceBrowsing = true; + err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, inType, inDomain ); + assert( err == kDNSNoErr ); +} + +//=========================================================================================================================== +// StopBrowsing +//=========================================================================================================================== + +void ChooserDialog::StopBrowsing( void ) +{ + // If searching, stop. + + if( mIsServiceBrowsing ) + { + DNSStatus err; + + mIsServiceBrowsing = false; + err = DNSBrowserStopServiceSearch( mBrowser, 0 ); + assert( err == kDNSNoErr ); + } + + // Remove all service instances. + + mChooserList.DeleteAllItems(); + assert( mChooserList.GetItemCount() == 0 ); + mServiceInstances.clear(); + assert( mServiceInstances.empty() ); + UpdateInfoDisplay(); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// BrowserCallBack +//=========================================================================================================================== + +static void + BrowserCallBack( + void * inContext, + DNSBrowserRef inRef, + DNSStatus inStatusCode, + const DNSBrowserEvent * inEvent ) +{ + ChooserDialog * dialog; + UINT message; + BOOL posted; + + UNUSED_ALWAYS( inStatusCode ); + UNUSED_ALWAYS( inRef ); + + // Check parameters. + + assert( inContext ); + dialog = reinterpret_cast ( inContext ); + + try + { + switch( inEvent->type ) + { + case kDNSBrowserEventTypeRelease: + break; + + // Domains + + case kDNSBrowserEventTypeAddDomain: + case kDNSBrowserEventTypeAddDefaultDomain: + case kDNSBrowserEventTypeRemoveDomain: + { + DomainEventInfo * domain; + std::auto_ptr < DomainEventInfo > domainAutoPtr; + + domain = new DomainEventInfo; + domainAutoPtr.reset( domain ); + + domain->eventType = inEvent->type; + domain->domain = inEvent->data.addDomain.domain; + domain->ifIP = inEvent->data.addDomain.interfaceIP; + + message = ( inEvent->type == kDNSBrowserEventTypeRemoveDomain ) ? WM_USER_DOMAIN_REMOVE : WM_USER_DOMAIN_ADD; + posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) domain ); + assert( posted ); + if( posted ) + { + domainAutoPtr.release(); + } + break; + } + + // Services + + case kDNSBrowserEventTypeAddService: + case kDNSBrowserEventTypeRemoveService: + { + ServiceEventInfo * service; + std::auto_ptr < ServiceEventInfo > serviceAutoPtr; + + service = new ServiceEventInfo; + serviceAutoPtr.reset( service ); + + service->eventType = inEvent->type; + service->name = inEvent->data.addService.name; + service->type = inEvent->data.addService.type; + service->domain = inEvent->data.addService.domain; + service->ifIP = inEvent->data.addService.interfaceIP; + + message = ( inEvent->type == kDNSBrowserEventTypeAddService ) ? WM_USER_SERVICE_ADD : WM_USER_SERVICE_REMOVE; + posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) service ); + assert( posted ); + if( posted ) + { + serviceAutoPtr.release(); + } + break; + } + + // Resolves + + case kDNSBrowserEventTypeResolved: + { + ServiceInstanceInfo * serviceInstance; + std::auto_ptr < ServiceInstanceInfo > serviceInstanceAutoPtr; + char s[ 32 ]; + + serviceInstance = new ServiceInstanceInfo; + serviceInstanceAutoPtr.reset( serviceInstance ); + + serviceInstance->name = inEvent->data.resolved->name; + serviceInstance->type = inEvent->data.resolved->type; + serviceInstance->domain = inEvent->data.resolved->domain; + serviceInstance->ip = DNSNetworkAddressToString( &inEvent->data.resolved->address, s ); + serviceInstance->ifIP = DNSNetworkAddressToString( &inEvent->data.resolved->interfaceIP, s ); + serviceInstance->text = inEvent->data.resolved->textRecord; + + posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_RESOLVE, 0, (LPARAM) serviceInstance ); + assert( posted ); + if( posted ) + { + serviceInstanceAutoPtr.release(); + } + break; + } + + default: + break; + } + } + catch( ... ) + { + // Don't let exceptions escape. + } +} + +//=========================================================================================================================== +// DNSNetworkAddressToString +// +// Note: Currently only supports IPv4 network addresses. +//=========================================================================================================================== + +static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString ) +{ + const DNSUInt8 * p; + DNSUInt16 port; + + p = inAddr->u.ipv4.addr.v8; + port = ntohs( inAddr->u.ipv4.port.v16 ); + if( port != kDNSPortInvalid ) + { + sprintf( outString, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port ); + } + else + { + sprintf( outString, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] ); + } + return( outString ); +} + +//=========================================================================================================================== +// UTF8StringToStringObject +//=========================================================================================================================== + +static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject ) +{ + DWORD err; + int n; + BSTR unicode; + + unicode = NULL; + + n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 ); + if( n > 0 ) + { + unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) ); + if( !unicode ) + { + err = ERROR_INSUFFICIENT_BUFFER; + goto exit; + } + + n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n ); + try + { + inObject = unicode; + } + catch( ... ) + { + err = ERROR_NO_UNICODE_TRANSLATION; + goto exit; + } + } + else + { + inObject = ""; + } + err = 0; + +exit: + if( unicode ) + { + free( unicode ); + } + return( err ); +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.h new file mode 100644 index 0000000..7acb0b5 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ChooserDialog.h,v $ +Revision 1.1 2003/08/21 02:06:47 bradley +Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. + +Revision 1.4 2003/08/12 19:56:28 cheshire +Update to APSL 2.0 + +Revision 1.3 2003/07/02 21:20:06 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.2 2002/09/21 20:44:55 zarzycki +Added APSL info + +Revision 1.1 2002/09/20 06:12:52 bradley +Rendezvous Browser for Windows + +*/ + +#if !defined(AFX_CHOOSERDIALOG_H__AC258704_B307_4901_9F98_A0AC022FD8AC__INCLUDED_) +#define AFX_CHOOSERDIALOG_H__AC258704_B307_4901_9F98_A0AC022FD8AC__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include +#include + +#include "afxcmn.h" + +#include "Resource.h" + +#include "DNSServices.h" + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +struct ServiceInstanceInfo +{ + std::string name; + std::string type; + std::string domain; + std::string ip; + std::string text; + std::string ifIP; +}; + +struct ServiceTypeInfo +{ + std::string serviceType; + std::string description; + std::string urlScheme; +}; + +//=========================================================================================================================== +// ChooserDialog +//=========================================================================================================================== + +class ChooserDialog : public CDialog +{ + public: + + ChooserDialog(CWnd* pParent = NULL); + virtual ~ChooserDialog( void ); + + //{{AFX_DATA(ChooserDialog) + enum { IDD = IDD_CHOOSER_DIALOG }; + CListCtrl mServiceList; + CListCtrl mDomainList; + CListCtrl mChooserList; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ChooserDialog) + public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void PostNcDestroy(); + //}}AFX_VIRTUAL + + protected: + + typedef std::vector < ServiceInstanceInfo > ServiceInstanceVector; + typedef std::vector < ServiceTypeInfo > ServiceTypeVector; + + HACCEL mMenuAcceleratorTable; + DNSBrowserRef mBrowser; + BOOL mIsServiceBrowsing; + ServiceInstanceVector mServiceInstances; + ServiceTypeVector mServiceTypes; + + public: + + void PopulateServicesList( void ); + void UpdateInfoDisplay( void ); + + void StartBrowsing( const char *inType, const char *inDomain ); + void StopBrowsing( void ); + + protected: + + //{{AFX_MSG(ChooserDialog) + virtual BOOL OnInitDialog(); + afx_msg void OnSysCommand(UINT nID, LPARAM lParam); + afx_msg void OnDomainListChanged(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnServiceListChanged(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnChooserListChanged(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnChooserListDoubleClick(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnAbout(); + afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu); + afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); + afx_msg void OnFileClose(); + virtual void OnCancel(); + afx_msg void OnExit(); + afx_msg void OnClose(); + afx_msg void OnNcDestroy(); + //}}AFX_MSG + afx_msg LONG OnDomainAdd( WPARAM inWParam, LPARAM inLParam ); + afx_msg LONG OnDomainRemove( WPARAM inWParam, LPARAM inLParam ); + afx_msg LONG OnServiceAdd( WPARAM inWParam, LPARAM inLParam ); + afx_msg LONG OnServiceRemove( WPARAM inWParam, LPARAM inLParam ); + afx_msg LONG OnResolve( WPARAM inWParam, LPARAM inLParam ); + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CHOOSERDIALOG_H__AC258704_B307_4901_9F98_A0AC022FD8AC__INCLUDED_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.cpp b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.cpp new file mode 100644 index 0000000..a26a1e7 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: StdAfx.cpp,v $ +Revision 1.1 2003/08/21 02:06:47 bradley +Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. + +Revision 1.4 2003/08/12 19:56:28 cheshire +Update to APSL 2.0 + +Revision 1.3 2003/07/02 21:20:06 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.2 2002/09/21 20:44:55 zarzycki +Added APSL info + +Revision 1.1 2002/09/20 06:12:53 bradley +Rendezvous Browser for Windows + +*/ + +#include "stdafx.h" diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.h b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.h new file mode 100644 index 0000000..3c16ff7 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: StdAfx.h,v $ +Revision 1.1 2003/08/21 02:06:47 bradley +Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. + +Revision 1.4 2003/08/12 19:56:28 cheshire +Update to APSL 2.0 + +Revision 1.3 2003/07/02 21:20:06 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.2 2002/09/21 20:44:56 zarzycki +Added APSL info + +Revision 1.1 2002/09/20 06:12:53 bradley +Rendezvous Browser for Windows + +*/ + +#if !defined(AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_) +#define AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + +#include // MFC socket extensions + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#include + +#include "DNSServices.h" + +#include "Application.h" + +#include "ChooserDialog.h" + +#endif // !defined(AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcc b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcc new file mode 100644 index 0000000..22f443d --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcc @@ -0,0 +1,37 @@ +; CLW file contains information for the MFC ClassWizard + +[General Info] +Version=1 +LastClass=BrowserDialog +LastTemplate=CDialog +NewFileInclude1=#include "stdafx.h" +NewFileInclude2=#include "Application.h" + +ClassCount=3 +Class1=Application +Class2=BrowserDialog + +ResourceCount=3 +Resource2=IDR_MAINFRAME +Resource3=IDD_APPLICATION_DIALOG + +[CLS:Application] +Type=0 +HeaderFile=Application.h +ImplementationFile=Application.cpp +Filter=N + +[CLS:BrowserDialog] +Type=0 +HeaderFile=BrowserDialog.h +ImplementationFile=BrowserDialog.cpp +Filter=D + + +[DLG:IDD_APPLICATION_DIALOG] +Type=1 +ControlCount=3 +Control1=IDOK,button,1342242817 +Control2=IDCANCEL,button,1342242816 +Control3=IDC_STATIC,static,1342308352 +Class=BrowserDialog diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcp b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcp new file mode 100644 index 0000000..7f62479 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcp @@ -0,0 +1,499 @@ +# Microsoft eMbedded Visual Tools Project File - Name="Application" - Package Owner=<4> +# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (WCE ARMV4) Application" 0xa301 +# TARGTYPE "Win32 (WCE emulator) Application" 0xa601 + +CFG=Application - Win32 (WCE emulator) Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Application.vcn". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Application.vcn" CFG="Application - Win32 (WCE emulator) Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Application - Win32 (WCE emulator) Release" (based on "Win32 (WCE emulator) Application") +!MESSAGE "Application - Win32 (WCE emulator) Debug" (based on "Win32 (WCE emulator) Application") +!MESSAGE "Application - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Application") +!MESSAGE "Application - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +# PROP ATL_Project 2 + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +# PROP BASE Use_MFC 2 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "emulatorRel" +# PROP BASE Intermediate_Dir "emulatorRel" +# PROP BASE CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "emulatorRel" +# PROP Intermediate_Dir "emulatorRel" +# PROP CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /Yu"stdafx.h" /Gs8192 /GF /O2 /c +# ADD CPP /nologo /W3 /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /Gs8192 /GF /O2 /c +# SUBTRACT CPP /YX /Yc /Yu +MTL=midl.exe +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /MACHINE:IX86 +# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /MACHINE:IX86 + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +# PROP BASE Use_MFC 2 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "emulatorDbg" +# PROP BASE Intermediate_Dir "emulatorDbg" +# PROP BASE CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "emulatorDbg" +# PROP Intermediate_Dir "emulatorDbg" +# PROP CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /Yu"stdafx.h" /Gs8192 /GF /c +# ADD CPP /nologo /W3 /WX /Zi /Od /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /FR /Gs8192 /GF /c +MTL=midl.exe +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /MACHINE:IX86 +# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /MACHINE:IX86 + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +# PROP BASE Use_MFC 2 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "ARMV4Rel" +# PROP BASE Intermediate_Dir "ARMV4Rel" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "ARMV4Rel" +# PROP Intermediate_Dir "ARMV4Rel" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r +CPP=clarm.exe +# ADD BASE CPP /nologo /W3 /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_AFXDLL" /Yu"stdafx.h" /O2 /M$(CECrtMT) /c +# ADD CPP /nologo /W3 /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_AFXDLL" /O2 /M$(CECrtMT) /c +# SUBTRACT CPP /YX /Yc /Yu +MTL=midl.exe +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +# PROP BASE Use_MFC 2 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "ARMV4Dbg" +# PROP BASE Intermediate_Dir "ARMV4Dbg" +# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "ARMV4Dbg" +# PROP Intermediate_Dir "ARMV4Dbg" +# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}" +# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r +# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r +CPP=clarm.exe +# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /Yu"stdafx.h" /M$(CECrtMTDebug) /c +# ADD CPP /nologo /W3 /Zi /Od /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /M$(CECrtMTDebug) /c +# SUBTRACT CPP /YX /Yc /Yu +MTL=midl.exe +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM +# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM + +!ENDIF + +# Begin Target + +# Name "Application - Win32 (WCE emulator) Release" +# Name "Application - Win32 (WCE emulator) Debug" +# Name "Application - Win32 (WCE ARMV4) Release" +# Name "Application - Win32 (WCE ARMV4) Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Sources\Application.cpp + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_APPLI=\ + "..\..\..\DNSServices\DNSServices.h"\ + ".\Sources\Application.h"\ + ".\Sources\BrowserDialog.h"\ + ".\Sources\StdAfx.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_APPLI=\ + "..\..\..\DNSServices\DNSServices.h"\ + ".\Sources\Application.h"\ + ".\Sources\BrowserDialog.h"\ + ".\Sources\StdAfx.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_APPLI=\ + "..\..\..\DNSServices\DNSServices.h"\ + ".\Sources\Application.h"\ + ".\Sources\BrowserDialog.h"\ + ".\Sources\StdAfx.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_APPLI=\ + "..\..\..\DNSServices\DNSServices.h"\ + ".\Sources\Application.h"\ + ".\Sources\BrowserDialog.h"\ + ".\Sources\StdAfx.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Sources\Application.h +# End Source File +# Begin Source File + +SOURCE=.\Sources\BrowserDialog.cpp + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_BROWS=\ + ".\Sources\Application.h"\ + ".\Sources\BrowserDialog.h"\ + ".\Sources\StdAfx.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_BROWS=\ + "..\..\..\DNSServices\DNSServices.h"\ + ".\Sources\Application.h"\ + ".\Sources\BrowserDialog.h"\ + ".\Sources\StdAfx.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_BROWS=\ + ".\Sources\Application.h"\ + ".\Sources\BrowserDialog.h"\ + ".\Sources\StdAfx.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_BROWS=\ + ".\Sources\Application.h"\ + ".\Sources\BrowserDialog.h"\ + ".\Sources\StdAfx.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Sources\BrowserDialog.h +# End Source File +# Begin Source File + +SOURCE=.\Sources\StdAfx.cpp + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_STDAF=\ + ".\Sources\StdAfx.h"\ + +# ADD CPP /Yc"stdafx.h" + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_STDAF=\ + ".\Sources\StdAfx.h"\ + +# ADD CPP /Yc"stdafx.h" + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_STDAF=\ + ".\Sources\StdAfx.h"\ + +# ADD CPP /Yc"stdafx.h" + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_STDAF=\ + ".\Sources\StdAfx.h"\ + +# ADD CPP /Yc"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Sources\StdAfx.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Resources\Application.ico +# End Source File +# Begin Source File + +SOURCE=.\Resources\Application.rc + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Resources\Application.rc2 +# PROP Exclude_From_Scan -1 +# PROP BASE Exclude_From_Build 1 +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\Resources\newres.h +# End Source File +# Begin Source File + +SOURCE=.\Resources\Resource.h +# End Source File +# End Group +# Begin Group "Rendezvous" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\DNSServices\DNSServices.c + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_DNSSE=\ + "..\..\..\DNSServices\DNSServices.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_DNSSE=\ + "..\..\..\DNSServices\DNSServices.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_DNSSE=\ + "..\..\..\DNSServices\DNSServices.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_DNSSE=\ + "..\..\..\DNSServices\DNSServices.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\..\DNSServices\DNSServices.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\mDNSCore\mDNS.c + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_MDNS_=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_MDNS_=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_MDNS_=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_MDNS_=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\mDNSCore\mDNSClientAPI.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\mDNSCore\mDNSDebug.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\mDNSCore\mDNSPlatformFunctions.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\mDNSWin32.c + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_MDNSW=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\mDNSWin32.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_MDNSW=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\mDNSWin32.h"\ + +# ADD CPP /W3 + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_MDNSW=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\mDNSWin32.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_MDNSW=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\mDNSWin32.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\..\mDNSWin32.h +# End Source File +# End Group +# End Target +# End Project diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcw b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcw new file mode 100644 index 0000000..11ef513 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcw @@ -0,0 +1,29 @@ +Microsoft eMbedded Visual Tools Workspace File, Format Version 4.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Application"=.\Application.vcp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.ico b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.ico new file mode 100644 index 0000000000000000000000000000000000000000..51a182ea7cb7ad75ea832d1119ab539537ae2ac6 GIT binary patch literal 1078 zcmc&yF>d295F95d3@D(?o`tJJp5a18iad(f2?$_+g~b&Hwh;lBe1u9@Yn2AB(9Kdd zaz%hUAVtcq0nm-3QA5~_o`=s94p6r za%ZRjv^*r-}wc+(xDIJwkC`(>t$(Le61p{3N`*jy<&mr#PbQAHXF zIfPt%uDlV+A?M(WXKyOauaCSs@+97e;JN8&&k8XXeD)e~u-B7)y&x;tQx9Q^`QUHQ zBvx#3d#0ZYy9*z*I{u~Q1$Jn*glVP_rVAo%YKT-CLi2wiq literal 0 HcmV?d00001 diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc new file mode 100644 index 0000000..c453f27 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc @@ -0,0 +1,194 @@ +//Microsoft eMbedded Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "newres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "#include ""newres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#include ""Resources\\Application.rc2"" // non-Microsoft eMbedded Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#include ""wceres.rc"" // WCE-specific components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "Application.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_APPLICATION_DIALOG DIALOG DISCARDABLE 0, 0, 139, 153 +STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION +EXSTYLE WS_EX_APPWINDOW | 0x80000000L +CAPTION "Rendezvous Browser" +FONT 8, "System" +BEGIN + CONTROL "List1",IDC_BROWSE_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | + WS_BORDER | WS_TABSTOP,7,7,125,141 +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "Apple Computer, Inc.\0" + VALUE "FileDescription", "Rendezvous Browser for Windows CE\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "Application\0" + VALUE "LegalCopyright", "Copyright © 2003 Apple Computer, Inc.\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "Application.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Rendezvous Browser for Windows CE\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_APPLICATION_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 132 + TOPMARGIN, 7 + BOTTOMMARGIN, 146 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDP_SOCKETS_INIT_FAILED "Windows CE sockets initialization failed." + IDS_BROWSER_LIST_COLUMN_NAME "Name" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +#include "Resources\Application.rc2" // non-Microsoft eMbedded Visual C++ edited resources +#include "afxres.rc" // Standard components +#include "wceres.rc" // WCE-specific components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc2 b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc2 new file mode 100644 index 0000000..29c9fe7 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc2 @@ -0,0 +1,13 @@ +// +// APPLICATION.RC2 - resources Microsoft eMbedded Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft eMbedded Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/newres.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/newres.h new file mode 100644 index 0000000..31c3a43 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/newres.h @@ -0,0 +1,28 @@ +#ifndef __NEWRES_H__ +#define __NEWRES_H__ + +#define SHMENUBAR RCDATA +#if !(defined(_WIN32_WCE_PSPC) && (_WIN32_WCE >= 300)) + #undef HDS_HORZ + #undef HDS_BUTTONS + #undef HDS_HIDDEN + + #include + // for MenuBar + #define I_IMAGENONE (-2) + #define NOMENU 0xFFFF + #define IDS_SHNEW 1 + #define IDM_SHAREDNEW 10 + #define IDM_SHAREDNEWDEFAULT 11 + + // for Tab Control + #define TCS_SCROLLOPPOSITE 0x0001 // assumes multiline tab + #define TCS_BOTTOM 0x0002 + #define TCS_RIGHT 0x0002 + #define TCS_VERTICAL 0x0080 + #define TCS_MULTISELECT 0x0004 // allow multi-select in button mode + #define TCS_FLATBUTTONS 0x0008 +#endif //_WIN32_WCE_PSPC + + +#endif //__NEWRES_H__ diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/resource.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/resource.h new file mode 100644 index 0000000..0337c56 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/resource.h @@ -0,0 +1,22 @@ +//{{NO_DEPENDENCIES}} +// Microsoft eMbedded Visual C++ generated include file. +// Used by Application.rc +// +#define IDD_APPLICATION_DIALOG 102 +#define IDP_SOCKETS_INIT_FAILED 103 +#define IDS_BROWSER_LIST_COLUMN_NAME 104 +#define IDR_MAINFRAME 128 +#define IDC_BROWSE_LIST 1000 +#define IDC_IP_TEXT 1003 +#define IDC_TXT_TEXT 1004 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1005 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.cpp b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.cpp new file mode 100644 index 0000000..ba28f41 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Application.cpp,v $ +Revision 1.1 2003/08/21 02:16:10 bradley +Rendezvous Browser for HTTP services for Windows CE/PocketPC. + +*/ + +#include "stdafx.h" + +#include "DNSServices.h" + +#include "BrowserDialog.h" + +#include "Application.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP(Application, CWinApp) + //{{AFX_MSG_MAP(Application) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +Application gApp; + +//=========================================================================================================================== +// Application +//=========================================================================================================================== + +Application::Application() + : CWinApp() +{ + // +} + +//=========================================================================================================================== +// InitInstance +//=========================================================================================================================== + +BOOL Application::InitInstance() +{ + DNSStatus err; + BrowserDialog dialog; + BOOL dnsInitialized; + + dnsInitialized = FALSE; + + if( !AfxSocketInit() ) + { + AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); + goto exit; + } + + err = DNSServicesInitialize( kDNSFlagAdvertise, 0 ); + if( err ) + { + AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); + goto exit; + } + dnsInitialized = TRUE; + + // Display the main browser dialog. + + m_pMainWnd = &dialog; + dialog.DoModal(); + + // Dialog has been closed. Return false to exit the app and not start the app's message pump. + +exit: + if( dnsInitialized ) + { + DNSServicesFinalize(); + } + return( FALSE ); +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.h new file mode 100644 index 0000000..1f4fe13 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Application.h,v $ +Revision 1.1 2003/08/21 02:16:10 bradley +Rendezvous Browser for HTTP services for Windows CE/PocketPC. + +*/ + +#if !defined(AFX_APPLICATION_H__E2E51302_D643_458E_A7A5_5157233D1E5C__INCLUDED_) +#define AFX_APPLICATION_H__E2E51302_D643_458E_A7A5_5157233D1E5C__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "Resource.h" + +//=========================================================================================================================== +// Application +//=========================================================================================================================== + +class Application : public CWinApp +{ + public: + + Application(); + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(Application) + public: + virtual BOOL InitInstance(); + //}}AFX_VIRTUAL + + //{{AFX_MSG(Application) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_APPLICATION_H__E2E51302_D643_458E_A7A5_5157233D1E5C__INCLUDED_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.cpp b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.cpp new file mode 100644 index 0000000..991fc08 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: BrowserDialog.cpp,v $ +Revision 1.1 2003/08/21 02:16:10 bradley +Rendezvous Browser for HTTP services for Windows CE/PocketPC. + +*/ + +#include "stdafx.h" + +#include "Application.h" + +#include "DNSServices.h" + +#include "BrowserDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP(BrowserDialog, CDialog) + //{{AFX_MSG_MAP(BrowserDialog) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject ); + +//=========================================================================================================================== +// BrowserDialog +//=========================================================================================================================== + +BrowserDialog::BrowserDialog( CWnd *inParent ) + : CDialog( BrowserDialog::IDD, inParent ) +{ + //{{AFX_DATA_INIT(BrowserDialog) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + + // Note that LoadIcon does not require a subsequent DestroyIcon in Win32. + + mIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME ); +} + +//=========================================================================================================================== +// DoDataExchange +//=========================================================================================================================== + +void BrowserDialog::DoDataExchange( CDataExchange *pDX ) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(BrowserDialog) + DDX_Control(pDX, IDC_BROWSE_LIST, mBrowserList); + //}}AFX_DATA_MAP +} + +//=========================================================================================================================== +// OnInitDialog +//=========================================================================================================================== + +BOOL BrowserDialog::OnInitDialog() +{ + CString s; + + CDialog::OnInitDialog(); + + // Set the icon for this dialog. The framework does this automatically when the application's main window is not a dialog. + + SetIcon( mIcon, TRUE ); // Set big icon + SetIcon( mIcon, FALSE ); // Set small icon + + CenterWindow( GetDesktopWindow() ); + + // Set up the list. + + CRect rect; + + s.LoadString( IDS_BROWSER_LIST_COLUMN_NAME ); + mBrowserList.GetWindowRect( rect ); + mBrowserList.InsertColumn( 0, s, LVCFMT_LEFT, rect.Width() - 8 ); + + // Start browsing for services. + + DNSStatus err; + + err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser ); + if( err ) + { + AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); + goto exit; + } + + err = DNSBrowserStartServiceSearch( mBrowser, 0, "_http._tcp", NULL ); + if( err ) + { + AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); + goto exit; + } + +exit: + return( TRUE ); +} + +//=========================================================================================================================== +// BrowserCallBack [static] +//=========================================================================================================================== + +void + BrowserDialog::BrowserCallBack( + void * inContext, + DNSBrowserRef inRef, + DNSStatus inStatusCode, + const DNSBrowserEvent * inEvent ) +{ + BrowserDialog * dialog; + + DNS_UNUSED( inStatusCode ); + dialog = reinterpret_cast < BrowserDialog * > ( inContext ); + + switch( inEvent->type ) + { + case kDNSBrowserEventTypeAddService: + dialog->BrowserAddService( inEvent->data.addService.name ); + break; + + case kDNSBrowserEventTypeRemoveService: + dialog->BrowserRemoveService( inEvent->data.removeService.name ); + break; + + default: + break; + } +} + +//=========================================================================================================================== +// BrowserAddService +//=========================================================================================================================== + +void BrowserDialog::BrowserAddService( const char *inName ) +{ + BrowserEntry newEntry; + INT_PTR n; + INT_PTR i; + + UTF8StringToStringObject( inName, newEntry.name ); + + n = mBrowserEntries.GetSize(); + for( i = 0; i < n; ++i ) + { + BrowserEntry & entry = mBrowserEntries.ElementAt( i ); + + if( entry.name.CompareNoCase( newEntry.name ) == 0 ) + { + break; + } + } + if( i >= n ) + { + mBrowserEntries.Add( newEntry ); + mBrowserList.InsertItem( i, newEntry.name ); + } +} + +//=========================================================================================================================== +// BrowserRemoveService +//=========================================================================================================================== + +void BrowserDialog::BrowserRemoveService( const char *inName ) +{ + BrowserEntry newEntry; + INT_PTR n; + INT_PTR i; + + UTF8StringToStringObject( inName, newEntry.name ); + + n = mBrowserEntries.GetSize(); + for( i = 0; i < n; ++i ) + { + BrowserEntry & entry = mBrowserEntries.ElementAt( i ); + + if( entry.name.CompareNoCase( newEntry.name ) == 0 ) + { + break; + } + } + if( i < n ) + { + mBrowserEntries.RemoveAt( i ); + mBrowserList.DeleteItem( i ); + } +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// UTF8StringToStringObject +//=========================================================================================================================== + +static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject ) +{ + DWORD err; + int n; + wchar_t * unicode; + + unicode = NULL; + + n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 ); + if( n > 0 ) + { + unicode = (wchar_t *) malloc( (size_t)( n * sizeof( wchar_t ) ) ); + if( !unicode ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; }; + + n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n ); + inObject = unicode; + } + else + { + inObject = ""; + } + err = 0; + +exit: + if( unicode ) + { + free( unicode ); + } + return( err ); +} diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.h new file mode 100644 index 0000000..7777510 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: BrowserDialog.h,v $ +Revision 1.1 2003/08/21 02:16:10 bradley +Rendezvous Browser for HTTP services for Windows CE/PocketPC. + +*/ + +#if !defined(AFX_BROWSERDIALOG_H__DECC5C82_C1C6_4630_B8D5_E1DDE570A061__INCLUDED_) +#define AFX_BROWSERDIALOG_H__DECC5C82_C1C6_4630_B8D5_E1DDE570A061__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#include "afxtempl.h" +#include "Resource.h" + +#include "DNSServices.h" + +//=========================================================================================================================== +// BrowserDialog +//=========================================================================================================================== + +class BrowserDialog : public CDialog +{ + public: + + BrowserDialog( CWnd *inParent = NULL ); + + //{{AFX_DATA(BrowserDialog) + enum { IDD = IDD_APPLICATION_DIALOG }; + CListCtrl mBrowserList; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(BrowserDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + + static void + BrowserCallBack( + void * inContext, + DNSBrowserRef inRef, + DNSStatus inStatusCode, + const DNSBrowserEvent * inEvent ); + + void BrowserAddService( const char *inName ); + void BrowserRemoveService( const char *inName ); + + protected: + + struct BrowserEntry + { + CString name; + }; + + + HICON mIcon; + DNSBrowserRef mBrowser; + CArray < BrowserEntry, BrowserEntry > mBrowserEntries; + + // Generated message map functions + //{{AFX_MSG(BrowserDialog) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_BROWSERDIALOG_H__DECC5C82_C1C6_4630_B8D5_E1DDE570A061__INCLUDED_) diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.cpp b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.cpp new file mode 100644 index 0000000..6622a5d --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: StdAfx.cpp,v $ +Revision 1.1 2003/08/21 02:16:10 bradley +Rendezvous Browser for HTTP services for Windows CE/PocketPC. + +*/ + +#include "stdafx.h" diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.h b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.h new file mode 100644 index 0000000..3672152 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: StdAfx.h,v $ +Revision 1.1 2003/08/21 02:16:10 bradley +Rendezvous Browser for HTTP services for Windows CE/PocketPC. + +*/ + +#if !defined(AFX_STDAFX_H__7F91E52B_CF39_429D_837D_599CE0B2B3D6__INCLUDED_) +#define AFX_STDAFX_H__7F91E52B_CF39_429D_837D_599CE0B2B3D6__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + + + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions + +#if defined(_AFXDLL) +#include // MFC support for Internet Explorer 4 Common Controls +#endif + +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + +#include // MFC socket extensions + +//{{AFX_INSERT_LOCATION}} +// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__7F91E52B_CF39_429D_837D_599CE0B2B3D6__INCLUDED_) diff --git a/mDNSWindows/Applications/RendezvousTest/Tool.c b/mDNSWindows/Applications/RendezvousTest/Tool.c new file mode 100644 index 0000000..7e49566 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/Tool.c @@ -0,0 +1,1047 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Tool.c,v $ +Revision 1.7 2003/08/20 07:06:34 bradley +Update to APSL 2.0. Updated change history to match other mDNSResponder files. + +Revision 1.6 2003/08/20 06:50:55 bradley +Updated to latest internal version of the Rendezvous for Windows code: Re-did everything to support +the latest DNSServices APIs (proxies, record updates, etc.); Added support for testing the platform +neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDiscovery API. + +*/ + +#if( defined( _MSC_VER ) ) + #pragma warning( disable:4068 ) // Disable "unknown pragma" warning for "pragma unused". + #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. + #pragma warning( disable:4311 ) // Disable "type cast : pointer truncation from void *const to int". + + // No stdint.h with Visual C++ so emulate it here. + + typedef signed char int8_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef unsigned char uint8_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef signed short int16_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef unsigned short uint16_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef signed long int32_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef unsigned long uint32_t; // C99 stdint.h not supported in VC++/VS.NET yet. +#else + #include +#endif + +#include +#include + +#if( __MACH__ ) + #include + #include + #include + + #include + #include + + #include +#else + #define WIN32_LEAN_AND_MEAN + + #include + #include +#endif + +#include "DNSServices.h" +#include "DNSServiceDiscovery.h" + +//=========================================================================================================================== +// Macros +//=========================================================================================================================== + +#if( !TARGET_OS_MAC ) + #define require_action_string( X, LABEL, ACTION, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + fprintf( stderr, "%s\n", ( STR ) ); \ + { ACTION; } \ + goto LABEL; \ + } \ + } while( 0 ) + + #define require_string( X, LABEL, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + fprintf( stderr, "%s\n", ( STR ) ); \ + goto LABEL; \ + \ + } \ + } while( 0 ) + + #define require_noerr_string( ERR, LABEL, STR ) \ + do \ + { \ + if( ( ERR ) != 0 ) \ + { \ + fprintf( stderr, "%s (%ld)\n", ( STR ), ( ERR ) ); \ + goto LABEL; \ + } \ + } while( 0 ) +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +int main( int argc, char* argv[] ); +static void Usage( void ); +static int ProcessArgs( int argc, char* argv[] ); +static DNSStatus ProcessPreset( int inPreset ); + +#if( __MACH__ ) + static void SigIntHandler( int inSignalNumber ); +#endif + +#if( defined( WINVER ) ) + static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ); +#endif + +static void BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inStatusCode, const DNSBrowserEvent *inEvent ); +static void ResolverCallBack( void *inContext, DNSResolverRef inRef, DNSStatus inStatusCode, const DNSResolverEvent *inEvent ); + +static void + RegistrationCallBack( + void * inContext, + DNSRegistrationRef inRef, + DNSStatus inStatusCode, + const DNSRegistrationEvent * inEvent ); + +static void + HostRegistrationCallBack( + void * inContext, + DNSHostRegistrationRef inRef, + DNSStatus inStatusCode, + void * inData ); + +static void + EmulatedBrowserCallBack( + DNSServiceBrowserReplyResultType inResult, + const char * inName, + const char * inType, + const char * inDomain, + DNSServiceDiscoveryReplyFlags inFlags, + void * inContext ); + +static void + EmulatedDomainEnumerationCallBack( + DNSServiceDomainEnumerationReplyResultType inResult, + const char * inDomain, + DNSServiceDiscoveryReplyFlags inFlags, + void * inContext ); + +static void + EmulatedResolverCallBack( + struct sockaddr * inInterfaceAddr, + struct sockaddr * inAddr, + const char * inTextRecord, + DNSServiceDiscoveryReplyFlags inFlags, + void * inContext ); + +static void EmulatedRegistrationCallBack( DNSServiceRegistrationReplyErrorType inResult, void *inContext ); + +static char * IPv4ToString( DNSOpaque32 inIP, char *outString ); + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +#if( defined( WINVER ) ) + static volatile int gQuit = 0; +#endif + +static int gPrintTXTRecords = 1; + +// Presets + +typedef struct PresetData PresetData; +struct PresetData +{ + int argc; + char * argv[ 16 ]; +}; + +#if 0 +#pragma mark == Presets == +#endif + +static const PresetData gPresets[] = +{ + /* 01 */ { 2, { "rendezvous", "-bbd" } }, + /* 02 */ { 4, { "rendezvous", "-bs", "_airport._tcp", "local." } }, + /* 03 */ { 4, { "rendezvous", "-bs", "_xserveraid._tcp", "local." } }, + /* 04 */ { 3, { "rendezvous", "-rdb", "apple.com" } }, + /* 05 */ { 7, { "rendezvous", "-rs", "My Fake AirPort", "_airport._tcp", "local.", "1234", "My Fake Info" } }, + /* 06 */ { 7, { "rendezvous", "-rs", "My Fake Xserve RAID", "_xserveraid._tcp", "local.", "1234", "My Fake Info" } }, + /* 07 */ { 7, { "rendezvous", "-rs", "My Fake Web Server", "_http._tcp", "local.", "8080", "index.html" } }, + /* 08 */ { 9, { "rendezvous", "-rps", "www.apple.com", "17.254.0.91", "Apple Web Server", "_http._tcp", "local.", "80", "index.html" } }, +}; + +const int gPresetsCount = sizeof( gPresets ) / sizeof( gPresets[ 0 ] ); + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// main +//=========================================================================================================================== + +int main( int argc, char* argv[] ) +{ + DNSStatus err; + + // Set up DNS Services and install a Console Control Handler to handle things like control-c signals. + + err = DNSServicesInitialize( kDNSFlagAdvertise, 0 ); + require_noerr_string( err, exit, "could not initialize Rendezvous" ); + +#if( __MACH__ ) + signal( SIGINT, SigIntHandler ); +#endif + +#if( defined( WINVER ) ) + SetConsoleCtrlHandler( ConsoleControlHandler, TRUE ); +#endif + + ProcessArgs( argc, argv ); + +exit: + DNSServicesFinalize(); + return( err ); +} + +//=========================================================================================================================== +// Usage +//=========================================================================================================================== + +static void Usage( void ) +{ + fprintf( stderr, "\n" ); + fprintf( stderr, "rendezvous - Rendezvous Tool 1.0d1\n" ); + fprintf( stderr, "\n" ); + fprintf( stderr, " -bbd 'b'rowse for 'b'rowsing 'd'omains\n" ); + fprintf( stderr, " -brd 'b'rowse for 'r'egistration 'd'omains\n" ); + fprintf( stderr, " -bs 'b'rowse for 's'ervices\n" ); + fprintf( stderr, " -lsi 'l'ookup 's'ervice 'i'nstance\n" ); + fprintf( stderr, " -rdb[d] 'r'egister 'd'omain for 'b'rowsing ['d'efault]\n" ); + fprintf( stderr, " -rdr[d] 'r'egister 'd'omain for 'r'egistration ['d'efault]\n" ); + fprintf( stderr, " -rs 'r'egister 's'ervice\n" ); + fprintf( stderr, " -rps 'r'egister 'p'roxy 's'ervice\n" ); + fprintf( stderr, " -rnss 'r'egister 'n'o 's'uch 's'ervice\n" ); + + fprintf( stderr, " -ebs 'e'mulated 'b'rowse for 's'ervices\n" ); + fprintf( stderr, " -ebd 'e'mulated 'b'rowse for 'd'omains\n" ); + fprintf( stderr, " -elsi 'e'mulated 'l'ookup 's'ervice 'i'nstance\n" ); + fprintf( stderr, " -ers 'e'mulated 'r'egister 's'ervice\n" ); + + fprintf( stderr, " -h[elp] 'h'elp\n" ); + fprintf( stderr, "\n" ); + + fprintf( stderr, " -1 Preset 1 (browse for browsing domains) rendezvous -bbd\n" ); + fprintf( stderr, " -2 Preset 2 (browse for AirPort) rendezvous -bs \"_airport._tcp\" \"local.\"\n" ); + fprintf( stderr, " -3 Preset 3 (browse for Xserve RAID) rendezvous -bs \"_xserveraid._tcp\" \"local.\"\n" ); + fprintf( stderr, " -4 Preset 4 (register apple.com domain) rendezvous -rdb \"apple.com\"\n" ); + fprintf( stderr, " -5 Preset 5 (register fake AirPort) rendezvous -rs \"My Fake AirPort\" \"_airport._tcp\" \"local.\" 1234 \"My Fake Info\"\n" ); + fprintf( stderr, " -6 Preset 6 (register fake Xserve RAID) rendezvous -rs \"My Fake Xserve RAID\" \"_xserveraid._tcp\" \"local.\" 1234 \"My Fake Info\"\n" ); + fprintf( stderr, " -7 Preset 7 (register fake web server) rendezvous -rs \"My Fake Web Server\" \"_http._tcp\" \"local.\" 8080 \"index.html\"\n" ); + fprintf( stderr, "\n" ); +} + +//=========================================================================================================================== +// ProcessArgs +//=========================================================================================================================== + +static int ProcessArgs( int argc, char* argv[] ) +{ + DNSStatus err; + int i; + const char * name; + const char * type; + const char * domain; + int port; + const char * text; + size_t textSize; + DNSBrowserRef browser; + DNSResolverFlags resolverFlags; + DNSDomainRegistrationType domainType; + const char * label; + const char * host; + const char * ip; + unsigned int b[ 4 ]; + DNSNetworkAddress addr; + dns_service_discovery_ref emulatedRef; + + // Parse the command line arguments (ignore first argument since it's just the program name). + + require_action_string( argc >= 2, exit, err = kDNSBadParamErr, "no arguments specified" ); + + for( i = 1; i < argc; ++i ) + { + if( strcmp( argv[ i ], "-bbd" ) == 0 ) + { + // 'b'rowse for 'b'rowsing 'd'omains + + fprintf( stdout, "browsing for browsing domains\n" ); + + err = DNSBrowserCreate( 0, BrowserCallBack, NULL, &browser ); + require_noerr_string( err, exit, "create browser failed" ); + + err = DNSBrowserStartDomainSearch( browser, 0 ); + require_noerr_string( err, exit, "start domain search failed" ); + } + else if( strcmp( argv[ i ], "-brd" ) == 0 ) + { + // 'b'rowse for 'r'egistration 'd'omains + + fprintf( stdout, "browsing for registration domains\n" ); + + err = DNSBrowserCreate( 0, BrowserCallBack, NULL, &browser ); + require_noerr_string( err, exit, "create browser failed" ); + + err = DNSBrowserStartDomainSearch( browser, kDNSBrowserFlagRegistrationDomainsOnly ); + require_noerr_string( err, exit, "start domain search failed" ); + } + else if( strcmp( argv[ i ], "-bs" ) == 0 ) + { + // 'b'rowse for 's'ervices + + require_action_string( argc > ( i + 2 ), exit, err = kDNSBadParamErr, "missing arguments" ); + ++i; + type = argv[ i++ ]; + domain = argv[ i ]; + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + fprintf( stdout, "browsing for \"%s.%s\"\n", type, domain ); + + err = DNSBrowserCreate( 0, BrowserCallBack, NULL, &browser ); + require_noerr_string( err, exit, "create browser failed" ); + + err = DNSBrowserStartServiceSearch( browser, kDNSBrowserFlagAutoResolve, type, domain ); + require_noerr_string( err, exit, "start service search failed" ); + } + else if( strcmp( argv[ i ], "-lsi" ) == 0 ) + { + // 'l'ookup 's'ervice 'i'nstance + + require_action_string( argc > ( i + 3 ), exit, err = kDNSBadParamErr, "missing arguments" ); + ++i; + name = argv[ i++ ]; + type = argv[ i++ ]; + domain = argv[ i ]; + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + fprintf( stdout, "resolving \"%s.%s.%s\"\n", name, type, domain ); + + resolverFlags = kDNSResolverFlagOnlyIfUnique | + kDNSResolverFlagAutoReleaseByName; + err = DNSResolverCreate( resolverFlags, name, type, domain, ResolverCallBack, 0, NULL, NULL ); + require_noerr_string( err, exit, "create resolver failed" ); + } + else if( ( strcmp( argv[ i ], "-rdb" ) == 0 ) || ( strcmp( argv[ i ], "-rdbd" ) == 0 ) ) + { + // 'r'egister 'd'omain for 'b'rowsing ['d'efault] + + require_action_string( argc > ( i + 1 ), exit, err = kDNSBadParamErr, "missing arguments" ); + if( strcmp( argv[ i ], "-rdb" ) == 0 ) + { + domainType = kDNSDomainRegistrationTypeBrowse; + label = ""; + } + else + { + domainType = kDNSDomainRegistrationTypeBrowseDefault; + label = "default "; + } + ++i; + domain = argv[ i ]; + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + fprintf( stdout, "registering \"%s\" as %sbrowse domain\n", domain, label ); + + err = DNSDomainRegistrationCreate( 0, domain, domainType, NULL ); + require_noerr_string( err, exit, "create domain registration failed" ); + } + else if( ( strcmp( argv[ i ], "-rdr" ) == 0 ) || ( strcmp( argv[ i ], "-rdrd" ) == 0 ) ) + { + // 'r'egister 'd'omain for 'r'egistration ['d'efault] + + require_action_string( argc > ( i + 1 ), exit, err = kDNSBadParamErr, "missing arguments" ); + if( strcmp( argv[ i ], "-rdr" ) == 0 ) + { + domainType = kDNSDomainRegistrationTypeRegistration; + label = ""; + } + else + { + domainType = kDNSDomainRegistrationTypeRegistrationDefault; + label = "default "; + } + ++i; + domain = argv[ i ]; + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + fprintf( stdout, "registering \"%s\" as %sregistration domain\n", domain, label ); + + err = DNSDomainRegistrationCreate( 0, domain, domainType, NULL ); + require_noerr_string( err, exit, "create domain registration failed" ); + } + else if( strcmp( argv[ i ], "-rs" ) == 0 ) + { + // 'r'egister 's'ervice + + require_action_string( argc > ( i + 5 ), exit, err = kDNSBadParamErr, "missing arguments" ); + ++i; + name = argv[ i++ ]; + type = argv[ i++ ]; + domain = argv[ i++ ]; + port = atoi( argv[ i++ ] ); + text = argv[ i ]; + textSize = strlen( text ); + if( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) + { + domain = "local."; + } + fprintf( stdout, "registering service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text ); + + err = DNSRegistrationCreate( 0, name, type, domain, (DNSPort) port, text, (DNSCount) textSize, NULL, NULL, + RegistrationCallBack, NULL, NULL ); + require_noerr_string( err, exit, "create registration failed" ); + } + else if( strcmp( argv[ i ], "-rps" ) == 0 ) + { + DNSHostRegistrationFlags hostFlags; + + // 'r'egister 'p'roxy 's'ervice + + require_action_string( argc > ( i + 7 ), exit, err = kDNSBadParamErr, "missing arguments" ); + ++i; + host = argv[ i++ ]; + ip = argv[ i++ ]; + name = argv[ i++ ]; + type = argv[ i++ ]; + domain = argv[ i++ ]; + port = atoi( argv[ i++ ] ); + text = argv[ i ]; + textSize = strlen( text ); + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + + sscanf( ip, "%u.%u.%u.%u", &b[ 0 ], &b[ 1 ], &b[ 2 ], &b[ 3 ] ); + addr.addressType = kDNSNetworkAddressTypeIPv4; + addr.u.ipv4.addr.v32 = (DNSUInt32)( ( b[ 0 ] << 24 ) | ( b[ 1 ] << 16 ) | ( b[ 2 ] << 8 ) | ( b[ 3 ] << 0 ) ); + + fprintf( stdout, "registering proxy service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text ); + + hostFlags = kDNSHostRegistrationFlagOnlyIfNotFound | kDNSHostRegistrationFlagAutoRenameOnConflict; + err = DNSHostRegistrationCreate( hostFlags, host, domain, &addr, NULL, + HostRegistrationCallBack, NULL, NULL ); + require_noerr_string( err, exit, "create host registration failed" ); + + err = DNSRegistrationCreate( 0, name, type, domain, (DNSPort) port, text, (DNSCount) textSize, host, NULL, + RegistrationCallBack, NULL, NULL ); + require_noerr_string( err, exit, "create registration failed" ); + } + else if( strcmp( argv[ i ], "-rnss" ) == 0 ) + { + // 'r'egister 'n'o 's'uch 's'ervice + + require_action_string( argc > ( i + 3 ), exit, err = kDNSBadParamErr, "missing arguments" ); + ++i; + name = argv[ i++ ]; + type = argv[ i++ ]; + domain = argv[ i++ ]; + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + fprintf( stdout, "registering no-such-service \"%s.%s.%s\"\n", name, type, domain ); + + err = DNSNoSuchServiceRegistrationCreate( 0, name, type, domain, NULL, RegistrationCallBack, NULL, NULL ); + require_noerr_string( err, exit, "create no-such-service registration failed" ); + } + else if( strcmp( argv[ i ], "-ebs" ) == 0 ) + { + // 'e'mulated 'b'rowse for 's'ervices + + require_action_string( argc > ( i + 2 ), exit, err = kDNSBadParamErr, "missing arguments" ); + ++i; + type = argv[ i++ ]; + domain = argv[ i ]; + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + fprintf( stdout, "emulated browsing for \"%s.%s\"\n", type, domain ); + + emulatedRef = DNSServiceBrowserCreate( type, domain, EmulatedBrowserCallBack, NULL ); + require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated browser failed" ); + } + else if( strcmp( argv[ i ], "-ebd" ) == 0 ) + { + int registrationOnly; + + // 'e'mulated 'b'rowse for 'd'omains + + require_action_string( argc > ( i + 1 ), exit, err = kDNSBadParamErr, "missing arguments" ); + ++i; + type = argv[ i++ ]; + if( strcmp( type, "registration" ) == 0 ) + { + registrationOnly = 1; + } + else if( strcmp( type, "browse" ) == 0 ) + { + registrationOnly = 0; + } + else + { + require_action_string( 0, exit, err = kDNSBadParamErr, "invalid browse type" ); + } + fprintf( stdout, "emulated browsing for %s domains\n", type ); + + emulatedRef = DNSServiceDomainEnumerationCreate( registrationOnly, EmulatedDomainEnumerationCallBack, NULL ); + require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated domain browser failed" ); + } + else if( strcmp( argv[ i ], "-elsi" ) == 0 ) + { + // 'e'mulated 'l'ookup 's'ervice 'i'nstance + + require_action_string( argc > ( i + 3 ), exit, err = kDNSBadParamErr, "missing arguments" ); + ++i; + name = argv[ i++ ]; + type = argv[ i++ ]; + domain = argv[ i ]; + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + fprintf( stdout, "emulated resolving \"%s.%s.%s\"\n", name, type, domain ); + + emulatedRef = DNSServiceResolverResolve( name, type, domain, EmulatedResolverCallBack, NULL ); + require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated resolver failed" ); + } + else if( strcmp( argv[ i ], "-ers" ) == 0 ) + { + // 'e'mulated 'r'egister 's'ervice + + require_action_string( argc > ( i + 5 ), exit, err = kDNSBadParamErr, "missing arguments" ); + ++i; + name = argv[ i++ ]; + type = argv[ i++ ]; + domain = argv[ i++ ]; + port = atoi( argv[ i++ ] ); + text = argv[ i ]; + textSize = strlen( text ); + if( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) + { + domain = "local."; + } + fprintf( stdout, "registering service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text ); + + emulatedRef = DNSServiceRegistrationCreate( name, type, domain, (uint16_t) port, text, + EmulatedRegistrationCallBack, NULL ); + require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated registration failed" ); + } + else if( ( argv[ i ][ 0 ] == '-' ) && isdigit( argv[ i ][ 1 ] ) ) + { + // Preset + + ProcessPreset( atoi( &argv[ i ][ 1 ] ) ); + err = 0; + goto exit; + } + else if( strcmp( argv[ i ], "-q" ) == 0 ) + { + // Quiet (no text records) + + gPrintTXTRecords = 0; + } + else if( ( strcmp( argv[ i ], "-help" ) == 0 ) || ( strcmp( argv[ i ], "-h" ) == 0 ) ) + { + // Help + + Usage(); + err = 0; + goto exit; + } + else + { + // Unknown parameter. + + require_action_string( 0, exit, err = kDNSBadParamErr, "unknown parameter" ); + goto exit; + } + } + + // Run until control-C'd. + + #if( __MACH__ ) + CFRunLoopRun(); + #endif + + #if( defined( WINVER ) ) + while( !gQuit ) + { + Sleep( 200 ); + } + #endif + + err = kDNSNoErr; + +exit: + if( err ) + { + Usage(); + } + return( err ); +} + +//=========================================================================================================================== +// ProcessPreset +//=========================================================================================================================== + +static DNSStatus ProcessPreset( int inPreset ) +{ + DNSStatus err; + + require_action_string( ( inPreset > 0 ) && ( inPreset <= gPresetsCount ), exit, err = kDNSBadParamErr, "invalid preset" ); + + err = ProcessArgs( gPresets[ inPreset - 1 ].argc, (char **) gPresets[ inPreset - 1 ].argv ); + +exit: + return( err ); +} + +#if( __MACH__ ) +//=========================================================================================================================== +// SigIntHandler +//=========================================================================================================================== + +static void SigIntHandler( int inSignalNumber ) +{ + DNS_UNUSED( inSignalNumber ); + + signal( SIGINT, SIG_DFL ); + CFRunLoopStop( CFRunLoopGetCurrent() ); +} +#endif + +#if( defined( WINVER ) ) +//=========================================================================================================================== +// ConsoleControlHandler +//=========================================================================================================================== + +static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ) +{ + BOOL handled; + + handled = 0; + switch( inControlEvent ) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + gQuit = 1; + handled = 1; + break; + + default: + break; + } + return( handled ); +} +#endif + +//=========================================================================================================================== +// BrowserCallBack +//=========================================================================================================================== + +static void BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inStatusCode, const DNSBrowserEvent *inEvent ) +{ + char ifIP[ 32 ]; + char ip[ 32 ]; + + DNS_UNUSED( inContext ); + DNS_UNUSED( inRef ); + DNS_UNUSED( inStatusCode ); + + switch( inEvent->type ) + { + case kDNSBrowserEventTypeRelease: + break; + + case kDNSBrowserEventTypeAddDomain: + fprintf( stdout, "domain \"%s\" added on interface 0x%08X (%s)\n", + inEvent->data.addDomain.domain, + (int) inEvent->data.addDomain.interfaceID, + IPv4ToString( inEvent->data.addDomain.interfaceIP.u.ipv4.addr, ifIP ) ); + break; + + case kDNSBrowserEventTypeAddDefaultDomain: + fprintf( stdout, "default domain \"%s\" added on interface 0x%08X (%s)\n", + inEvent->data.addDefaultDomain.domain, + (int) inEvent->data.addDefaultDomain.interfaceID, + IPv4ToString( inEvent->data.addDefaultDomain.interfaceIP.u.ipv4.addr, ifIP ) ); + break; + + case kDNSBrowserEventTypeRemoveDomain: + fprintf( stdout, "domain \"%s\" removed on interface 0x%08X (%s)\n", + inEvent->data.removeDomain.domain, + (int) inEvent->data.removeDomain.interfaceID, + IPv4ToString( inEvent->data.removeDomain.interfaceIP.u.ipv4.addr, ifIP ) ); + break; + + case kDNSBrowserEventTypeAddService: + fprintf( stdout, "service \"%s.%s%s\" added on interface 0x%08X (%s)\n", + inEvent->data.addService.name, + inEvent->data.addService.type, + inEvent->data.addService.domain, + (int) inEvent->data.addService.interfaceID, + IPv4ToString( inEvent->data.addService.interfaceIP.u.ipv4.addr, ifIP ) ); + break; + + case kDNSBrowserEventTypeRemoveService: + fprintf( stdout, "service \"%s.%s%s\" removed on interface 0x%08X (%s)\n", + inEvent->data.removeService.name, + inEvent->data.removeService.type, + inEvent->data.removeService.domain, + (int) inEvent->data.removeService.interfaceID, + IPv4ToString( inEvent->data.removeService.interfaceIP.u.ipv4.addr, ifIP ) ); + break; + + case kDNSBrowserEventTypeResolved: + { + const uint8_t * p; + const uint8_t * end; + int i; + + fprintf( stdout, "resolved \"%s.%s%s\" to %s:%u on interface 0x%08X (%s)%s\n", + inEvent->data.resolved->name, + inEvent->data.resolved->type, + inEvent->data.resolved->domain, + IPv4ToString( inEvent->data.resolved->address.u.ipv4.addr, ip ), + ( inEvent->data.resolved->address.u.ipv4.port.v8[ 0 ] << 8 ) | + inEvent->data.resolved->address.u.ipv4.port.v8[ 1 ], + (int) inEvent->data.resolved->interfaceID, + IPv4ToString( inEvent->data.resolved->interfaceIP.u.ipv4.addr, ifIP ), + ( inEvent->data.resolved->textRecordRawSize > 0 ) ? " with text:" : "" ); + + p = (const uint8_t *) inEvent->data.resolved->textRecordRaw; + end = p + inEvent->data.resolved->textRecordRawSize; + i = 0; + + if( gPrintTXTRecords ) + { + while( p < end ) + { + uint8_t size; + + size = *p++; + if( ( p + size ) > end ) + { + fprintf( stdout, "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" ); + break; + } + fprintf( stdout, "%5d (%3d bytes): \"%.*s\"\n", i, size, size, p ); + p += size; + ++i; + } + fprintf( stdout, "\n" ); + } + break; + } + + default: + break; + } +} + +//=========================================================================================================================== +// ResolverCallBack +//=========================================================================================================================== + +static void ResolverCallBack( void *inContext, DNSResolverRef inRef, DNSStatus inStatusCode, const DNSResolverEvent *inEvent ) +{ + char ifIP[ 32 ]; + char ip[ 32 ]; + + DNS_UNUSED( inContext ); + DNS_UNUSED( inRef ); + DNS_UNUSED( inStatusCode ); + + switch( inEvent->type ) + { + case kDNSResolverEventTypeResolved: + { + const uint8_t * p; + const uint8_t * end; + int i; + + fprintf( stdout, "resolved \"%s.%s%s\" to %s:%u on interface 0x%08X (%s)%s\n", + inEvent->data.resolved.name, + inEvent->data.resolved.type, + inEvent->data.resolved.domain, + IPv4ToString( inEvent->data.resolved.address.u.ipv4.addr, ip ), + ( inEvent->data.resolved.address.u.ipv4.port.v8[ 0 ] << 8 ) | + inEvent->data.resolved.address.u.ipv4.port.v8[ 1 ], + (int) inEvent->data.resolved.interfaceID, + IPv4ToString( inEvent->data.resolved.interfaceIP.u.ipv4.addr, ifIP ), + ( inEvent->data.resolved.textRecordRawSize > 0 ) ? " with text:" : "" ); + + p = (const uint8_t *) inEvent->data.resolved.textRecordRaw; + end = p + inEvent->data.resolved.textRecordRawSize; + i = 0; + + if( gPrintTXTRecords ) + { + while( p < end ) + { + uint8_t size; + + size = *p++; + if( ( p + size ) > end ) + { + fprintf( stdout, "\n### MALFORMED TXT RECORD (length byte too big for record)\n\n" ); + break; + } + fprintf( stdout, "%5d (%3d bytes): \"%.*s\"\n", i, size, size, p ); + p += size; + ++i; + } + fprintf( stdout, "\n" ); + } + break; + } + + case kDNSResolverEventTypeRelease: + break; + + default: + break; + } +} + +//=========================================================================================================================== +// RegistrationCallBack +//=========================================================================================================================== + +static void + RegistrationCallBack( + void * inContext, + DNSRegistrationRef inRef, + DNSStatus inStatusCode, + const DNSRegistrationEvent * inEvent ) +{ + DNS_UNUSED( inContext ); + DNS_UNUSED( inRef ); + DNS_UNUSED( inStatusCode ); + + switch( inEvent->type ) + { + case kDNSRegistrationEventTypeRelease: + break; + + case kDNSRegistrationEventTypeRegistered: + fprintf( stdout, "name registered and active\n" ); + break; + + case kDNSRegistrationEventTypeNameCollision: + fprintf( stdout, "name in use, please choose another name\n" ); + break; + + default: + break; + } +} + +//=========================================================================================================================== +// HostRegistrationCallBack +//=========================================================================================================================== + +static void + HostRegistrationCallBack( + void * inContext, + DNSHostRegistrationRef inRef, + DNSStatus inStatusCode, + void * inData ) +{ + DNS_UNUSED( inContext ); + DNS_UNUSED( inRef ); + DNS_UNUSED( inData ); + + if( inStatusCode == kDNSNoErr ) + { + fprintf( stdout, "host name registered and active\n" ); + } + else if( inStatusCode == kDNSNameConflictErr ) + { + fprintf( stdout, "host name in use, please choose another name\n" ); + } + else + { + fprintf( stdout, "unknown host registration status (%ld)\n", inStatusCode ); + } +} + +//=========================================================================================================================== +// EmulatedBrowserCallBack +//=========================================================================================================================== + +static void + EmulatedBrowserCallBack( + DNSServiceBrowserReplyResultType inResult, + const char * inName, + const char * inType, + const char * inDomain, + DNSServiceDiscoveryReplyFlags inFlags, + void * inContext ) +{ + DNS_UNUSED( inFlags ); + DNS_UNUSED( inContext ); + + if( inResult == DNSServiceBrowserReplyAddInstance ) + { + fprintf( stdout, "\"%s.%s%s\" service added emulated\n", inName, inType, inDomain ); + } + else if( inResult == DNSServiceBrowserReplyRemoveInstance ) + { + fprintf( stdout, "\"%s.%s%s\" service removed emulated\n", inName, inType, inDomain ); + } + else + { + fprintf( stdout, "### unknown emulated browser callback result (%d)\n", inResult ); + } +} + +//=========================================================================================================================== +// EmulatedDomainEnumerationCallBack +//=========================================================================================================================== + +static void + EmulatedDomainEnumerationCallBack( + DNSServiceDomainEnumerationReplyResultType inResult, + const char * inDomain, + DNSServiceDiscoveryReplyFlags inFlags, + void * inContext ) +{ + DNS_UNUSED( inFlags ); + DNS_UNUSED( inContext ); + + if( inResult == DNSServiceDomainEnumerationReplyAddDomain ) + { + fprintf( stdout, "\"%s\" domain added emulated\n", inDomain ); + } + else if( inResult == DNSServiceDomainEnumerationReplyAddDomainDefault ) + { + fprintf( stdout, "\"%s\" default domain added emulated\n", inDomain ); + } + else if( inResult == DNSServiceDomainEnumerationReplyRemoveDomain ) + { + fprintf( stdout, "\"%s\" domain removed emulated\n", inDomain ); + } + else + { + fprintf( stdout, "### unknown emulated domain enumeration callback result (%d)\n", inResult ); + } +} + +//=========================================================================================================================== +// EmulatedResolverCallBack +//=========================================================================================================================== + +static void + EmulatedResolverCallBack( + struct sockaddr * inInterfaceAddr, + struct sockaddr * inAddr, + const char * inTextRecord, + DNSServiceDiscoveryReplyFlags inFlags, + void * inContext ) +{ + struct sockaddr_in * ifSin4; + struct sockaddr_in * sin4; + char ifIP[ 64 ]; + char ip[ 64 ]; + + DNS_UNUSED( inFlags ); + DNS_UNUSED( inContext ); + + ifSin4 = (struct sockaddr_in *) inInterfaceAddr; + sin4 = (struct sockaddr_in *) inAddr; + + fprintf( stdout, "service resolved to %s:%d on interface %s with text \"%s\"\n", + IPv4ToString( *( (DNSOpaque32 *) &sin4->sin_addr.s_addr ), ip ), + ntohs( sin4->sin_port ), + IPv4ToString( *( (DNSOpaque32 *) &ifSin4->sin_addr.s_addr ), ifIP ), + inTextRecord ? inTextRecord : "" ); +} + +//=========================================================================================================================== +// EmulatedResolverCallBack +//=========================================================================================================================== + +static void EmulatedRegistrationCallBack( DNSServiceRegistrationReplyErrorType inResult, void *inContext ) +{ + DNS_UNUSED( inContext ); + + if( inResult == kDNSServiceDiscoveryNoError ) + { + fprintf( stdout, "service name registered successfully\n" ); + } + else + { + fprintf( stdout, "service registration failed( %d)\n", inResult ); + } +} + +//=========================================================================================================================== +// IPv4ToString +//=========================================================================================================================== + +static char * IPv4ToString( DNSOpaque32 inIP, char *outString ) +{ + sprintf( outString, "%u.%u.%u.%u", inIP.v8[ 0 ], inIP.v8[ 1 ], inIP.v8[ 2 ], inIP.v8[ 3 ] ); + return( outString ); +} diff --git a/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindows.h b/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindows.h new file mode 100644 index 0000000..eaa6a70 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindows.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ToolPrefixWindows.h,v $ +Revision 1.2 2003/08/20 07:06:34 bradley +Update to APSL 2.0. Updated change history to match other mDNSResponder files. + +Revision 1.1 2003/08/20 06:01:19 bradley +Prefix files for CodeWarrior Windows builds to set compile options. + +*/ + +#ifndef __TOOL_PREFIX_WINDOWS__ +#define __TOOL_PREFIX_WINDOWS__ + +#define DEBUG 0 + +#endif // __TOOL_PREFIX_WINDOWS__ diff --git a/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindowsDebug.h b/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindowsDebug.h new file mode 100644 index 0000000..1f76fc2 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/ToolPrefixWindowsDebug.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ToolPrefixWindowsDebug.h,v $ +Revision 1.2 2003/08/20 07:06:34 bradley +Update to APSL 2.0. Updated change history to match other mDNSResponder files. + +Revision 1.1 2003/08/20 06:01:19 bradley +Prefix files for CodeWarrior Windows builds to set compile options. + +*/ + +#ifndef __TOOL_PREFIX_WINDOWS_DEBUG__ +#define __TOOL_PREFIX_WINDOWS_DEBUG__ + +#define DEBUG 1 +#define MDNS_DEBUGMSGS 1 + +#endif // __TOOL_PREFIX_WINDOWS_DEBUG__ diff --git a/mDNSWindows/Applications/RendezvousTest/ToolWin32.mcp b/mDNSWindows/Applications/RendezvousTest/ToolWin32.mcp new file mode 100644 index 0000000000000000000000000000000000000000..064aba024384e04fd43cd810abd4688ce02028e7 GIT binary patch literal 284642 zcmeHQ31Az=)gH-l67B#2!mWhEDM=I(n-Icf$4N+nO@wVofRtF4l~{>vsgfKVu9$F? zQc8hBxsL*+^nh}e0tE^bXbY55dT_Ut8w&k_(n4uV{_pL+)$U5WtCh4Ci8Y%%>)SW8 z^Jd<>d2{UUY&?;O)CeIa`h=Ku@Cea$$OtjGqYz_o?ynhv5cOzIbVnD5Q>j=Y6$qsg zE2EK&7E~3#pFkhNFT~1O_&rF7&4I0sD42twwWIK7v=B-B*d3gt7>Nj*fHnnf2BJ%A zLAYE{-B2xT4%&igOZ<)jZ4D~*ptJ%YD#jl8fh<4d_#xMi{P?#A?Eo4F+6lBXXgARA zAYA940NMuE&ID0jNi9^4*&s@DDrhTE1BlA7FNn%F4Mg#$gQ$G2x$5JTc~A9F)i|Ak>XgR+2&GCjF334gocSP&Z<4 z5Spx_1JswI_bK?LIzXL?T|pB-6G3&LZqS!O5l}0r3v>i%FAyD6H)L;jMi0W%DXddV=(0tGW&_dARAgUX(L9$`8BiNeQ7DRSL zbxh?aJNJXAZR`$$4T|v~vXgy4WK&_#640@rV?c{Q$AOLn%>eBQY5|e$!KTG_ps}Df zPyj^sSOX&aL$en9gT4d`fylPWKFNkBgUHq@c~HKn4bd;v#}f;Bes(GqBIh7o%A>k9 z;8R*5wM69rP_Xu^@kOKBq~J=Zg;^hd;iE$dR9qOxg*O9}IAoF9LGg&lQq@D__>@-O zv1k!BHsu)6mPlkMW1*wk$SWw#dz;qwmO#@H?F#}ev944&6^o`Rg}E&Y=E3XLYEen% zwu@tZ_0t3Ga}L+zQgRd*t)P~E;T@D;@ReUQm~0vpchk0LUw8Dx)rtNzIW4*bjzx(& zfq&sdm2)^x32E8$f+^ zCr(JuiFWm`2=-*75}*`}htJ(*#!H459;IqEPccQ8a@n$UCY;I?eVu0)XoE}YsP{l) z$gOI%-BXuJ!xZ(*rwn(L)4V^FjyzNiXu8yX?e#KhQGX`b73;H$0cWEgirx9en6_wI z@ndS!4ff4$l5TI7|4g>J_*zCnJkYm z4F##hgy(@MuY_s%i(Dyq5Wh5BCjNyW8nO|-3`8x0@QomnNBA}nb()0l1HpeP_}BQQ zVKMQ0@rQ=?gipsG=uSB<0EP~g<2%4KRiNX?z%<;aZWx@fL7EK|=f& zcs%26p=l~V9d*EHxyo?>@Scnp0q?~)0=zflUf?>$1Hcm*uK}jvG#wWJ)3BY6?*Y^F zf{r_Y$rsS^2(aoCg!naZP-TnffvG;|coTSE6^efW(-5AH5f~v%SE1M%n1=3jj0dJ6 zJRQ@4X{t!aA;2?KD2@Q8p+6nR0aJa`u^gDDBXslw&r+c{4Vb13bes=NQwKV(1*Rz) z9X|#>M1|rW;6oWd1>DH^HQ*-3p8(HhJOYiUnejHja~SUdJeTo)z+{Vb%mqG7g(3tz zpYc-Q1&lj^Y5GM+4=_!`=tu##s8Fm0Ze@HH@DYr^4ZMi)b-?PF1$75}B=a8u)<$|S z0n>Dmj(34OR46_LrZz*zC^W94RVcOvUd(uR;3bUr2Bv8p9Sy+8s!$vTd>rFrftNCl z0Mpc#j&;Dtt5945yo~WrfIAt#032q##YlB3OsCU;Bg{V*xSMelILbH!yqxiwz$+ME z4&1}|YTy{-n}Js{{sZtT#-9Mk8Sf3p(#v=@a3AAifD?=pz)8mPr+f`~Idwu4W@A}S zh|B;U3hrBT(8M(#qs^!Cj5XlJEL2lwcBsPxo)PHwvPYo`Qo|7)V+9^D0ihAfqslcf zcR_pW)(iSF(NteF6PP=Yj3-jjylK9(lFGc?IdK`f;cGx$Od;Q*ObryzgflmX8qMcd z`k3qm7*5#C%oG`lB=Tw5gL)ucpjkF0hGQg!Zvv_f2NV5eEG-<0qbU?>8AO7E+9;1C zlz32j+NDQ{^L?B6PBN8^ z6SA$qZ1bgyOpC<@Zwr=nq54so%m^9 zN%5c~+jv+%@zWgGN-o&h18XaOnjaU67eG9kBNvJ{0r6p*=;=BM)lKtT~E)L3Ih0W8&8W*1+|P<6vPQ`{Kpk2_LhZCl4MEEdqUbSW!aJyDtZ&`r+Xt!zAmC0!%CCgx3SptxduK z^sgA2E4T?5!*2z50tXnM3Ot_irN9#y)2+BY7(WNRC*yws@5Ojq^b~tDo(5dUm~O32 zWZVb*CB_#3)2&U)+bzI!Ym@Mkz;tVq@EgEW7=HpB1g2w0j76rZP#g%X-P$}3Si7}3 z0Ic2GycAfwwRsn?c5CzZz}l_NkAP=znpSJk-*xm%|2l5*5*0D+O5s& zfVEqj4*_epHvbHK2&eNIu(kxV9eR*PO%`wy<0F7)GhPYY%=qiTa~R(UJeTpq!1EYC z4}2Ko^}zEPPr&$X0pt0=+O5qvuy$+no50$w%{zf<3Q5QFz%;F+<8xq|y3sKn<3#P& zW;3vMYqJ|zyR~^1uy$+n`@q_*%_o4hTbmyNAFU=Sw#B%Urb=||2TaplI@*A>Tbrwa zwOgB)0Bg54e+I1G+I$&UyS4c_Fio%N7>_aSG8KwBz@3a&0EZc$4cx`}Cg2F;UjlbC zehWCtcr?bt%Nb7uUctB(xQB5ZIL7!~;FXNO2fT{$L%?yy&jI%`{s_2_@unDe(^Qv^ z@xa=x%~`--QQ4v$SX)<*1E-mPI_ z#^ZrcV!S`_$&BX%pTf8g_*BLh0k36z3-BQ0r-4sn{0Z;xw=zBl z_%_CO0N>8|8Q?n@{}1?1#(Qrk#7`K{1-^@M4EU#v&jG%h@h!mjFn${NUdA5+|BUf= z+pCWz(djJU`cWHyk?}gO_(jG` zfL~&K8t}`Ee+2wz#(x0*3*+^`uP_emEW}?KHv_-QI12n48z0mtBN-oAIH*?=TJnzsvY6;J-1x8TdWMPXoWt_!Hp2Gak3A5dX*cK;RD;cL9IM z_-x>h7~cZ?55~U-{+RK9f&a;P_uYi}7vnj=pD^wP{*>{>!2f1^Kk#Ra-v#~;;~jU$ zBXW%Q0sbH31A#wh90Fd?xEoj*?BQoMuy!NhtH6Gr8tDq)8pb~Y)}BSU4_JFr_Gw_{ zmK!d41$Y$4{{VP2<4pp%Bgc3*;7u9V18>H79`NRjJAk)fyb^dz#-{*p#rOhXdh?Ht z>jQWUk?|eC+c16vcw5H52Oi7#Rp9Lye+s-kF_@D7K;Jp|x0N$H% zCvY9(6M^YXNjfe7{u1Nw0q?{3A>c`jp9P-G_@cxWz_Qd#~@i^cE7*7G7#drbmfsDI=4`Q4F zKA7>>fe&H)1K>j$KM35&_)ox1jQvF#c!!CEz)XX9Le=90s1pI0Jkbi(!T2cPMT~oaLyXS`K9cdZz-^3w3f#{4 z8Q>1a?*Jdgm?n%zGad`Pm~jK}62>9mV;IMQk7axo@NtZ<0A9-YZs0F7eg^n>#_t0! zV?1&q#{Z1>1r9Sl61aAaFP1Zv#ge{|Iws4=J`6a{_;_G?oSBY(;6BEe0Vf#W2ApL41n^fFzX6cKPUj;sqF-_i2V!SKx$&3#HK85jdz^5`E0A9=ZLf}EhcLSfs z_yyo~jQT4vl-tD{58h!0-wWp>nRxjGY$fu z%eV#j8;pB^&trTB@HZJ>4}3o3$AB+j{3h^)jMoEyi}9{OjQ<%o17FOz6ZqSVPXWG! z@%Ml)W&9}cWsKhdzMOIGRE+-_PXfM@@j<{>F>VFEnsFEKHH=pSU(5Jx;O{WL4EQ?6 zHv?bK_yOP>7(WI4UB<5h-^lnQ;O{Z^?~Cz2rs&j5EN$WPBFzuNeOb_}7eo4g5IcH-MjD?61f8pYa6X-!MJ^_$kJV zfPc$)Iq=hr*8=~J@i&2=VSF|4?-}0${0GK=0RAK6Pk{f#c+Uom{~6B%evWY;@bio> z0)Bz<-M}w0eh&C0#{U6+neoK^F#cyeANVhfqrk5)J{S0}jBf&dmGR@iuQ7fX_;tpk zXJGu#cmnX7j1L8Vi}7;cw;8VmeuweJ!0$5t3Gm+-KMwpJ<2QicXZ$(v-x+VWKgR!z zCjx)K_z>U^86OY)5#x2h|6qJA@W+fF0sbfBcYy!Jc&nKh|1+Kr{3+w3fd9=n4g4A7 zuLJ*w@%MrM%lKEo|6}|%@aK$c55V}J@dRMuSGqWH05IK-r(+>7Jt{#*7`TS<8sHI( zF9fb-d=v0U#t#6GV*F>|(TqpT!uX%@Zor!|J_L9(#zz9%?Np)WW@!GB|Dd)&K85@T z`3>sBs0~rSL+yv!4fRvhPmqtKK8X6Zb3xR0s6V5AgZhgrKv#mu#;yX9?5GsQr-dBpV=`pmuf{i263N$BRMa)2Xi@n;tKOcZT z2G!u4=Ey$$jsW>V@8f(Ven)}c1bzp!3Gj#b{RikH(55)2`E)IQyYL$Ub%Uayqd~`j zjs+bDS`Io2bOGpm&=SzMKwk!34Ei?c63|7U3qk#$)u0nV1E4ja6G4}PP6Ay93WFAd zmV%B4T?x7dv<%b<3W1IUJpyV2wS(q>eg`@XG#|78)B$P+eHC;TXddWmpu<6rg1!!V z3iK@KT+lZ_=YY-zJqLOo)CWp{lAu382HgX?7j!@9KG4rVKLuR}x)t;z(9NLlfo=i)7<35eP|z|9W)3!74#R-E1(si z9#9PQGH4~}ub@?+UxVVHS3$3V&H(j-&ICOJdINMY=m}6gXeQ`4pjn_lf-VPL1v(IP z5NI-J3MdGg3fdX88)$b>0JJY?C(y2-I?$e=JwSVb#)Bq+CV_SV?G2g;`Vwd#5FOiq zwgrs^Z3n6WjRcJXjRtKG@`GwYTYxqNZ35a1G!C>QXlu|2(B_~mL0f^wfPA1GKz9H< z1iBq`C+H`j<3SIAqM)CH9t3rPdO@dymV=goBA|ysVNf3^0ZM_=pqD@yP(Nrj=qsQT zK`(<&0{sH?2#ueF%CKlmx8+{TUPooeDYu^cT=8pud7%1)U693pxe#7-$Ws z8+00I9q7xTPS6>k9#9Ol3bYb*CTIXO2>L1L3D8}jyFvGW_5=M2bP(v*pvOT6faZXX z1sx2U0h$GR5;PMu7c>vF0JISF8t8CP3#b(|A9N(>bx<4VH=w6LzXRP0`WW;N&~HJ9 zferz^0cr*v1v&!sCg?5D+n{$q?VzJU9iXQ{A<%)KC7@$K4WRu&$AJz7HG*b?nm|iI zi$IG(ZVs})-` zQ@@-p^_7u3O-gbNK0Wf{0b0__9wT>r>y_+DF`#zNVJ%A{t76@>$N1v*P;+l&b$aK4 z+dswJV5hz5u~jS%-SdGek8-s0TUa@Ji^6JglX}!hF^EO7kHjYP;$d2BXltELP? zJ`_rH;dfL| z|DGdv8Go%(u7L+`RplAVg|OOpZhgg{D!yE!+~%#S;tl>~xI3+&5C}7_w`uoL^~&ISNrun*+^>zH}lU4KyW_VqPqj&IH<{D|(}S8KG{m z(z-a%Jhge!q`<(;83HHRO1G_9(BEh}u%I`Yzz(n2FqxqeYf;xq<0+?u-IYz!g3*Df zpgosOT&1TDMnqFzIu?P*U_90(T2F|iGI0eSm5!$Brz`Mq+V3dNCx>;%8095tEoj5x zE)Imk=}0)P=uTILvRSd94XK-#%$zYN*s@@Dj=I$Cj7g2X>G)EU#)x6iT6Vwcr3U@! zOp_*cwyskpeN#^Ke8O-7ZP9o%oQ?`JS1rNp{#d*_&=il0zC>TRH>xOJ7|c$)S_v@n z_42hYZf@&nQFWl^52k^=xBFIMpQ|GheURLr&LnyRhey|(kVti>MN=daO{byROix;L zgi|Z9Ggf;vlZo}MNW;!y@U7uwQaMvIICmhE3a5o4b44^2P_NO?+xq)5vEHa4!Sof9 z!=inH>#aG_n?#B!v7jxmD2Z6H6LDh5q1u45(I`awc!?T>Pnz^8dEpeZ=O}C&DU}7x z>ji-Vse_loFF3n-|TSC)jY7!bzr= z<||W4g0_-hYEa|hu}o&o{O}2@mbUiCGqFfGok1R!vbr2XwIW4?O(acG?fpq|-Aa;L zDfCMW1zu{vD15;r5V(NW)n%Qj;7X_jKsvH@)}IZs!OgY&p4{8sNfAq*pzya4oqlW z6Ihh#f#YwAr9x;K69N;Kg<~mdB*A5wND}Aq1bXS4W=-sBQKB2l}=a4FsT8l0jUA00jUA00jUA00jUA0 zfelLoH8d?yCk9H!7R+sJ->`Bhb5m&z^kg#0SyQL3#CN!Z>HfZ8B+)xHnM!o`M>6TD zE4x=sofe!nwL1|>PYowy^s3e{JBH8mNQGAjOp|y#Vq-eDsaLrYV51nxmKu;6kQ$I0 zkQ$I0kQ$I0kQ&%%G+=rZZ&vOji{?<%1B~XN+{YQsq2`Ah&0(fT9nC>G&nQ|2W}iwl z2QI`UR)3z+_(1V2C1_E9CW&t~T3mE=YYrC=j~O3#G+$zRy3!n^JZ#4gW#)(QbE5eH z*@rMqnQn`os6TOOzG%kG!x5xD{FxtOdTcU3*fJZGqIEW!=d~gb54NW8z+6Uq9+l$; z<|HEh^pv*#P;1_E&v^!xAH!s1`N6q{mLJ1xZ293PgUb)jF*>^lS;NbZU@|_NU^YOT zP#9r;NRA=qM=%*&A*flRo0ADqrlEcZcZy*Vk*aOT-|Zn(*^a|3hjIyc;G)w!W2o6Zf)v1seytUc!j z=jz2K-1d4?YskIW+8L|rRKiT|Smo!mgWM!dtspnh*aob^^8BGHk}lKiKld_|_2&i} zZQm-)WcgMRY%*lD`rOMjCvEINwD%btY?K~{=XWM%*;uMEioLxWW7%1)F^-Mx8l%`+ zaDF5UJ2XbJH6CLm8xt}{u{NaK7r4-} zHZV2Z!MxN6_Qs`$JD8Rp$sv>WW~E-vMy1vo-ucP%V0wtjfOK}Q+2|o=W6}94;24u}a56CeBJ-}4;I@?&`W-e>c%+xAZ=bEZiXB#Wh%r#Y?IVex0v8wc_maSgv zNbCm>M#o`4urkGpE;mojvfWiEM17#e6v!0AR8+mwG>Je$mbRKL4fsLM)FWAzQV-V3s8@4t$?`(7F?dn*2hebv*qoNb7JK%wm3AAnVGIwQmX>59^Mx#}^8=t{}q zvnq19npNp+t=J}xURdkaK>b|ao5JR(s^Tmo>Y^-z&FN^(hN2dD4_OzP7tt&-FW5q4 z-Z;f7j%mbV6(M)*Vim_cim{5J=AOqhRuOUy(J_u@l2(yiXj!R{S-PcKC1V;MSw+Yh zirGiW_DWV!D7OydhFNaSONnUNqpF3abhMUZm5-cZqE!^*2+k_peiSx~E;aSAR@YM` zhY}cvepZ*~J0+`FrqQWY1ac-$6++DAfT_W0spT~eI;A2TPF2rnoW?8HnA@|KObDGjV|}Ec1?F{1T7WS# zB;9!KXk?~Q5v8W;Hak8bR?Y~KSR}(LGdy24^-zd!waBist<0FOurOv0$unu@=NdM% zvodt^=8#+?XLgRMGuOi4IV8vInVVz$2Ch{TFa+n?yg`ahP=2G}YfMdpd6o@Clabd< z3>W1z7DI@s$r$;1!!dFV7RqdMBVwkxDKXQsL20SvH7hLuZIm0A&M`GHo$X}Xl+FCi z=QKJ_%BksPqZl5p(c46RGvxUBjgbQiG)cu0Y5Of*hGGP*;l9{%?9nx4VbdJ#*_ptC zzU7JBh>NkteAu3i%y$-sz?|WqAw+Q_hE?aBAl+Z&2H2ZqZp=KJ%!{BtYM0B_+OL6c z@ID4ERTlT;2Cl8W=Q}3jRc6m*3}M$~X6e4k%+GgDIpG$iqjFfVZT*uWMbikuz|Zke zhL9W=WeA~c^4vd7gzM*!UL? zwe~U`Xzyz{g0;utKx@Ba3^aQmLx_V9GQ_g+HXOsjA8`y@uf*Y2zKMgJJd`G1aYwY> zpfXd>Gdg>3Wt5R!@v^)gvueA})H=R{|1wJD5tMZr`OR7@F}zBi@8U2^zouPh)0=a! zlaJF~!1nd|Y0XLfSLG5Ix9TI|Y*XYyXeFgh$c&-GEnGrA=@H+rJ%IXNHg zB+uVy0VWqi%sj6`%p6BTEZuj|S-!ha(lUDpEuwM8qw(~yOO|8b^s`a&TDu;i4pXw{ zn*9=kipb5FP7N(r2{koV;^enaC7!uSDlBe=jP-`61?KhCT7WTo#ui24wv9*)G_)IH ze z$P^_NRpsYZq@i|9jvz0&xh?Zdb052u$PCokGmQxxW8pa*Qia{9E)qhTG|L zmaQgdIq5K)sFn7}tS!|>0r~pM*#NUL6t>x(6jq)I8Lzi0 zw_fqwa+H3r)pRQ=FOKO3OkSv=E7$HYS;j#pM7F#{>$#jGX^zS+7H(t}N{FK5HBPmo zw=3{0o1z-WEBS>~LhXxZ4$d!_7H(N2W1wNsWl?QZ^``7Awtx(8y|Vew2z*qO_6^3j8ykJJ2eCY)2A}~lj`cY3qu50-xD@{lUzm_us!9!Lc0s$- zlfO{y0)Lec=Oj|ms?>@sv}PByLx0vTq*|(81DajXHxK>zhc%}9=~HM~N&AA){Ajp4 zno2LNdR0}G0yMk8h_lh!F7T1ER5%rjO3SDw!_e#kUjtL@0^4y^Q}Jb4Z8V_S#R%9% z;qTnoBr*)xqzC6?6jrB#UH0FYFRVSIjKZ;jPY>!mBRUj^dQM+oAFa&)X$4@;5epW_ z%;+({b5% z8dWE;)PU50)PU50)PU50)PU50)PU50)PU50)W8O&0XcEsz$!#$M`}Q7;0x2hP_E?` zE>sOxTWiC?=s+1Sv(U3tt#g;mZ7eLVr2K>RP z!=V@@mqFfDDp_^%La70%0jUA00jUA00jUA00jUA00jUA00jYrvLIYL5xVJ&ng3QE* zq=8M58LaIm`pbCToYwZ6LZOyHG_VJklwUeZ&nXp>Gt@^ZA}agVy;%cVewBL|cs-vE z+_u4g`c)4)r$gS)QT0#_f93t0YN%!VGFJQj9O~bcJ=Ow-&lT9KONK}d3_A_vZe}j} zR}8x{Iu%InI$PF@MvXBTwUdsrIi|x8ju>08Yrv*Wxl}MFb9*7p2AlQs~Af0 zmo-nCv<%;BNhQ+3YEys^v}?OOqz0;81C6^^y9Q+Wr3UDZi9DnRzDNyh$2ae^`+Q$y zUZiZPfiF@6_*S1hs$2sdE%WAVy`V1>P4(du#d8OeaeQknRh&|!FBPIp6&9y(8GD1& zzzNaV>Xqr<4Ki;sQ&Iz!(m*sO8-aUW8^8A8{;J^>b3xzoL@*hTrOSAFc4O9v@^tCO zTmTR9E}yXUK#3c15}9<|=KEnAaiL1cI=)0GHffEfRzw5%;$8^};1cak0(wAaW8BTh z%lwz7LGx8#q;HaagAiY&{>_#k&%Ou^j1Wr>Ynjs>TNPQENQIh1?ThJ7lIA6_{^S!o zO{0%X;utw*Jr!zHiDq#(Xxe8*#Rl&-p^^GB_4_>U=vii1bdb5g|&K^1AriQR|VfU1pww{9mG!7QNs`HT>m`E_4;-93;(PKg}D7?;a_|G-v$ORLq5Qp zFAjbA+FgbJ=<7!uf@>RD{y%Zetw`GkdA^Q_I2gJJkls2X{;d&b^vT=T3{2i0AK(Q> zJjflDxpa5mmX^(EIe6*Z5QOeUrwM&gTo?xr#kRU=P>89wLF62Ss2HHAZ{z^{^ex zoVb%10Xx{2?QaC>e5PU#lb$!*gMWbR0d+G5dKsbBzf$kl)SNZ*S}|h2Z=b8^8l?@8 zO*-a5yl%)tz>tT4ArD8H^Wgi~kcWUp9sG9x$Z(1w-YJ2jJy>)lks@g6>gRlv1 zGYx}lYf?8M?Bp8%SlCD(e8v$RHgRw*o%_4hFyB(t%~*4M-_jo*3cH1W=@7o(!lutZ zdw1jo_76PnY}jT;=g4W)HcsvwmBIC@&2K*{bNNGJ>I~^QjG-@566c%$a$W5I6l? zjF|Zl&e!`!&KJ#(p1fvM1C{CU^}pG~koLq=e=#a^_Gx0y$%CUZ@E26}v6o)sn}_s# zgJIw1mx4EWN3p)~G%@ZhRA!dcWdOF_QQb)$2zezW&MY4*Ew9 zQu&ep%@KAsco*RJjN7hP{HO0$pBNbQiNW)C$l9^eF4jg~B>vko$BS1^9$jOY> z=MjSt_>LA7f@-;ts^H6eGTsLpz~Dm6`zMq33)-jHg5 z^u8Vk7TX(#R8R?tXJ^jgWHK(=JK8!5Ct{pvtJl+CyepvujKDi=n;;QTqx3$7wOK7O zv{r!fDs~YJF2(;jN3^#`Q>$ZYql zG`kpOw2Nvfyey}!1~j{%k*pv8lucx&(4L#^{mEn^m08Lg>MXTQLIw;&4QO_Ow?!JQ z?V=^tl?ta~(X_OTVOalmMbhj7cZU?aXl-w?iz@k5qyfz?XjU%w>8zrPmqnEtkQ$I0 zC_)2rpU(Q}a-U9GpQ2SN^^k9+YnrI`U%Wypdc(24YSx3Yk8NKf5J~j)MI)J5qAwi? zuMWrJ;jXw`!L4S?sIIcheQc|%zGZ2xH6WkGw$_W}R!ReMAKOZ4TFTkbH9%|K@{k&k z8ju>OW({Dw8alEnSTMJ>J$oWSRcfFolS$5+I&~#htAgqNzF;KLJ2ja~boWOx>8UHb zS52K3oHn&P5lK%CCu38GpNgn85Vc|jb4qhOoUYObahv)xi7AIg`=Y6ECfYsx>RFaZ zYQRYYdavjdONL1eRF4Ma_g|`~uI)-HzyD&Vnd12J`!B`mYdFT1-+vj7wJOV0tOn%w zUy9L~eA14-o+iKlQjE<|Dx&&Z*0l$zhIN@rckKGi|^OCw?O-t zU!-rs?lV=tc=<)@-`*-@NToC&zyBh?|02KtV)5M*`TZC5J2?0Tjr{(L{Qe6)mruK+ z$b&TC=4&{GG^KqlXAomhH($tclb%x?lCz;6(z=W_gL!Ig01O|R8|^;hqrVmh-YKpf z?;;-Fv8lkYcWoDMH^e)FOhA2=xlClCkH>&M`y>9xgEit zOmDmlNp6fpkwZ_gJIE~LjM1RR_V7ZEvhPxW_I9=IK(>K&#r7k%*-!$Im*%O> zlO|D8Rxy=8s{jK}l{0CQft3fnljB?<@8mdEq3}?Sqae@ZbVrA}k5cWeC$H*F3=hgP zMzJ?Xt>K-W#eq;b9SO(n4BM0|>l0N@*zv6&@8m=rHRple2G#OJdYoFScXE>b)>>7$ z9_URiPpY6lfU9gPI2LrutD?7*^Jf@HNHiT-&`bMlJ0(^iEQt5%3PdS4I7A5qwt7p9B`L~;^cE1l#l>RRcIoC?%WYdEr~JuqYD;Wm9PR%M3Wd~J41Yr%Pk!B(Ig zr`om;o7WPmh@48zqXG+O(}J4g(HdH;*?!4->q@jJ?b%UK`GJ+47Z&h<**>adt!ZOp zr=Cz5L#SipvgJ4z$h-154~jgL<0!~8Ine<}Ipv!*+kxZA%M&{&a?Cno=Bg44SKOGj zTsbbstfjY~;>N6H%W*qqEm?1BdD77pC04KN%ZA(fO;^0faUMpB3C*2Xet+7ZsfZlP zZ^g#?SYvbUfhad+9i2xc`W!d2^&$CGsPI^_tX!xUH>%_GP^j5voIV!uhTDCWBFE`a zPHCDha-3eWoJx(;D|)}9KqJhKn{2e^`C4q*a-0j~U3r{`fgZ|n6y%wl4Q*Uox*hPi z*5x>`bUDS11Iv(8<>MwB%@yt|x{4b&l`MyHQi*YsI)-SCrdC)FYv?Jd;Wmb-XkXan zJn5btj>i*;zQFuw+|egHS*E&f?-WCbw&;nD5ekLV#g)2;?Pd<-SQwXqu>GDXivnn) z)&uArkr22jnQT=XsL{x;@0g;E>Ds3fs*A^^?ZP|X3=AMJ|8Gs zj?4K#*>aq_gzPxId^wJSJd?Ab%?C=?TXFM&(&ZF4A1GT+JeDr!roOQQu*RWEja7!* z{D#ySbb7cG;fDLuWC>FW-k7znql(Nsl=5`1aeO+%v+~d@8V-jSa^NFF;dpNV4^A|- zwZ@~FoFU~H$X9INx$DrMw=<`f&AsP+VcBw=3zY3U%9i8UMR};Vq@y6u%HupO^rAd! zALaNIoZ+5t>Gi{MhFovO^|_^&r?@`13^~-O3;A@|8~c;FE!tf_o7ZTvKEeuAVl7E2 zPc-iM{HADCK9JKpsTPB`H${>6*Ghg8p~(4k z$#RP7b8Vz}-shGr$7P>ewj9Tt>AS z=AM=(a$MZg@Rim_yC`MLaV}7HPEod;q^1JPUk~Lt3i3?OhIWsx^!h1s{j+4dE^_^|OgZt02Zf%uD-=!zQ_6F~D(eR~ zO(ieyq#Ey~dok`Mtyk>6OM!doo|R`s%(4GzP9)a^Iuf`U9$6K(d8W94HL7bSu}GiO z3u>OpS($WvN6|Amh3;2*Fh5o7tjMTQZq&+$h9NW}V}7s&dkfcOlw)qH#yXIm@8XLHZM{z`Kx&9OvQ)56VMgL@=F6 z#rjs*x&+VcAQM~ew*v$SBkrt8$zlS)q0+_h~ijPhIaTus&vZ zN2lZpBC&FxRs>6LmmM()#ElBIXLS%=GxiEN&;b|YDpp%fs&}I9n#{fWIwJ@;;#II(E|Sn6+FvE}!uzU2i;= zbop(8vgNoPvzDwkwLBGg?_#(;L*QnQBDqg%o_|rt`W3i0Dfem3(>*aZ)NwlQiCX5j zAk)n=?1s5x$#N<+POs?Rbk4&-eNE9uYcAGa)c!x>a$H-s9OnXM$F*h4aXGFnUyh?7 z&&so*jcZHSTXFB-mM*8babOv8s(jpJqq&0Jf{^>P7S>UTF)B8OsAyj(_h~KG96H_1 zfr?GkDn32CEca>6*NkCYtEwnJNp!U3cOq9S$UkdVew>tTjidX+^03q17!OSZL=G7_rz>G z_kFu#7p3=w<;!stuYVEt8m{(xx61$wj7u9&$8t>c7=MMTfQ7eL7vr* z+djAS@)S2OEL~1dz1ULYWnc)@=b5F|)y$#EIT5_LOxlikoxgA00 zPZ;QPB;405asNl5zQg14U6gX=Y-soBN-s~5>z^gp56c>W1Q~YqfHp)?)S6Nf!j=KCQ*-4I?GFPpg&EJlNNi=gM-Q zRxABF@WyP4z}j#y(XT%$lNbF^98IB63k3}NRB8l%(D=a~3bZ(U!iOuW`v}Gbgd}#1!5W^)M^a* zD>c9xpyxw+!?C_d$Z(4# z@7fNx8r|5+vz>hId1I?GncpGOK-G6gL!?)ktki(iz{aD28jJ~WWHUT>L3?ZVM1rc+ zKu;!CdH z8+9wO)Ig;*Airp>YrNXOXkDz2r4=u^7qV_YI-+&JrckJ55Gs9eiOw$-QF>0-k{oXj zH}lp)5;uM=6suO*B2=5$_-kJMrjYy;WuZ1M!-uy9u!=8aD;VB&EK65N14=9Ss}P|@ z?eyFnHc+LVmgOE5%3dwFW1rPTcXWBUKb~0@P9}rNDtt+Oj#~ZzUMnqRQ^PWRT5#WC zSuVGQTDGh`6HaCNlgpO5y<~XBQ#|GHD$d*jM0sU7Fy0NIVioeSHD!H{UXEQk(k+T^&n~}4b zoJJu}tu>(ZDnmA#v6gA%HVT1F>uKM5?RRV{+SeUDado0Uy{tRh)xVRfWho^t%1St`fhCD^IFWO!*n8_!Drkmn<%e;8h-E=yB_2IR!F1f}5;&94@7 zwcMvf)s`Hus(Q%jlVyKYOk4V@6v0#Eq@yc}xTK`0{gFg>Nf#D( zQD-ETuEdjbos_L$cOp_;_KJ=jQsk_NEqA)IIgwlw=tu-Y;mE4+ijuzEpvs9w`qB=9 zJd?9B>1c1B$?1!AITS~DCMOw>r8D-DJdiVnQV%AR3C9B2XTfbqCq*pjW6*9TsPQ`K zCz#HpVtp%YP0lmDWn#;n8juHaLamF@{NlZVMePA>6&H_Y0(hiiRW$31r~(yVTfk!= zo#v^{lP2Y)M(R=yB~UxD`Bmjinq(8Ku)UMxTp;h{B%O*QJe1=o$TK-Tj>Yk!JYy6) z=oxZ5U^&ivsd}I{RZcSEs5uYh3@Unq94F0rA%`p=7%pfB>`&&l%nK~&O(s$q-K#iX zYYeT}SS1>FaG&NFKDTZoZ`mdC6}kjF?h*x<}@!ZsJEQz zDCM-XxvjmelnaZzrc)V1;3fScuPZvbDrdm4Z!bDgiwhJv&Ql6~Y)n=F?~d)<4UZ<8uA8TsiTGl(*cILg5r&DbES3O1j~nE6?eC zSU$T^;J%9&^99mr#qPUwuW`I1;#qm*vnWnY#VRbM*g>V9MR8ur5T4lqzChu0grcc9 zo=HmB#iDSh>$R}paC^2UnRXocw)aHij*8P1WB)ryL6v)k$FVrVgYqaP9ahf@g(G-O zXo>A2-M%PsHLlUoka&^9O%OM03~tuwJdxvKjm`@>Zr13$kR#XV6gN}!A7u<4uF#r^ z;|$3#C#Tha7zI6yxWrH*H|<9$PsHUKUD!C zuD9aW=*pJka*eKBIUCvF(7=#1L%6n9c;1)q)fV7{Qpsnl~#1=i@i7{@E+k!y6MhGGtNjm~ie z75$u7_ExF2K)FU&j15rSPWls+YjmWBqL1OUMmGvO_SJ^V`yR=mI9lf}nF~cWD#{#F zv`PzJFfv@;O0V**&w^5hcZ6BPyLhs61vKCfPAw2a21^Y{4GdQe_{Em3Z3~<5MW0wC zoQWm+@(b$AXGxCKfYgB0fYgB0fYgB0fYgB0fYgB0fYgB0fYiW-qJa@u$KQ1BKs3^y z33tUydsSvA*YUN5g{|~Fmb9QR*YSmr>-ZZ=tNUW|QSIyazDn%2@x@fFlqxk)Py_Ow zeL)osn+US6A2wB4QbDS{uP;ey@*=5$VWI(f&wiLxq%4NifYgB0fYgB0fYgB0fYgB0 zfYgB0fYd-g1E*H$I1_H}%D&)&)^B(GXDAn(~%OHIq?^Qxz&txH<% zeZ93-Bv)#nIy6u#HWNoA`Wzp^r|02Qp~BxF_CRBe3&X3!)?dj}Q{NOnUVKxGawJE+$Vuj@3GIhVz-ts;n!q|ybxfMQb!1G|tM(i+kz9lC*aSl1$f~eSn>S0& zJ5%=-jYayL3gwxcmB|%(IWRChlT*1pO!2`tLjiK22jx+AWgJd>SE_v!Z7SXp>suAg z>*FUwhhFqiIaOhG>Pa7Em2eKPbGD1`u^7I61?A+V;PTYX_gK7;BfrPuvO;pAZK&U4 z$;qa2IuzeyDNm03@3E98$8nf$`kq9YavTL2zl%_ooDJ}MEG3qw=L*(srz-MJj-109?ZM4lOzF3s zCP+vSgSMu$lc5-e+F(Ua1-_X)Z&9=H(`%NwS<<{{-aKBJq>n*bMk>G@Zr^5hGxrb8 zEmvzoboFNnSR3rk136TE75fHsfpudK=E`y{qntIM0^er#!XGI1Hr&3MjC)DWGm`>N zvT$&xqPKJ=73*7J>lZv|Kk8ajCbrz^4x9&aLW^U4_0t0bGiTVW0quYSD}7-B#b(Du zNXkl$p3h;+mg8I?@5wv{yY*3^q{j>j}?_iI=a zwkeU_l{$an>_r9TEb3aR%_vHiLlO&pd(n&XC~~^vj*q;|X=(AMJeB&!V|2j!!2qsC z&&m^zb#aoGKc3Z3fibIR_Lk^cX;~0vdL}0owH@D)C~^kTG@G1Lh8)7w`)@O59&R)C z8jowM{Qeu1|bk`EncuW#>rc%GuEFiN~h%aQjfj6TiveTr1XvHgT% z@$x1V$duwo(^1D~xr}))6%V)j2c!qN-d?mdC}WVIe0H!{#f?$yEtyO>PR5E&H9vl* zB1f)+=WE896T(;~6pr@>@X&r!TWdU;$(fT;1r{}CHK&%9?)jLtY&kB+tYyn_>mF=D#l|Ypxa0jD&&pF^UD7i-1=b}!lam>6c>BP%T8j$MOcm3)oZd-IW*mNs ze7@agW>`2|m0Djbs?W74jpu!C*>YU=xn;|7>4*cIz@@`Rc{#*Lsve<~8? zyFo_PIbBX+0s0f{>UC^CivG*1pO7M_Quj8KU9pVA^S7RrC)wp71Nd&loQ#_7!S|iR7Os5@0gZco+8KKCCjPQJ?@I$dk&}a-gnM1sj^R%u}XNrv2QP1 z2ofl2jzm^q@eYZb_M^&idK%C0j$GMtT+Wfomg8JK*||&kavTMDCTBytmtMNwid+9I zTaL^1&vNC&BaZ89p7{gE5uS(b)agY@6vi{VcDrs|di@l)ZtQ^^rz-M5Z`txVRgrgc z0aY_N5r%8$ag=D&D0GJDRxk)eXN|9 zGK6P#kaT>VLWH8JIQBD3*u|o7H_8hRw|7jFX~(+|?LE=Bqv8rHZBVhdiao>QSRCO& zd6W_lzkSn|*e=rTixOAk8XXOZ7dhMnakIwYW{u7hIWE@dypZE&jm`@>a*a-LGe!T| zAlK+<^&dt-4idv&9TaL>$y0YasE}iLX4CTvl6qKDSmn&yOTcazzJjJci zl`Y5R8eO?^HncUm(#xaX8*o|xg2(jCuHCNDm0q6gnwHaD2oK7WmE&rS&V%weRgrh) zk!y6u0fJnkGit+A+)1ewd^XyH`GO*+QqMINSfle|9Iuo|uF;ViiaFFZI>!}M^mAU> zTcy?l}(JHNbq-Yy|*=r}T~dD=Cg&P2b258PYfM zudEefGUCV5NmDvUw_v@S(qEA=@qfLi5F}!GG?o17ar4FYoS!Gb-;DW_Z#qoS9+Pw& z0I*<$8vpo%JH+lPTdYJPL10QJePo9SF#l5U5nbUw@J_oJ&-|s(!GD1>>6Xy#H?@l? z%s+2uA!c&?wUe5aG%4sY@V8+8b9c=aQ;CVA2Kfo#H=fS)qS)Evr<5`1BAzt!2cWk> z1f_HMpMp+@}?b#P+ zivx&><4mNZ=`A|CSb^$Bg0y6L+L@dERIGu|h( z4Zc3vTZrwM|JTRjVlAih1QOTu({WI*ID`3zK(1DgOWNb&OyHTWBT!yVZ+q>%ljvan zV*pxtZ}``aVhQu#NBwI0d}ZN|;wa{Cj{4H{dCFII6h||E)?!_sZ+>G(fm7x118Dj= zs%9r~3iBJ0j;5c<7wsrcWqt$rTK>Ph#4lc8{@~?L*1pF4ryyu4+7ZQ{d;DVYI`j8=MdwfY<JM+Iu_QU+Gue(8f!2D~Wz%J&8Uc6p> z$b3I+`#m%aO6TeQZxA0b|L-GwS-IDpe1rH{ix0cf zj@RYxdjIv}Q|3Rjqb~P^!5hTCng2BG4X#Yd&nJ)7i!|B-9dG_hi2Gm=l+W5f){6}D z2hgC#us*juy^qk3@?^IrmgC!|N| z?Db&1IGOqX0)GPYU%jhdoWlHZkhvT4r+&R&oXY%PjS^xy^WXbny;#fq&%oExzu}2` zG06O}DAPEOf7HYE;xy(@248D;VdQ@u^A88J5t#B>`%Jw!o%ti`ed0XU^Xd21i!+#? z`3&{N`~@G^i!+%&1L+|Yo>%~`@|6Me?vA`rZ^?bcJoB3y< zA&h4Gj9peQzQ+8=5m`&;^Pkm=bC|zm6Cq}Bc{?Ad7hh-og)@EPEQC?|uf13=&Sn1B z(RLr__+zfB7vEt1d5C*8^UwKay*Q8g<7Vsi{^*VM;+xE$INvAsWy||G1i}!-xV}A6>dhsphN1-3hFLnK(UR=cd?slJe zn$w?pXT7+X`5*0EBc5hG?}qYzoB8*^8P4W%t%p5d!u$sGO`F3ODL-HNMZLI``Ts$D z%@4=UtQVIte-kkGK6%+9`^lOy=Y;6D6IRFC;whAP_4>w;tu|-KY0!QP%5fjk*Yf|uCH3OV z%!x}UW&XIE^m;$(-FmT{`4ex^ z{mEyrhZW2};tt)PJd1YS!~AYU(EQ2us4tkea&+J0%leb=qn)l~{tM8j)=nRNvtF!X z{sfHrHGgs<{BxZ75AUw~lWqQ5FM63j4&yq_pKN+wz35~9M`?f7pF9cuCz$^x-XZ=aX;SB<2qa1B( z{n_!!Ib#~cHOxQfw>9E9*e<2>VPk{1mif!i^k@Caq>cvh9p<0?4S&|3)F0R&u4Dd9 z=R+neZNpWL`jgFv%VjtQN5{y#!GKW6@U z*P&jx-+U5!zLojceHVOS(&v&T4dOQD-*qF>=X&3ytwG$*{0DxZr?cI`4Fb(pIj&6T z{$$H=gSeCV&)n=6XYlx>2mSI-nE%0A-JhJWMT59Yi;r})@yRIE?@yV3^iTDApA0*^ zoB7M{_lteFUt8ARAmA31W6MALv*VNdmNkfbnScJn{;WSa74`Bn=06OuB{1n>HuOy0 zHyuCSQ}-voKBz(5&-^`K*Zs++^$p?y=70Py_^jvF0~*B7nSb?jxMwD=|Sk45^L+@)Lk#jluuZ&IHx zN0Ioing0~{TDhM6n@>E>{6FvD%dRI}0povy`7aIX>F>R*Up&eD>z>uqe`TCs{D%2= zfv=_i*lRxV6!TLsfRVsdzkf!4e#?CS^LqMkY>m2O{+8g6h2ALsBd_|z@0dRUd@cP+ zFy?2N|I$bPZ2C1YhTk*)&KLCbpFX%w{6UNVPqZ(#liN?M6Mtm>>z^VW=GP?Z#Gjb| zSJbaI4oRI?C!S^gEjyy0<$T`q%{ZS_y5l?w&8ZY>AE^)?E;s> z2h@nkh(qPw>)bl=BFE35!)@U72TrIHFEPK-Un7tuCH^Ljb>d~_KWHfL{~@1$X8v-- z*UCEv<@yWr`)X^l<^93ZI`Ing&wE!d?~{w_#9x{JfT6sfEvpl#8s*3!ac!J%@3cCB zs#lJ4H>t^%_mc0{iPxEb^%i=0r(IMh-eCUYTh?UD``nB=@h0;>2VX1iio@%~Tg<-+ z`qAwF1*HEr^Y`3JFYh6j)`@qRKY1IyygxsqPQ1(fJCTl7zsJ|t2{aAmn7&<2wtkPg zqE5WW{Mq1ZksjZkD0#&?FdUf ziv1k9i(mYc`Kdqn#e10ZlO0A5^$R4W9CspqoZ~-)&f*j1&p5j#8@~>Nz)zV!;ryCx zK93vpiGMTycq~}J6)WlA0Au@%`Ij2<`Pu(`;y=v4`yD->qwDR;;dx7i84i1|-{;1@HI zJ*t;WKHW}?V*WN$Yeazk$v=OxouGAaI$r&U9{-sqw-cK%|H3`=`2V|TJFzMA&%uJV Zmj2``wiBB%|KS4+@t@dE8GBRu{}1wP!+8Jz literal 0 HcmV?d00001 diff --git a/mDNSWindows/Applications/RendezvousTest/ToolWin32.sln b/mDNSWindows/Applications/RendezvousTest/ToolWin32.sln new file mode 100644 index 0000000..495d5df --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/ToolWin32.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tool", "ToolWin32.vcproj", "{F66EFE7E-50A6-44D4-87C7-742B303BA852}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {F66EFE7E-50A6-44D4-87C7-742B303BA852}.Debug.ActiveCfg = Debug|Win32 + {F66EFE7E-50A6-44D4-87C7-742B303BA852}.Debug.Build.0 = Debug|Win32 + {F66EFE7E-50A6-44D4-87C7-742B303BA852}.Release.ActiveCfg = Release|Win32 + {F66EFE7E-50A6-44D4-87C7-742B303BA852}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/mDNSWindows/Applications/RendezvousTest/ToolWin32.vcproj b/mDNSWindows/Applications/RendezvousTest/ToolWin32.vcproj new file mode 100644 index 0000000..d5e1868 --- /dev/null +++ b/mDNSWindows/Applications/RendezvousTest/ToolWin32.vcproj @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/DNSServices/DNSServiceDiscovery.c b/mDNSWindows/DNSServices/DNSServiceDiscovery.c new file mode 100644 index 0000000..77e3dcb --- /dev/null +++ b/mDNSWindows/DNSServices/DNSServiceDiscovery.c @@ -0,0 +1,761 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSServiceDiscovery.c,v $ +Revision 1.2 2003/08/20 07:06:34 bradley +Update to APSL 2.0. Updated change history to match other mDNSResponder files. + +Revision 1.1 2003/08/20 06:04:45 bradley +Platform-neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDiscovery API. + +*/ + +#include +#include +#include + +#if( macintosh || __MACH__ ) + + #include + #include + #include + +#elif( defined( _MSC_VER ) || defined( __MWERKS__ ) ) + + #pragma warning( disable:4054 ) // Disable "type cast : from function pointer to data pointer". + #pragma warning( disable:4055 ) // Disable "type cast : from data pointer to function pointer". + #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. + #pragma warning( disable:4152 ) // Disable "nonstandard extension, function/data pointer conversion in expression". + + #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces. + + #include + +#endif + +#include "mDNSClientAPI.h" +#include "mDNSPlatformFunctions.h" +#include "DNSServices.h" + +#include "DNSServiceDiscovery.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == Constants & Types == +#endif + +//=========================================================================================================================== +// Constants & Types +//=========================================================================================================================== + +#define DEBUG_NAME "[DNSServiceDiscovery] " + +typedef enum +{ + kDNSServiceDiscoveryObjectTypeRegistration = 1, + kDNSServiceDiscoveryObjectTypeDomainEnumeration = 2, + kDNSServiceDiscoveryObjectTypeBrowser = 3, + kDNSServiceDiscoveryObjectTypeResolver = 4 + +} DNSServiceDiscoveryObjectType; + +typedef struct _dns_service_discovery_t _dns_service_discovery_t; +struct _dns_service_discovery_t +{ + DNSServiceDiscoveryObjectType type; + void * ref; + void * callback; + void * context; +}; + +#if 0 +#pragma mark == Macros == +#endif + +//=========================================================================================================================== +// Macros +//=========================================================================================================================== + +// Emulate Mac OS debugging macros for non-Mac platforms. + +#if( !TARGET_OS_MAC ) + #define check(assertion) + #define check_string( assertion, cstring ) + #define check_noerr(err) + #define check_noerr_string( error, cstring ) + #define debug_string( cstring ) + #define require( assertion, label ) do { if( !(assertion) ) goto label; } while(0) + #define require_string( assertion, label, string ) require(assertion, label) + #define require_quiet( assertion, label ) require( assertion, label ) + #define require_noerr( error, label ) do { if( (error) != 0 ) goto label; } while(0) + #define require_noerr_quiet( assertion, label ) require_noerr( assertion, label ) + #define require_noerr_action( error, label, action ) do { if( (error) != 0 ) { {action;}; goto label; } } while(0) + #define require_noerr_action_quiet( assertion, label, action ) require_noerr_action( assertion, label, action ) + #define require_action( assertion, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) + #define require_action_quiet( assertion, label, action ) require_action( assertion, label, action ) + #define require_action_string( assertion, label, action, cstring ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) +#endif + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +DNS_LOCAL void + DNSServiceRegistrationPrivateCallBack( + void * inContext, + DNSRegistrationRef inRef, + DNSStatus inStatusCode, + const DNSRegistrationEvent * inEvent ); + +DNS_LOCAL void + DNSServiceDomainEnumerationPrivateCallBack( + void * inContext, + DNSBrowserRef inRef, + DNSStatus inStatusCode, + const DNSBrowserEvent * inEvent ); + +DNS_LOCAL void + DNSServiceBrowserPrivateCallBack( + void * inContext, + DNSBrowserRef inRef, + DNSStatus inStatusCode, + const DNSBrowserEvent * inEvent ); + +DNS_LOCAL void + DNSServiceResolverPrivateCallBack( + void * inContext, + DNSResolverRef inRef, + DNSStatus inStatusCode, + const DNSResolverEvent * inEvent ); + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// DNSServiceRegistrationCreate +//=========================================================================================================================== + +dns_service_discovery_ref + DNSServiceRegistrationCreate( + const char * inName, + const char * inType, + const char * inDomain, + uint16_t inPort, + const char * inTextRecord, + DNSServiceRegistrationReply inCallBack, + void * inContext ) +{ + DNSStatus err; + dns_service_discovery_ref result; + dns_service_discovery_ref obj; + void * txt; + size_t txtSize; + DNSRegistrationRef registration; + + result = NULL; + txt = NULL; + txtSize = 0; + + // Allocate and initialize the object. + + obj = (dns_service_discovery_ref) malloc( sizeof( *obj ) ); + require_action( obj, exit, err = kDNSNoMemoryErr ); + + obj->type = kDNSServiceDiscoveryObjectTypeRegistration; + obj->ref = NULL; + obj->callback = inCallBack; + obj->context = inContext; + + // Create the underlying registration. Build a \001-escaped text record if needed. + + if( inTextRecord ) + { + err = DNSDynamicTextRecordBuildEscaped( inTextRecord, &txt, &txtSize ); + require_noerr( err, exit ); + } + + err = DNSRegistrationCreate( kDNSRegistrationFlagPreFormattedTextRecord, inName, inType, inDomain, inPort, txt, + (DNSCount) txtSize, NULL, NULL, DNSServiceRegistrationPrivateCallBack, obj, ®istration ); + require_noerr( err, exit ); + obj->ref = registration; + + // Success! + + result = obj; + obj = NULL; + +exit: + if( txt ) + { + DNSDynamicTextRecordRelease( txt ); + } + if( obj ) + { + DNSServiceDiscoveryDeallocate( obj ); + } + return( result ); +} + +//=========================================================================================================================== +// DNSServiceRegistrationPrivateCallBack +//=========================================================================================================================== + +DNS_LOCAL void + DNSServiceRegistrationPrivateCallBack( + void * inContext, + DNSRegistrationRef inRef, + DNSStatus inStatusCode, + const DNSRegistrationEvent * inEvent ) +{ + dns_service_discovery_ref obj; + DNSServiceRegistrationReply callback; + + DNS_UNUSED( inRef ); + DNS_UNUSED( inStatusCode ); + + check( inContext ); + obj = (dns_service_discovery_ref) inContext; + check( obj->callback ); + callback = (DNSServiceRegistrationReply) obj->callback; + + switch( inEvent->type ) + { + case kDNSRegistrationEventTypeRegistered: + debugf( DEBUG_NAME "name registered and active\n" ); + + if( callback ) + { + callback( kDNSServiceDiscoveryNoError, obj->context ); + } + break; + + case kDNSRegistrationEventTypeNameCollision: + debugf( DEBUG_NAME "name in use, please choose another name\n" ); + + if( callback ) + { + callback( kDNSServiceDiscoveryNameConflict, obj->context ); + } + break; + + default: + break; + } +} + +//=========================================================================================================================== +// DNSServiceRegistrationAddRecord +//=========================================================================================================================== + +DNSRecordReference + DNSServiceRegistrationAddRecord( + dns_service_discovery_ref inRef, + uint16_t inRRType, + uint16_t inRDLength, + const char * inRData, + uint32_t inTTL ) +{ + DNS_UNUSED( inRef ); + DNS_UNUSED( inRRType ); + DNS_UNUSED( inRDLength ); + DNS_UNUSED( inRData ); + DNS_UNUSED( inTTL ); + + debugf( DEBUG_NAME "DNSServiceRegistrationAddRecord is currently not supported\n" ); + return( 0 ); +} + +//=========================================================================================================================== +// DNSServiceRegistrationUpdateRecord +//=========================================================================================================================== + +DNSServiceRegistrationReplyErrorType + DNSServiceRegistrationUpdateRecord( + dns_service_discovery_ref inRef, + DNSRecordReference inRecordRef, + uint16_t inRDLength, + const char * inRData, + uint32_t inTTL ) +{ + DNS_UNUSED( inRef ); + DNS_UNUSED( inRecordRef ); + DNS_UNUSED( inRDLength ); + DNS_UNUSED( inRData ); + DNS_UNUSED( inTTL ); + + debugf( DEBUG_NAME "DNSServiceRegistrationUpdateRecord is currently not supported\n" ); + return( kDNSServiceDiscoveryUnsupportedErr ); +} + +//=========================================================================================================================== +// DNSServiceRegistrationRemoveRecord +//=========================================================================================================================== + +DNSServiceRegistrationReplyErrorType + DNSServiceRegistrationRemoveRecord( + dns_service_discovery_ref inRef, + DNSRecordReference inRecordRef ) +{ + DNS_UNUSED( inRef ); + DNS_UNUSED( inRecordRef ); + + debugf( DEBUG_NAME "DNSServiceRegistrationRemoveRecord is currently not supported\n" ); + return( kDNSServiceDiscoveryUnsupportedErr ); +} + +//=========================================================================================================================== +// DNSServiceDomainEnumerationCreate +//=========================================================================================================================== + +dns_service_discovery_ref + DNSServiceDomainEnumerationCreate( + int inRegistrationDomains, + DNSServiceDomainEnumerationReply inCallBack, + void * inContext ) +{ + DNSStatus err; + dns_service_discovery_ref result; + dns_service_discovery_ref obj; + DNSBrowserRef browser; + DNSBrowserFlags flags; + + result = NULL; + browser = NULL; + + // Allocate and initialize the object. + + obj = (dns_service_discovery_ref) malloc( sizeof( *obj ) ); + require_action( obj, exit, err = kDNSNoMemoryErr ); + + obj->type = kDNSServiceDiscoveryObjectTypeDomainEnumeration; + obj->ref = NULL; + obj->callback = inCallBack; + obj->context = inContext; + + // Create the underlying browser and start searching for domains. + + err = DNSBrowserCreate( 0, DNSServiceDomainEnumerationPrivateCallBack, obj, &browser ); + require_noerr( err, exit ); + obj->ref = browser; + + if( inRegistrationDomains ) + { + flags = kDNSBrowserFlagRegistrationDomainsOnly; + } + else + { + flags = 0; + } + err = DNSBrowserStartDomainSearch( browser, flags ); + require_noerr( err, exit ); + + // Success! + + result = obj; + browser = NULL; + obj = NULL; + +exit: + if( browser ) + { + DNSBrowserRelease( browser, 0 ); + } + if( obj ) + { + DNSServiceDiscoveryDeallocate( obj ); + } + return( result ); +} + +//=========================================================================================================================== +// DNSServiceDomainEnumerationPrivateCallBack +//=========================================================================================================================== + +DNS_LOCAL void + DNSServiceDomainEnumerationPrivateCallBack( + void * inContext, + DNSBrowserRef inRef, + DNSStatus inStatusCode, + const DNSBrowserEvent * inEvent ) +{ + dns_service_discovery_ref obj; + DNSServiceDomainEnumerationReply callback; + + DNS_UNUSED( inRef ); + DNS_UNUSED( inStatusCode ); + + check( inContext ); + obj = (dns_service_discovery_ref) inContext; + check( obj->callback ); + callback = (DNSServiceDomainEnumerationReply) obj->callback; + + switch( inEvent->type ) + { + case kDNSBrowserEventTypeAddDomain: + debugf( DEBUG_NAME "add domain \"%s\"\n", inEvent->data.addDomain.domain ); + + if( callback ) + { + callback( DNSServiceDomainEnumerationReplyAddDomain, inEvent->data.addDomain.domain, + DNSServiceDiscoverReplyFlagsFinished, obj->context ); + } + break; + + case kDNSBrowserEventTypeAddDefaultDomain: + debugf( DEBUG_NAME "add default domain \"%s\"\n", inEvent->data.addDefaultDomain.domain ); + + if( callback ) + { + callback( DNSServiceDomainEnumerationReplyAddDomainDefault, inEvent->data.addDefaultDomain.domain, + DNSServiceDiscoverReplyFlagsFinished, obj->context ); + } + break; + + case kDNSBrowserEventTypeRemoveDomain: + debugf( DEBUG_NAME "add default domain \"%s\"\n", inEvent->data.removeDomain.domain ); + + if( callback ) + { + callback( DNSServiceDomainEnumerationReplyRemoveDomain, inEvent->data.removeDomain.domain, + DNSServiceDiscoverReplyFlagsFinished, obj->context ); + } + break; + + default: + break; + } +} + +//=========================================================================================================================== +// DNSServiceBrowserCreate +//=========================================================================================================================== + +dns_service_discovery_ref + DNSServiceBrowserCreate( + const char * inType, + const char * inDomain, + DNSServiceBrowserReply inCallBack, + void * inContext ) +{ + DNSStatus err; + dns_service_discovery_ref result; + dns_service_discovery_ref obj; + DNSBrowserRef browser; + + result = NULL; + browser = NULL; + + // Allocate and initialize the object. + + obj = (dns_service_discovery_ref) malloc( sizeof( *obj ) ); + require_action( obj, exit, err = kDNSNoMemoryErr ); + + obj->type = kDNSServiceDiscoveryObjectTypeBrowser; + obj->ref = NULL; + obj->callback = inCallBack; + obj->context = inContext; + + // Create the underlying browser and start searching for domains. + + err = DNSBrowserCreate( 0, DNSServiceBrowserPrivateCallBack, obj, &browser ); + require_noerr( err, exit ); + obj->ref = browser; + + err = DNSBrowserStartServiceSearch( browser, 0, inType, inDomain ); + require_noerr( err, exit ); + + // Success! + + result = obj; + browser = NULL; + obj = NULL; + +exit: + if( browser ) + { + DNSBrowserRelease( browser, 0 ); + } + if( obj ) + { + DNSServiceDiscoveryDeallocate( obj ); + } + return( result ); +} + +//=========================================================================================================================== +// DNSServiceBrowserPrivateCallBack +//=========================================================================================================================== + +DNS_LOCAL void + DNSServiceBrowserPrivateCallBack( + void * inContext, + DNSBrowserRef inRef, + DNSStatus inStatusCode, + const DNSBrowserEvent * inEvent ) +{ + dns_service_discovery_ref obj; + DNSServiceBrowserReply callback; + + DNS_UNUSED( inRef ); + DNS_UNUSED( inStatusCode ); + + check( inContext ); + obj = (dns_service_discovery_ref) inContext; + check( obj->callback ); + callback = (DNSServiceBrowserReply) obj->callback; + + switch( inEvent->type ) + { + case kDNSBrowserEventTypeAddService: + debugf( DEBUG_NAME "add service \"%s.%s%s\"\n", + inEvent->data.addService.name, + inEvent->data.addService.type, + inEvent->data.addService.domain ); + + if( callback ) + { + callback( DNSServiceBrowserReplyAddInstance, + inEvent->data.addService.name, + inEvent->data.addService.type, + inEvent->data.addService.domain, + DNSServiceDiscoverReplyFlagsFinished, + obj->context ); + } + break; + + case kDNSBrowserEventTypeRemoveService: + debugf( DEBUG_NAME "remove service \"%s.%s%s\"\n", + inEvent->data.removeService.name, + inEvent->data.removeService.type, + inEvent->data.removeService.domain ); + + if( callback ) + { + callback( DNSServiceBrowserReplyRemoveInstance, + inEvent->data.removeService.name, + inEvent->data.removeService.type, + inEvent->data.removeService.domain, + DNSServiceDiscoverReplyFlagsFinished, + obj->context ); + } + break; + + default: + break; + } +} + +//=========================================================================================================================== +// DNSServiceResolverResolve +//=========================================================================================================================== + +dns_service_discovery_ref + DNSServiceResolverResolve( + const char * inName, + const char * inType, + const char * inDomain, + DNSServiceResolverReply inCallBack, + void * inContext ) +{ + DNSStatus err; + dns_service_discovery_ref result; + dns_service_discovery_ref obj; + DNSResolverRef resolver; + + result = NULL; + + // Allocate and initialize the object. + + obj = (dns_service_discovery_ref) malloc( sizeof( *obj ) ); + require_action( obj, exit, err = kDNSNoMemoryErr ); + + obj->type = kDNSServiceDiscoveryObjectTypeResolver; + obj->ref = NULL; + obj->callback = inCallBack; + obj->context = inContext; + + // Create the underlying resolver and start searching for domains. + + err = DNSResolverCreate( 0, inName, inType, inDomain, DNSServiceResolverPrivateCallBack, obj, NULL, &resolver ); + require_noerr( err, exit ); + obj->ref = resolver; + + // Success! + + result = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceDiscoveryDeallocate( obj ); + } + return( result ); +} + +//=========================================================================================================================== +// DNSServiceResolverPrivateCallBack +//=========================================================================================================================== + +DNS_LOCAL void + DNSServiceResolverPrivateCallBack( + void * inContext, + DNSResolverRef inRef, + DNSStatus inStatusCode, + const DNSResolverEvent * inEvent ) +{ + dns_service_discovery_ref obj; + DNSServiceResolverReply callback; + struct sockaddr_in interfaceAddr; + struct sockaddr_in addr; + + DNS_UNUSED( inRef ); + DNS_UNUSED( inStatusCode ); + + check( inContext ); + obj = (dns_service_discovery_ref) inContext; + check( obj->callback ); + callback = (DNSServiceResolverReply) obj->callback; + + switch( inEvent->type ) + { + case kDNSResolverEventTypeResolved: + debugf( DEBUG_NAME "resolved \"%s.%s%s\"\n", + inEvent->data.resolved.name, + inEvent->data.resolved.type, + inEvent->data.resolved.domain ); + + memset( &interfaceAddr, 0, sizeof( interfaceAddr ) ); + interfaceAddr.sin_family = AF_INET; + interfaceAddr.sin_port = 0; + interfaceAddr.sin_addr.s_addr = inEvent->data.resolved.interfaceIP.u.ipv4.addr.v32; + + memset( &addr, 0, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = inEvent->data.resolved.address.u.ipv4.port.v16; + addr.sin_addr.s_addr = inEvent->data.resolved.address.u.ipv4.addr.v32; + + if( callback ) + { + callback( (struct sockaddr *) &interfaceAddr, (struct sockaddr *) &addr, inEvent->data.resolved.textRecord, + DNSServiceDiscoverReplyFlagsFinished, obj->context ); + } + break; + + default: + break; + } +} + +//=========================================================================================================================== +// DNSServiceDiscoveryMachPort +//=========================================================================================================================== + +mach_port_t DNSServiceDiscoveryMachPort( dns_service_discovery_ref inRef ) +{ + DNS_UNUSED( inRef ); + + debugf( DEBUG_NAME "DNSServiceDiscoveryMachPort is not supported\n" ); + return( 0 ); +} + +//=========================================================================================================================== +// DNSServiceDiscoveryDeallocate +//=========================================================================================================================== + +void DNSServiceDiscoveryDeallocate( dns_service_discovery_ref inRef ) +{ + _dns_service_discovery_t * obj; + DNSStatus err; + + check( inRef ); + check( inRef->ref ); + + obj = (_dns_service_discovery_t *) inRef; + switch( obj->type ) + { + case kDNSServiceDiscoveryObjectTypeRegistration: + if( inRef->ref ) + { + err = DNSRegistrationRelease( (DNSRegistrationRef) inRef->ref, 0 ); + check_noerr( err ); + } + free( inRef ); + break; + + case kDNSServiceDiscoveryObjectTypeDomainEnumeration: + if( inRef->ref ) + { + err = DNSBrowserRelease( (DNSBrowserRef) inRef->ref, 0 ); + check_noerr( err ); + } + free( inRef ); + break; + + case kDNSServiceDiscoveryObjectTypeBrowser: + if( inRef->ref ) + { + err = DNSBrowserRelease( (DNSBrowserRef) inRef->ref, 0 ); + check_noerr( err ); + } + free( inRef ); + break; + + case kDNSServiceDiscoveryObjectTypeResolver: + if( inRef->ref ) + { + err = DNSResolverRelease( (DNSResolverRef) inRef->ref, 0 ); + check_noerr( err ); + } + free( inRef ); + break; + + default: + debugf( DEBUG_NAME "unknown object type (%d)\n", obj->type ); + break; + } +} + +//=========================================================================================================================== +// DNSServiceDiscovery_handleReply +//=========================================================================================================================== + +void DNSServiceDiscovery_handleReply( void *inReplyMessage ) +{ + DNS_UNUSED( inReplyMessage ); + + debugf( DEBUG_NAME "DNSServiceDiscovery_handleReply is not supported\n" ); +} + +#ifdef __cplusplus + } +#endif diff --git a/mDNSWindows/DNSServices/DNSServiceDiscovery.h b/mDNSWindows/DNSServices/DNSServiceDiscovery.h new file mode 100644 index 0000000..618bbb3 --- /dev/null +++ b/mDNSWindows/DNSServices/DNSServiceDiscovery.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSServiceDiscovery.h,v $ +Revision 1.2 2003/08/20 07:06:34 bradley +Update to APSL 2.0. Updated change history to match other mDNSResponder files. + +Revision 1.1 2003/08/20 06:04:45 bradley +Platform-neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDiscovery API. + +*/ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header DNSServiceDiscovery + + @abstract DNSServiceDiscovery emulation using DNSServices. +*/ + +#ifndef __DNS_SERVICE_DISCOVERY__ +#define __DNS_SERVICE_DISCOVERY__ + +#include + +#if( __MACH__ ) + + #include + + #include + #include + #include + + #include + +#elif( defined( __MWERKS__ ) ) + + #include + +#elif( defined( _MSC_VER ) ) + + typedef signed char int8_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef unsigned char uint8_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef signed short int16_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef unsigned short uint16_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef signed long int32_t; // C99 stdint.h not supported in VC++/VS.NET yet. + typedef unsigned long uint32_t; // C99 stdint.h not supported in VC++/VS.NET yet. + +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +// Note: The following is mostly copied from DNSServiceDiscovery.h. + +// Compatibility types. + +#if( !__MACH__ ) + typedef int mach_port_t; +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef dns_service_discovery_ref + + @abstract Reference to a DNS Service Discovery object. +*/ + +typedef struct _dns_service_discovery_t * dns_service_discovery_ref; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSServiceRegistrationReplyErrorType + + @abstract Error codes. +*/ + +typedef enum +{ + kDNSServiceDiscoveryWaiting = 1, + kDNSServiceDiscoveryNoError = 0, + + // mDNS Error codes are in the range + // FFFE FF00 (-65792) to FFFE FFFF (-65537) + + kDNSServiceDiscoveryUnknownErr = -65537, // 0xFFFE FFFF + kDNSServiceDiscoveryNoSuchNameErr = -65538, + kDNSServiceDiscoveryNoMemoryErr = -65539, + kDNSServiceDiscoveryBadParamErr = -65540, + kDNSServiceDiscoveryBadReferenceErr = -65541, + kDNSServiceDiscoveryBadStateErr = -65542, + kDNSServiceDiscoveryBadFlagsErr = -65543, + kDNSServiceDiscoveryUnsupportedErr = -65544, + kDNSServiceDiscoveryNotInitializedErr = -65545, + kDNSServiceDiscoveryNoCache = -65546, + kDNSServiceDiscoveryAlreadyRegistered = -65547, + kDNSServiceDiscoveryNameConflict = -65548, + kDNSServiceDiscoveryInvalid = -65549, + kDNSServiceDiscoveryMemFree = -65792 // 0xFFFE FF00 + +} DNSServiceRegistrationReplyErrorType; + +typedef uint32_t DNSRecordReference; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! + @function DNSServiceResolver_handleReply + + @param replyMsg The Mach message. + + @description + + This function should be called with the Mach message sent to the port returned by the call to DNSServiceResolverResolve. + The reply message will be interpreted and will result in a call to the specified callout function. +*/ + +void DNSServiceDiscovery_handleReply( void *replyMsg ); + +/* Service Registration */ + +typedef void (*DNSServiceRegistrationReply) ( + DNSServiceRegistrationReplyErrorType errorCode, + void *context +); + +/*! +@function DNSServiceRegistrationCreate + @description Register a named service with DNS Service Discovery + @param name The name of this service instance (e.g. "Steve's Printer") + @param regtype The service type (e.g. "_printer._tcp." -- see + RFC 2782 (DNS SRV) and ) + @param domain The domain in which to register the service (e.g. "apple.com.") + @param port The local port on which this service is being offered (in network byte order) + @param txtRecord Optional protocol-specific additional information + @param callBack The DNSServiceRegistrationReply function to be called + @param context A user specified context which will be passed to the callout function. + @result A dns_registration_t +*/ +dns_service_discovery_ref DNSServiceRegistrationCreate +( + const char *name, + const char *regtype, + const char *domain, + uint16_t port, + const char *txtRecord, + DNSServiceRegistrationReply callBack, + void *context +); + +/***************************************************************************/ +/* DNS Domain Enumeration */ + +typedef enum +{ + DNSServiceDomainEnumerationReplyAddDomain, // Domain found + DNSServiceDomainEnumerationReplyAddDomainDefault, // Domain found (and should be selected by default) + DNSServiceDomainEnumerationReplyRemoveDomain, // Domain has been removed from network +} DNSServiceDomainEnumerationReplyResultType; + +typedef enum +{ + DNSServiceDiscoverReplyFlagsFinished, + DNSServiceDiscoverReplyFlagsMoreComing, +} DNSServiceDiscoveryReplyFlags; + +typedef void (*DNSServiceDomainEnumerationReply) ( + DNSServiceDomainEnumerationReplyResultType resultType, // One of DNSServiceDomainEnumerationReplyResultType + const char *replyDomain, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context +); + +/*! + @function DNSServiceDomainEnumerationCreate + @description Asynchronously create a DNS Domain Enumerator + @param registrationDomains A boolean indicating whether you are looking + for recommended registration domains + (e.g. equivalent to the AppleTalk zone list in the AppleTalk Control Panel) + or recommended browsing domains + (e.g. equivalent to the AppleTalk zone list in the Chooser). + @param callBack The function to be called when domains are found or removed + @param context A user specified context which will be passed to the callout function. + @result A dns_registration_t +*/ +dns_service_discovery_ref DNSServiceDomainEnumerationCreate +( + int registrationDomains, + DNSServiceDomainEnumerationReply callBack, + void *context +); + +/***************************************************************************/ +/* DNS Service Browser */ + +typedef enum +{ + DNSServiceBrowserReplyAddInstance, // Instance of service found + DNSServiceBrowserReplyRemoveInstance // Instance has been removed from network +} DNSServiceBrowserReplyResultType; + +typedef void (*DNSServiceBrowserReply) ( + DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType + const char *replyName, + const char *replyType, + const char *replyDomain, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context +); + +/*! + @function DNSServiceBrowserCreate + @description Asynchronously create a DNS Service browser + @param regtype The type of service + @param domain The domain in which to find the service + @param callBack The function to be called when service instances are found or removed + @param context A user specified context which will be passed to the callout function. + @result A dns_registration_t +*/ +dns_service_discovery_ref DNSServiceBrowserCreate +( + const char *regtype, + const char *domain, + DNSServiceBrowserReply callBack, + void *context +); + +/***************************************************************************/ +/* Resolver requests */ + +typedef void (*DNSServiceResolverReply) ( + struct sockaddr *interfaceAddr, // Needed for scoped addresses like link-local + struct sockaddr *address, + const char *txtRecord, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context +); + +/*! +@function DNSServiceResolverResolve + @description Resolved a named instance of a service to its address, port, and + (optionally) other demultiplexing information contained in the TXT record. + @param name The name of the service instance + @param regtype The type of service + @param domain The domain in which to find the service + @param callBack The DNSServiceResolverReply function to be called when the specified + address has been resolved. + @param context A user specified context which will be passed to the callout function. + @result A dns_registration_t +*/ + +dns_service_discovery_ref DNSServiceResolverResolve +( + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolverReply callBack, + void *context +); + +/***************************************************************************/ +/* Mach port accessor and deallocation */ + +/*! + @function DNSServiceDiscoveryMachPort + @description Returns the mach port for a dns_service_discovery_ref + @param registration A dns_service_discovery_ref as returned from DNSServiceRegistrationCreate + @result A mach reply port which will be sent messages as appropriate. + These messages should be passed to the DNSServiceDiscovery_handleReply + function. A NULL value indicates that no address was + specified or some other error occurred which prevented the + resolution from being started. +*/ +mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDiscovery); + +/*! + @function DNSServiceDiscoveryDeallocate + @description Deallocates the DNS Service Discovery type / closes the connection to the server + @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a creation or enumeration call + @result void +*/ +void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery); + +/***************************************************************************/ +/* Registration updating */ + + +/*! + @function DNSServiceRegistrationAddRecord + @description Request that the mDNS Responder add the DNS Record of a specific type + @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call + @param rrtype A standard DNS Resource Record Type, from http://www.iana.org/assignments/dns-parameters + @param rdlen Length of the data + @param rdata Opaque binary Resource Record data, up to 64 kB. + @param ttl time to live for the added record. + @result DNSRecordReference An opaque reference that can be passed to the update and remove record calls. If an error occurs, this value will be zero or negative +*/ +DNSRecordReference DNSServiceRegistrationAddRecord(dns_service_discovery_ref dnsServiceDiscovery, uint16_t rrtype, uint16_t rdlen, const char *rdata, uint32_t ttl); + +/*! + @function DNSServiceRegistrationUpdateRecord + @description Request that the mDNS Responder add the DNS Record of a specific type + @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call + @param dnsRecordReference A dnsRecordReference as returned from a DNSServiceRegistrationAddRecord call + @param rdlen Length of the data + @param rdata Opaque binary Resource Record data, up to 64 kB. + @param ttl time to live for the updated record. + @result DNSServiceRegistrationReplyErrorType If an error occurs, this value will be non zero +*/ +DNSServiceRegistrationReplyErrorType DNSServiceRegistrationUpdateRecord(dns_service_discovery_ref ref, DNSRecordReference reference, uint16_t rdlen, const char *rdata, uint32_t ttl); + +/*! + @function DNSServiceRegistrationRemoveRecord + @description Request that the mDNS Responder remove the DNS Record(s) of a specific type + @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call + @param dnsRecordReference A dnsRecordReference as returned from a DNSServiceRegistrationAddRecord call + @result DNSServiceRegistrationReplyErrorType If an error occurs, this value will be non zero +*/ + +DNSServiceRegistrationReplyErrorType DNSServiceRegistrationRemoveRecord(dns_service_discovery_ref ref, DNSRecordReference reference); + +#ifdef __cplusplus + } +#endif + +#endif // __DNS_SERVICE_DISCOVERY__ diff --git a/mDNSWindows/DNSServices/DNSServices.c b/mDNSWindows/DNSServices/DNSServices.c new file mode 100755 index 0000000..ca84436 --- /dev/null +++ b/mDNSWindows/DNSServices/DNSServices.c @@ -0,0 +1,3158 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSServices.c,v $ +Revision 1.15 2003/08/20 06:44:24 bradley +Updated to latest internal version of the Rendezvous for Windows code: Added support for interface +specific registrations; Added support for no-such-service registrations; Added support for host +name registrations; Added support for host proxy and service proxy registrations; Added support for +registration record updates (e.g. TXT record updates); Added support for using either a single C +string TXT record, a raw, pre-formatted TXT record potentially containing multiple character string +entries, or a C-string containing a Mac OS X-style \001-delimited set of TXT record character +strings; Added support in resolve callbacks for providing both a simplified C-string for TXT records +and a ptr/size for the raw TXT record data; Added utility routines for dynamically building TXT +records from a variety of sources (\001-delimited, individual strings, etc.) and converting TXT +records to various formats for use in apps; Added utility routines to validate DNS names, DNS +service types, and TXT records; Moved to portable address representation unions (byte-stream vs host +order integer) for consistency, to avoid swapping between host and network byte order, and for IPv6 +support; Removed dependence on modified mDNSCore: define structures and prototypes locally; Added +support for automatically renaming services on name conflicts; Detect and correct TXT records from +old versions of mDNS that treated a TXT record as an arbitrary block of data, but prevent other +malformed TXT records from being accepted; Added many more error codes; Added complete HeaderDoc for +all constants, structures, typedefs, macros, and functions. Various other minor cleanup and fixes. + +Revision 1.14 2003/08/14 02:19:56 cheshire + Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord + +Revision 1.13 2003/08/12 19:56:29 cheshire +Update to APSL 2.0 + +Revision 1.12 2003/07/23 00:00:04 cheshire +Add comments + +Revision 1.11 2003/07/15 01:55:17 cheshire + Need to implement service registration with subtypes + +Revision 1.10 2003/07/02 21:20:10 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.9 2003/05/26 03:21:30 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.8 2003/05/06 00:00:51 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.7 2003/03/27 03:30:57 cheshire + Name conflicts not handled properly, resulting in memory corruption, and eventual crash +Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback +Fixes: +1. Make mDNS_DeregisterInterface() safe to call from a callback +2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead + (it never really needed to deregister the interface at all) + +Revision 1.6 2003/03/22 02:57:45 cheshire +Updated mDNSWindows to use new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") + +Revision 1.5 2003/02/20 00:59:04 cheshire +Brought Windows code up to date so it complies with +Josh Graessley's interface changes for IPv6 support. +(Actual support for IPv6 on Windows will come later.) + +Revision 1.4 2002/09/21 20:44:56 zarzycki +Added APSL info + +Revision 1.3 2002/09/20 08:36:50 bradley +Fixed debug messages to output the correct information when resolving. + +Revision 1.2 2002/09/20 05:58:01 bradley +DNS Services for Windows + +*/ + +#include +#include +#include + +#if( __MACH__ ) + #include +#endif + +#include "mDNSClientAPI.h" +#include "mDNSPlatformFunctions.h" + +#include "DNSServices.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == Preprocessor == +#endif + +//=========================================================================================================================== +// Preprocessor +//=========================================================================================================================== + +#if( defined( _MSC_VER ) ) + #pragma warning( disable:4068 ) // Disable "unknown pragma" warning for "pragma unused". + #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[DNSServices] " + +enum +{ + kDNSInitializeValidFlags = kDNSFlagAdvertise, + + // Browser + + kDNSBrowserCreateValidFlags = 0, + kDNSBrowserReleaseValidFlags = 0, + kDNSBrowserStartDomainSearchValidFlags = kDNSBrowserFlagRegistrationDomainsOnly, + kDNSBrowserStopDomainSearchValidFlags = 0, + kDNSBrowserStartServiceSearchValidFlags = kDNSBrowserFlagAutoResolve, + kDNSBrowserStopServiceSearchValidFlags = 0, + + // Resolver + + kDNSResolverCreateValidFlags = kDNSResolverFlagOneShot | + kDNSResolverFlagOnlyIfUnique | + kDNSResolverFlagAutoReleaseByName, + kDNSResolverReleaseValidFlags = 0, + + // Service Registration + + kDNSRegistrationCreateValidFlags = kDNSRegistrationFlagPreFormattedTextRecord | + kDNSRegistrationFlagAutoRenameOnConflict, + kDNSNoSuchServiceRegistrationCreateValidFlags = 0, + kDNSRegistrationReleaseValidFlags = 0, + kDNSRegistrationUpdateValidFlags = 0, + + kDNSRegistrationFlagPrivateNoSuchService = ( 1 << 16 ), + + // Domain Registration + + kDNSDomainRegistrationCreateValidFlags = 0, + kDNSDomainRegistrationReleaseValidFlags = 0, + + // Host Registration + + kDNSHostRegistrationCreateValidFlags = kDNSHostRegistrationFlagOnlyIfNotFound | + kDNSHostRegistrationFlagAutoRenameOnConflict, + kDNSHostRegistrationReleaseValidFlags = 0 +}; + +#define kDNSCountCacheEntryCountDefault 64 + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +// Browser + +typedef struct DNSBrowser DNSBrowser; +struct DNSBrowser +{ + DNSBrowser * next; + DNSBrowserFlags flags; + DNSBrowserCallBack callback; + void * callbackContext; + mDNSBool isDomainBrowsing; + DNSQuestion domainQuestion; + DNSQuestion defaultDomainQuestion; + DNSBrowserFlags domainSearchFlags; + mDNSBool isServiceBrowsing; + DNSQuestion serviceBrowseQuestion; + DNSBrowserFlags serviceSearchFlags; + char searchDomain[ 256 ]; + char searchServiceType[ 256 ]; +}; + +// Resolver + +typedef struct DNSResolver DNSResolver; +struct DNSResolver +{ + DNSResolver * next; + DNSResolverFlags flags; + DNSResolverCallBack callback; + void * callbackContext; + DNSBrowserRef owner; + ServiceInfoQuery query; + ServiceInfo info; + mDNSBool isResolving; + char resolveName[ 256 ]; + char resolveType[ 256 ]; + char resolveDomain[ 256 ]; +}; + +// Registration + +typedef struct DNSRegistration DNSRegistration; +struct DNSRegistration +{ + DNSRegistration * next; + DNSRegistrationFlags flags; + DNSRegistrationCallBack callback; + void * callbackContext; + char interfaceName[ 256 ]; + ServiceRecordSet set; + + // WARNING: Do not add fields after the ServiceRecordSet. This is where oversized TXT record space is allocated. +}; + +// Domain Registration + +typedef struct DNSDomainRegistration DNSDomainRegistration; +struct DNSDomainRegistration +{ + DNSDomainRegistration * next; + DNSDomainRegistrationFlags flags; + AuthRecord rr; +}; + +// Domain Registration + +typedef struct DNSHostRegistration DNSHostRegistration; +struct DNSHostRegistration +{ + DNSHostRegistration * next; + domainlabel name; + domainlabel domain; + long refCount; + DNSHostRegistrationCallBack callback; + void * callbackContext; + DNSHostRegistrationFlags flags; + char interfaceName[ 256 ]; + AuthRecord RR_A; + AuthRecord RR_PTR; +}; + +#if 0 +#pragma mark == Macros == +#endif + +//=========================================================================================================================== +// Macros +//=========================================================================================================================== + +// Emulate Mac OS debugging macros for non-Mac platforms. + +#if( !TARGET_OS_MAC ) + #define check(assertion) + #define check_string( assertion, cstring ) + #define check_noerr(err) + #define check_noerr_string( error, cstring ) + #define debug_string( cstring ) + #define require( assertion, label ) do { if( !(assertion) ) goto label; } while(0) + #define require_string( assertion, label, string ) require(assertion, label) + #define require_noerr( error, label ) do { if( (error) != 0 ) goto label; } while(0) + #define require_noerr_action( error, label, action ) do { if( (error) != 0 ) { {action;}; goto label; } } while(0) + #define require_action( assertion, label, action ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) + #define require_action_string( assertion, label, action, cstring ) do { if( !(assertion) ) { {action;}; goto label; } } while(0) +#endif + +#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC).c, (DST).c, DomainNameLength(&(SRC))) + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// General + +mDNSlocal void DNSServicesLock( void ); +mDNSlocal void DNSServicesUnlock( void ); +mDNSlocal void DNSServicesMDNSCallBack( mDNS *const inMDNS, mStatus inStatus ); +mDNSlocal void DNSServicesUpdateInterfaceSpecificObjects( mDNS *const inMDNS ); + +// Browser + +mDNSlocal void + DNSBrowserPrivateCallBack( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ); + +mDNSlocal void + DNSBrowserPrivateResolverCallBack( + void * inContext, + DNSResolverRef inRef, + DNSStatus inStatusCode, + const DNSResolverEvent * inEvent ); + +mDNSlocal DNSBrowserRef DNSBrowserFindObject( DNSBrowserRef inRef ); +mDNSlocal DNSBrowserRef DNSBrowserRemoveObject( DNSBrowserRef inRef ); + +// Resolver + +mDNSlocal void DNSResolverPrivateCallBack( mDNS * const inMDNS, ServiceInfoQuery *inQuery ); +mDNSlocal DNSResolverRef DNSResolverFindObject( DNSResolverRef inRef ); +mDNSlocal DNSResolverRef DNSResolverRemoveObject( DNSResolverRef inRef ); +mDNSlocal void DNSResolverRemoveDependentByBrowser( DNSBrowserRef inBrowserRef ); +mDNSlocal void DNSResolverRemoveDependentByName( const domainname *inName ); +mDNSlocal DNSResolverRef DNSResolverFindObjectByName( const domainname *inName ); + +// Registration + +mDNSlocal void + DNSRegistrationPrivateCallBack( + mDNS * const inMDNS, + ServiceRecordSet * const inSet, + mStatus inResult ); + +mDNSlocal void + DNSNoSuchServiceRegistrationPrivateCallBack( + mDNS * const inMDNS, + AuthRecord * const inRR, + mStatus inResult ); + +mDNSlocal void DNSRegistrationUpdateCallBack( mDNS * const inMDNS, AuthRecord * const inRR, RData *inOldData ); + +mDNSlocal DNSRegistrationRef * DNSRegistrationFindObject( DNSRegistrationRef inRef ); +mDNSlocal DNSRegistrationRef DNSRegistrationRemoveObject( DNSRegistrationRef inRef ); + +// Domain Registration + +mDNSlocal DNSDomainRegistrationRef DNSDomainRegistrationRemoveObject( DNSDomainRegistrationRef inRef ); + +// Host Registration + +mDNSlocal DNSHostRegistrationRef * DNSHostRegistrationFindObject( DNSHostRegistrationRef inRef ); +mDNSlocal DNSHostRegistrationRef DNSHostRegistrationFindObjectByName( const domainname *inName ); +mDNSlocal void DNSHostRegistrationPrivateCallBack( mDNS * const inMDNS, AuthRecord *const inRR, mStatus inResult ); + +// Utilities + +mDNSlocal DNSStatus DNSMemAlloc( size_t inSize, void *outMem ); +mDNSlocal void DNSMemFree( void *inMem ); +mDNSlocal void MDNSAddrToDNSAddress( const mDNSAddr *inAddr, DNSNetworkAddress *outAddr ); + +// Platform Accessors + +typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo; +struct mDNSPlatformInterfaceInfo +{ + const char * name; + mDNSAddr ip; +}; + +mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ); +mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ); + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +mDNSexport mDNS gMDNS; +mDNSlocal mDNS * gMDNSPtr = mDNSNULL; +mDNSlocal CacheRecord * gMDNSCache = mDNSNULL; +mDNSlocal DNSBrowserRef gDNSBrowserList = mDNSNULL; +mDNSlocal DNSResolverRef gDNSResolverList = mDNSNULL; +mDNSlocal DNSRegistrationRef gDNSRegistrationList = mDNSNULL; +mDNSlocal DNSDomainRegistrationRef gDNSDomainRegistrationList = mDNSNULL; +mDNSlocal DNSHostRegistrationRef gDNSHostRegistrationList = mDNSNULL; + +#if 0 +#pragma mark - +#pragma mark == General == +#endif + +//=========================================================================================================================== +// DNSServicesInitialize +//=========================================================================================================================== + +DNSStatus DNSServicesInitialize( DNSFlags inFlags, DNSCount inCacheEntryCount ) +{ + DNSStatus err; + mDNSBool advertise; + + require_action( ( inFlags & ~kDNSInitializeValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + + // Allocate the record cache. + + if( inCacheEntryCount == 0 ) + { + inCacheEntryCount = kDNSCountCacheEntryCountDefault; + } + gMDNSCache = (CacheRecord *) malloc( inCacheEntryCount * sizeof( *gMDNSCache ) ); + require_action( gMDNSCache, exit, err = kDNSNoMemoryErr ); + + // Initialize mDNS. + + if( inFlags & kDNSFlagAdvertise ) + { + advertise = mDNS_Init_AdvertiseLocalAddresses; + } + else + { + advertise = mDNS_Init_DontAdvertiseLocalAddresses; + } + err = mDNS_Init( &gMDNS, mDNSNULL, gMDNSCache, inCacheEntryCount, advertise, DNSServicesMDNSCallBack, mDNSNULL ); + require_noerr( err, exit ); + err = gMDNS.mDNSPlatformStatus; + require_noerr( err, exit ); + + gMDNSPtr = &gMDNS; + +exit: + if( err ) + { + DNSServicesFinalize(); + } + return( err ); +} + +//=========================================================================================================================== +// DNSServicesFinalize +//=========================================================================================================================== + +void DNSServicesFinalize( void ) +{ + if( gMDNSPtr ) + { + mDNSPlatformLock( &gMDNS ); + + // Clean up any dangling service registrations. + + while( gDNSRegistrationList ) + { + DNSRegistrationRef serviceRef; + + serviceRef = gDNSRegistrationList; + DNSRegistrationRelease( serviceRef, 0UL ); + check_string( serviceRef != gDNSRegistrationList, "dangling service registration cannot be cleaned up" ); + } + + // Clean up any dangling domain registrations. + + while( gDNSDomainRegistrationList ) + { + DNSDomainRegistrationRef domainRef; + + domainRef = gDNSDomainRegistrationList; + DNSDomainRegistrationRelease( domainRef, 0 ); + check_string( domainRef != gDNSDomainRegistrationList, "dangling domain registration cannot be cleaned up" ); + } + + // Clean up any dangling host registrations. + + while( gDNSHostRegistrationList ) + { + DNSHostRegistrationRef hostRef; + long refCount; + + hostRef = gDNSHostRegistrationList; + refCount = hostRef->refCount; + DNSHostRegistrationRelease( hostRef, 0 ); + check_string( ( refCount > 1 ) || ( hostRef != gDNSHostRegistrationList ), + "dangling host registration cannot be cleaned up" ); + } + + // Clean up any dangling browsers. + + while( gDNSBrowserList ) + { + DNSBrowserRef browserRef; + + browserRef = gDNSBrowserList; + DNSBrowserRelease( browserRef, 0 ); + check_string( browserRef != gDNSBrowserList, "dangling browser cannot be cleaned up" ); + } + + // Clean up any dangling resolvers. + + while( gDNSResolverList ) + { + DNSResolverRef resolverRef; + + resolverRef = gDNSResolverList; + DNSResolverRelease( resolverRef, 0 ); + check_string( resolverRef != gDNSResolverList, "dangling resolver cannot be cleaned up" ); + } + + // Null out our MDNS ptr before releasing the lock so no other threads can sneak in and start operations. + + gMDNSPtr = mDNSNULL; + mDNSPlatformUnlock( &gMDNS ); + + // Tear down mDNS. + + mDNS_Close( &gMDNS ); + } + if( gMDNSCache ) + { + free( gMDNSCache ); + gMDNSCache = mDNSNULL; + } +} + +//=========================================================================================================================== +// DNSServicesLock +//=========================================================================================================================== + +mDNSlocal void DNSServicesLock( void ) +{ + if( gMDNSPtr ) + { + mDNSPlatformLock( gMDNSPtr ); + } +} + +//=========================================================================================================================== +// DNSServicesUnlock +//=========================================================================================================================== + +mDNSlocal void DNSServicesUnlock( void ) +{ + if( gMDNSPtr ) + { + mDNSPlatformUnlock( gMDNSPtr ); + } +} + +//=========================================================================================================================== +// DNSServicesMDNSCallBack +//=========================================================================================================================== + +mDNSlocal void DNSServicesMDNSCallBack( mDNS *const inMDNS, mStatus inStatus ) +{ + DNS_UNUSED( inMDNS ); + DNS_UNUSED( inStatus ); + check( inMDNS ); + + debugf( DEBUG_NAME "MDNS callback (status=%ld)", inStatus ); + + if( inStatus == mStatus_ConfigChanged ) + { + DNSServicesUpdateInterfaceSpecificObjects( inMDNS ); + } +} + +//=========================================================================================================================== +// DNSServicesUpdateInterfaceSpecificObjects +//=========================================================================================================================== + +mDNSlocal void DNSServicesUpdateInterfaceSpecificObjects( mDNS *const inMDNS ) +{ + DNSRegistration * serviceRegistration; + + DNSServicesLock(); + + // Update interface-specific service registrations. + + for( serviceRegistration = gDNSRegistrationList; serviceRegistration; serviceRegistration = serviceRegistration->next ) + { + if( serviceRegistration->interfaceName[ 0 ] != '\0' ) + { + mStatus err; + mDNSInterfaceID interfaceID; + + err = mDNSPlatformInterfaceNameToID( inMDNS, serviceRegistration->interfaceName, &interfaceID ); + check_noerr( err ); + if( err == mStatus_NoError ) + { + // Update all the resource records with the new interface ID. + + serviceRegistration->set.RR_ADV.resrec.InterfaceID = interfaceID; + serviceRegistration->set.RR_PTR.resrec.InterfaceID = interfaceID; + serviceRegistration->set.RR_SRV.resrec.InterfaceID = interfaceID; + serviceRegistration->set.RR_TXT.resrec.InterfaceID = interfaceID; + } + } + } + + DNSServicesUnlock(); +} + +#if 0 +#pragma mark - +#pragma mark == Browser == +#endif + +//=========================================================================================================================== +// DNSBrowserCreate +//=========================================================================================================================== + +DNSStatus + DNSBrowserCreate( + DNSBrowserFlags inFlags, + DNSBrowserCallBack inCallBack, + void * inCallBackContext, + DNSBrowserRef * outRef ) +{ + DNSStatus err; + DNSBrowser * objectPtr; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( ( inFlags & ~kDNSBrowserCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + require_action( inCallBack, exit, err = kDNSBadParamErr ); + + // Allocate the object and set it up. + + err = DNSMemAlloc( sizeof( *objectPtr ), &objectPtr ); + require_noerr( err, exit ); + memset( objectPtr, 0, sizeof( *objectPtr ) ); + + objectPtr->flags = inFlags; + objectPtr->callback = inCallBack; + objectPtr->callbackContext = inCallBackContext; + + // Add the object to the list. + + objectPtr->next = gDNSBrowserList; + gDNSBrowserList = objectPtr; + + if( outRef ) + { + *outRef = objectPtr; + } + +exit: + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSBrowserRelease +//=========================================================================================================================== + +DNSStatus DNSBrowserRelease( DNSBrowserRef inRef, DNSBrowserFlags inFlags ) +{ + DNSStatus err; + DNSBrowserEvent event; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( inRef, exit, err = kDNSBadReferenceErr ); + require_action( ( inFlags & ~kDNSBrowserReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + + // Stop service and domain browsing and remove any resolvers dependent on this browser. + + DNSBrowserStopDomainSearch( inRef, 0 ); + DNSBrowserStopServiceSearch( inRef, 0 ); + DNSResolverRemoveDependentByBrowser( inRef ); + + // Remove the object from the list. + + inRef = DNSBrowserRemoveObject( inRef ); + require_action( inRef, exit, err = kDNSBadReferenceErr ); + + // Call the callback with a release event. + + check( inRef->callback ); + memset( &event, 0, sizeof( event ) ); + event.type = kDNSBrowserEventTypeRelease; + inRef->callback( inRef->callbackContext, inRef, kDNSNoErr, &event ); + + // Release the memory used by the object. + + DNSMemFree( inRef ); + err = kDNSNoErr; + +exit: + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSBrowserStartDomainSearch +//=========================================================================================================================== + +DNSStatus DNSBrowserStartDomainSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags ) +{ + DNSStatus err; + mDNS_DomainType type; + mDNS_DomainType defaultType; + DNSBrowserEvent event; + mDNSBool isDomainBrowsing; + + isDomainBrowsing = mDNSfalse; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( inRef && DNSBrowserFindObject( inRef ), exit, err = kDNSBadReferenceErr ); + require_action( ( inFlags & ~kDNSBrowserStartDomainSearchValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + require_action( !inRef->isDomainBrowsing, exit, err = kDNSBadStateErr ); + + // Determine whether to browse for normal domains or registration domains. + + if( inFlags & kDNSBrowserFlagRegistrationDomainsOnly ) + { + type = mDNS_DomainTypeRegistration; + defaultType = mDNS_DomainTypeRegistrationDefault; + } + else + { + type = mDNS_DomainTypeBrowse; + defaultType = mDNS_DomainTypeBrowseDefault; + } + + // Start the browse operations. + + err = mDNS_GetDomains( gMDNSPtr, &inRef->domainQuestion, type, mDNSInterface_Any, DNSBrowserPrivateCallBack, inRef ); + require_noerr( err, exit ); + isDomainBrowsing = mDNStrue; + + err = mDNS_GetDomains( gMDNSPtr, &inRef->defaultDomainQuestion, defaultType, mDNSInterface_Any, DNSBrowserPrivateCallBack, inRef ); + require_noerr( err, exit ); + + inRef->domainSearchFlags = inFlags; + inRef->isDomainBrowsing = mDNStrue; + + // Call back immediately with "local." since that is always available for all types of browsing. + + memset( &event, 0, sizeof( event ) ); + event.type = kDNSBrowserEventTypeAddDefaultDomain; + event.data.addDefaultDomain.domain = kDNSLocalDomain; + event.data.addDefaultDomain.flags = 0; + inRef->callback( inRef->callbackContext, inRef, kDNSNoErr, &event ); + +exit: + if( err && isDomainBrowsing ) + { + mDNS_StopGetDomains( gMDNSPtr, &inRef->domainQuestion ); + } + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSBrowserStopDomainSearch +//=========================================================================================================================== + +DNSStatus DNSBrowserStopDomainSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags ) +{ + DNSStatus err; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( inRef && DNSBrowserFindObject( inRef ), exit, err = kDNSBadReferenceErr ); + require_action( ( inFlags & ~kDNSBrowserStopDomainSearchValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + if( !inRef->isDomainBrowsing ) + { + err = kDNSBadStateErr; + goto exit; + } + + // Stop the browse operations. + + mDNS_StopGetDomains( gMDNSPtr, &inRef->defaultDomainQuestion ); + mDNS_StopGetDomains( gMDNSPtr, &inRef->domainQuestion ); + inRef->isDomainBrowsing = mDNSfalse; + err = kDNSNoErr; + +exit: + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSBrowserStartServiceSearch +//=========================================================================================================================== + +DNSStatus + DNSBrowserStartServiceSearch( + DNSBrowserRef inRef, + DNSBrowserFlags inFlags, + const char * inType, + const char * inDomain ) +{ + DNSStatus err; + domainname type; + domainname domain; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( inRef && DNSBrowserFindObject( inRef ), exit, err = kDNSBadReferenceErr ); + require_action( ( inFlags & ~kDNSBrowserStartServiceSearchValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + require_action( !inRef->isServiceBrowsing, exit, err = kDNSBadStateErr ); + require_action( inType, exit, err = kDNSBadParamErr ); + + // Default to the local domain when null is passed in. + + if( !inDomain || ( inDomain[ 0 ] == '\0' ) || ( inDomain[ 0 ] == '.' ) ) + { + inDomain = kDNSLocalDomain; + } + + // Save off the search criteria (in case it needs to be automatically restarted later). + + inRef->serviceSearchFlags = inFlags; + + strncpy( inRef->searchServiceType, inType, sizeof( inRef->searchServiceType ) - 1 ); + inRef->searchServiceType[ sizeof( inRef->searchServiceType ) - 1 ] = '\0'; + + strncpy( inRef->searchDomain, inDomain, sizeof( inRef->searchDomain ) - 1 ); + inRef->searchDomain[ sizeof( inRef->searchDomain ) - 1 ] = '\0'; + + // Start the browse operation with mDNS using our private callback. + + MakeDomainNameFromDNSNameString( &type, inType ); + MakeDomainNameFromDNSNameString( &domain, inDomain ); + + err = mDNS_StartBrowse( gMDNSPtr, &inRef->serviceBrowseQuestion, &type, &domain, mDNSInterface_Any, + DNSBrowserPrivateCallBack, inRef ); + require_noerr( err, exit ); + + inRef->isServiceBrowsing = mDNStrue; + +exit: + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSBrowserStopServiceSearch +//=========================================================================================================================== + +DNSStatus DNSBrowserStopServiceSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags ) +{ + DNSStatus err; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( inRef && DNSBrowserFindObject( inRef ), exit, err = kDNSBadReferenceErr ); + require_action( ( inFlags & ~kDNSBrowserStopServiceSearchValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + if( !inRef->isServiceBrowsing ) + { + err = kDNSBadStateErr; + goto exit; + } + + // Stop the browse operation with mDNS. Remove any resolvers dependent on browser since we are no longer searching. + + mDNS_StopBrowse( gMDNSPtr, &inRef->serviceBrowseQuestion ); + DNSResolverRemoveDependentByBrowser( inRef ); + inRef->isServiceBrowsing = mDNSfalse; + err = kDNSNoErr; + +exit: + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSBrowserPrivateCallBack +//=========================================================================================================================== + +mDNSlocal void + DNSBrowserPrivateCallBack( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ) +{ + DNSBrowserRef objectPtr; + domainlabel name; + domainname type; + domainname domain; + char nameString[ 256 ]; + char typeString[ 256 ]; + char domainString[ 256 ]; + DNSBrowserEvent event; + mStatus err; + + check( inMDNS ); + check( inQuestion ); + check( inAnswer ); + + DNSServicesLock(); + + // Exclude non-PTR answers. + + require( inAnswer->rrtype == kDNSType_PTR, exit ); + + // Exit if object is no longer valid. Should never happen. + + objectPtr = DNSBrowserFindObject( (DNSBrowserRef) inQuestion->QuestionContext ); + require( objectPtr, exit ); + + // Determine what type of callback it is based on the question. + + memset( &event, 0, sizeof( event ) ); + if( inQuestion == &objectPtr->serviceBrowseQuestion ) + { + DNSBrowserEventServiceData * serviceDataPtr; + DNSBrowserFlags browserFlags; + + // Extract name, type, and domain from the resource record. + + DeconstructServiceName( &inAnswer->rdata->u.name, &name, &type, &domain ); + ConvertDomainLabelToCString_unescaped( &name, nameString ); + ConvertDomainNameToCString( &type, typeString ); + ConvertDomainNameToCString( &domain, domainString ); + + // Fill in the event data. A TTL of zero means the service is no longer available. If the service instance is going + // away (ttl == 0), remove any resolvers dependent on the name since it is no longer valid. + + if( !inAddRecord ) + { + DNSResolverRemoveDependentByName( &inAnswer->rdata->u.name ); + + event.type = kDNSBrowserEventTypeRemoveService; + serviceDataPtr = &event.data.removeService; + } + else + { + event.type = kDNSBrowserEventTypeAddService; + serviceDataPtr = &event.data.addService; + } + serviceDataPtr->interfaceName = ""; + if( inAnswer->InterfaceID != mDNSInterface_Any ) + { + mDNSPlatformInterfaceInfo info; + + err = mDNSPlatformInterfaceIDToInfo( inMDNS, inAnswer->InterfaceID, &info ); + if( err == mStatus_NoError ) + { + serviceDataPtr->interfaceName = info.name; + MDNSAddrToDNSAddress( &info.ip, &serviceDataPtr->interfaceIP ); + } + else + { + serviceDataPtr->interfaceName = ""; + } + } + serviceDataPtr->interfaceID = inAnswer->InterfaceID; + serviceDataPtr->name = nameString; + serviceDataPtr->type = typeString; + serviceDataPtr->domain = domainString; + serviceDataPtr->flags = 0; + + // Call the callback. + + browserFlags = objectPtr->serviceSearchFlags; + objectPtr->callback( objectPtr->callbackContext, objectPtr, kDNSNoErr, &event ); + + // Automatically resolve newly discovered names if the auto-resolve option is enabled. + + if( ( browserFlags & kDNSBrowserFlagAutoResolve ) && inAddRecord ) + { + DNSStatus err; + DNSResolverFlags flags; + + flags = kDNSResolverFlagOnlyIfUnique | kDNSResolverFlagAutoReleaseByName; + err = DNSResolverCreate( flags, nameString, typeString, domainString, DNSBrowserPrivateResolverCallBack, + mDNSNULL, objectPtr, mDNSNULL ); + check_noerr( err ); + } + } + else + { + DNSBrowserEventDomainData * domainDataPtr; + + // Determine the event type. A TTL of zero means the domain is no longer available. + + domainDataPtr = mDNSNULL; + if( inQuestion == &objectPtr->domainQuestion ) + { + if( !inAddRecord ) + { + event.type = kDNSBrowserEventTypeRemoveDomain; + domainDataPtr = &event.data.removeDomain; + } + else + { + event.type = kDNSBrowserEventTypeAddDomain; + domainDataPtr = &event.data.addDomain; + } + } + else if( inQuestion == &objectPtr->defaultDomainQuestion ) + { + if( !inAddRecord ) + { + event.type = kDNSBrowserEventTypeRemoveDomain; + domainDataPtr = &event.data.removeDomain; + } + else + { + event.type = kDNSBrowserEventTypeAddDefaultDomain; + domainDataPtr = &event.data.addDefaultDomain; + } + } + require_string( domainDataPtr, exit, "domain response for unknown question" ); + + // Extract domain name from the resource record and fill in the event data. + + ConvertDomainNameToCString( &inAnswer->rdata->u.name, domainString ); + + domainDataPtr->interfaceName = ""; + if( inAnswer->InterfaceID != mDNSInterface_Any ) + { + mDNSPlatformInterfaceInfo info; + + err = mDNSPlatformInterfaceIDToInfo( inMDNS, inAnswer->InterfaceID, &info ); + if( err == mStatus_NoError ) + { + domainDataPtr->interfaceName = info.name; + MDNSAddrToDNSAddress( &info.ip, &domainDataPtr->interfaceIP ); + } + else + { + domainDataPtr->interfaceName = ""; + } + } + domainDataPtr->interfaceID = inAnswer->InterfaceID; + domainDataPtr->domain = domainString; + domainDataPtr->flags = 0; + + // Call the callback. + + objectPtr->callback( objectPtr->callbackContext, objectPtr, kDNSNoErr, &event ); + } + +exit: + DNSServicesUnlock(); +} + +//=========================================================================================================================== +// DNSBrowserPrivateResolverCallBack +//=========================================================================================================================== + +mDNSlocal void + DNSBrowserPrivateResolverCallBack( + void * inContext, + DNSResolverRef inRef, + DNSStatus inStatusCode, + const DNSResolverEvent * inEvent ) +{ + DNSBrowserRef objectPtr; + DNSBrowserEvent event; + + DNS_UNUSED( inContext ); + DNS_UNUSED( inStatusCode ); + + DNSServicesLock(); + + // Exit if object is no longer valid. Should never happen. + + objectPtr = inRef->owner; + require( objectPtr, exit ); + + switch( inEvent->type ) + { + case kDNSResolverEventTypeResolved: + verbosedebugf( DEBUG_NAME "private resolver callback: resolved (ref=0x%08X)", inRef ); + verbosedebugf( DEBUG_NAME " name: \"%s\"", inEvent->data.resolved.name ); + verbosedebugf( DEBUG_NAME " type: \"%s\"", inEvent->data.resolved.type ); + verbosedebugf( DEBUG_NAME " domain: \"%s\"", inEvent->data.resolved.domain ); + verbosedebugf( DEBUG_NAME " if: %.4a", &inEvent->data.resolved.interfaceIP.u.ipv4.addr.v32 ); + verbosedebugf( DEBUG_NAME " ip: %.4a:%u", &inEvent->data.resolved.address.u.ipv4.addr.v32, + ( inEvent->data.resolved.address.u.ipv4.port.v8[ 0 ] << 8 ) | + inEvent->data.resolved.address.u.ipv4.port.v8[ 1 ] ); + verbosedebugf( DEBUG_NAME " text: \"%s\"", inEvent->data.resolved.textRecord ); + + // Re-package the resolver event as a browser event and call the callback. + + memset( &event, 0, sizeof( event ) ); + event.type = kDNSBrowserEventTypeResolved; + event.data.resolved = &inEvent->data.resolved; + + objectPtr->callback( objectPtr->callbackContext, objectPtr, kDNSNoErr, &event ); + break; + + case kDNSResolverEventTypeRelease: + verbosedebugf( DEBUG_NAME "private resolver callback: release (ref=0x%08X)", inRef ); + break; + + default: + verbosedebugf( DEBUG_NAME "private resolver callback: unknown event (ref=0x%08X, event=%ld)", inRef, inEvent->type ); + break; + } + +exit: + DNSServicesUnlock(); +} + +//=========================================================================================================================== +// DNSBrowserFindObject +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSBrowserRef DNSBrowserFindObject( DNSBrowserRef inRef ) +{ + DNSBrowser * p; + + check( inRef ); + + // Find the object in the list. + + for( p = gDNSBrowserList; p; p = p->next ) + { + if( p == inRef ) + { + break; + } + } + return( p ); +} + +//=========================================================================================================================== +// DNSBrowserRemoveObject +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSBrowserRef DNSBrowserRemoveObject( DNSBrowserRef inRef ) +{ + DNSBrowser ** p; + DNSBrowser * found; + + for( p = &gDNSBrowserList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + found = *p; + if( found ) + { + *p = found->next; + } + return( found ); +} + +#if 0 +#pragma mark - +#pragma mark == Resolver == +#endif + +//=========================================================================================================================== +// DNSResolverCreate +//=========================================================================================================================== + +DNSStatus + DNSResolverCreate( + DNSResolverFlags inFlags, + const char * inName, + const char * inType, + const char * inDomain, + DNSResolverCallBack inCallBack, + void * inCallBackContext, + DNSBrowserRef inOwner, + DNSResolverRef * outRef ) +{ + DNSStatus err; + int isAutoRelease; + DNSResolver * objectPtr; + domainlabel name; + domainname type; + domainname domain; + domainname fullName; + + objectPtr = mDNSNULL; + + // Check parameters. + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( ( inFlags & ~kDNSResolverCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + require_action( inName, exit, err = kDNSBadParamErr ); + require_action( inType, exit, err = kDNSBadParamErr ); + require_action( inDomain, exit, err = kDNSBadParamErr ); + require_action( inCallBack, exit, err = kDNSBadParamErr ); + isAutoRelease = inOwner || ( inFlags & ( kDNSResolverFlagOneShot | kDNSResolverFlagAutoReleaseByName ) ); + require_action( outRef || isAutoRelease, exit, err = kDNSBadParamErr ); + require_action( !inOwner || DNSBrowserFindObject( inOwner ), exit, err = kDNSBadReferenceErr ); + + // Convert and package up the name, type, and domain into a single fully-qualified domain name to resolve. + + MakeDomainLabelFromLiteralString( &name, inName ); + MakeDomainNameFromDNSNameString( &type, inType ); + MakeDomainNameFromDNSNameString( &domain, inDomain ); + ConstructServiceName( &fullName, &name, &type, &domain ); + + // If the caller only wants to add unique resolvers, check if a resolver for this name is already present. + + if( inFlags & kDNSResolverFlagOnlyIfUnique ) + { + if( DNSResolverFindObjectByName( &fullName ) ) + { + if( outRef ) + { + *outRef = mDNSNULL; + } + err = kDNSNoErr; + goto exit; + } + } + + // Allocate the object and set it up. + + err = DNSMemAlloc( sizeof( *objectPtr ), &objectPtr ); + require_noerr( err, exit ); + memset( objectPtr, 0, sizeof( *objectPtr ) ); + + objectPtr->flags = inFlags; + objectPtr->callback = inCallBack; + objectPtr->callbackContext = inCallBackContext; + objectPtr->owner = inOwner; + AssignDomainName( objectPtr->info.name, fullName ); + objectPtr->info.InterfaceID = mDNSInterface_Any; + + // Save off the resolve info so the callback can get it. + + strncpy( objectPtr->resolveName, inName, sizeof( objectPtr->resolveName ) - 1 ); + objectPtr->resolveName[ sizeof( objectPtr->resolveName ) - 1 ] = '\0'; + + strncpy( objectPtr->resolveType, inType, sizeof( objectPtr->resolveType ) - 1 ); + objectPtr->resolveType[ sizeof( objectPtr->resolveType ) - 1 ] = '\0'; + + strncpy( objectPtr->resolveDomain, inDomain, sizeof( objectPtr->resolveDomain ) - 1 ); + objectPtr->resolveDomain[ sizeof( objectPtr->resolveDomain ) - 1 ] = '\0'; + + // Add the object to the list. + + objectPtr->next = gDNSResolverList; + gDNSResolverList = objectPtr; + + // Start the resolving process. + + objectPtr->isResolving = mDNStrue; + err = mDNS_StartResolveService( gMDNSPtr, &objectPtr->query, &objectPtr->info, DNSResolverPrivateCallBack, objectPtr ); + require_noerr( err, exit ); + + if( outRef ) + { + *outRef = objectPtr; + } + +exit: + if( err && objectPtr ) + { + DNSResolverRemoveObject( objectPtr ); + DNSMemFree( objectPtr ); + } + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSResolverRelease +//=========================================================================================================================== + +DNSStatus DNSResolverRelease( DNSResolverRef inRef, DNSResolverFlags inFlags ) +{ + DNSStatus err; + DNSResolverEvent event; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( ( inFlags & ~kDNSResolverReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + + // Remove the object from the list. + + inRef = DNSResolverRemoveObject( inRef ); + require_action( inRef, exit, err = kDNSBadReferenceErr ); + + // Stop the resolving process. + + if( inRef->isResolving ) + { + inRef->isResolving = mDNSfalse; + mDNS_StopResolveService( gMDNSPtr, &inRef->query ); + } + + // Call the callback with a release event. + + check( inRef->callback ); + memset( &event, 0, sizeof( event ) ); + event.type = kDNSResolverEventTypeRelease; + inRef->callback( inRef->callbackContext, inRef, kDNSNoErr, &event ); + + // Release the memory used by the object. + + DNSMemFree( inRef ); + err = kDNSNoErr; + +exit: + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSResolverFindObject +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSResolverRef DNSResolverFindObject( DNSResolverRef inRef ) +{ + DNSResolver * p; + + check( inRef ); + + // Find the object in the list. + + for( p = gDNSResolverList; p; p = p->next ) + { + if( p == inRef ) + { + break; + } + } + return( p ); +} + +//=========================================================================================================================== +// DNSResolverFindObjectByName +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSResolverRef DNSResolverFindObjectByName( const domainname *inName ) +{ + DNSResolver * p; + + check( inName ); + + for( p = gDNSResolverList; p; p = p->next ) + { + if( SameDomainName( &p->info.name, inName ) ) + { + break; + } + } + return( p ); +} + +//=========================================================================================================================== +// DNSResolverPrivateCallBack +//=========================================================================================================================== + +mDNSlocal void DNSResolverPrivateCallBack( mDNS * const inMDNS, ServiceInfoQuery *inQuery ) +{ + DNSResolverRef objectPtr; + DNSResolverEvent event; + char * txtString; + mStatus err; + mDNSBool release; + + txtString = NULL; + + DNSServicesLock(); + + // Exit if object is no longer valid. Should never happen. + + objectPtr = DNSResolverFindObject( (DNSResolverRef) inQuery->ServiceInfoQueryContext ); + require( objectPtr, exit ); + + // Convert the raw TXT record into a null-terminated string with \001-delimited records for Mac OS X-style clients. + + err = DNSTextRecordEscape( inQuery->info->TXTinfo, inQuery->info->TXTlen, &txtString ); + check_noerr( err ); + + // Package up the results and call the callback. + + memset( &event, 0, sizeof( event ) ); + event.type = kDNSResolverEventTypeResolved; + event.data.resolved.name = objectPtr->resolveName; + event.data.resolved.type = objectPtr->resolveType; + event.data.resolved.domain = objectPtr->resolveDomain; + event.data.resolved.interfaceName = ""; + if( inQuery->info->InterfaceID != mDNSInterface_Any ) + { + mDNSPlatformInterfaceInfo info; + + err = mDNSPlatformInterfaceIDToInfo( inMDNS, inQuery->info->InterfaceID, &info ); + if( err == mStatus_NoError ) + { + event.data.resolved.interfaceName = info.name; + MDNSAddrToDNSAddress( &info.ip, &event.data.resolved.interfaceIP ); + } + else + { + event.data.resolved.interfaceName = ""; + } + } + event.data.resolved.interfaceID = inQuery->info->InterfaceID; + event.data.resolved.address.addressType = kDNSNetworkAddressTypeIPv4; + event.data.resolved.address.u.ipv4.addr.v32 = inQuery->info->ip.ip.v4.NotAnInteger; + event.data.resolved.address.u.ipv4.port.v16 = inQuery->info->port.NotAnInteger; + event.data.resolved.textRecord = txtString ? txtString : ""; + event.data.resolved.flags = 0; + event.data.resolved.textRecordRaw = (const void *) inQuery->info->TXTinfo; + event.data.resolved.textRecordRawSize = (DNSCount) inQuery->info->TXTlen; + release = (mDNSBool)( ( objectPtr->flags & kDNSResolverFlagOneShot ) != 0 ); + objectPtr->callback( objectPtr->callbackContext, objectPtr, kDNSNoErr, &event ); + + // Auto-release the object if needed. + + if( release ) + { + DNSResolverRelease( objectPtr, 0 ); + } + +exit: + DNSServicesUnlock(); + if( txtString ) + { + free( txtString ); + } +} + +//=========================================================================================================================== +// DNSResolverRemoveObject +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSResolverRef DNSResolverRemoveObject( DNSResolverRef inRef ) +{ + DNSResolver ** p; + DNSResolver * found; + + for( p = &gDNSResolverList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + found = *p; + if( found ) + { + *p = found->next; + } + return( found ); +} + +//=========================================================================================================================== +// DNSResolverRemoveDependentByBrowser +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal void DNSResolverRemoveDependentByBrowser( DNSBrowserRef inBrowserRef ) +{ + DNSResolver * p; + + check( inBrowserRef ); + + // Removes all the resolver objects dependent on the specified browser. Restart the search from the beginning of the + // list after each removal to handle the list changing in possible callbacks that may be invoked. + + do + { + for( p = gDNSResolverList; p; p = p->next ) + { + if( p->owner == inBrowserRef ) + { + DNSResolverRelease( p, 0 ); + break; + } + } + + } while( p ); +} + +//=========================================================================================================================== +// DNSResolverRemoveDependentByName +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal void DNSResolverRemoveDependentByName( const domainname *inName ) +{ + DNSResolver * p; + + check( inName ); + + // Removes all the resolver objects dependent on the specified name that want to be auto-released by name. Restart + // the search from the beginning of the list after each removal to handle the list changing in possible callbacks + // that may be invoked. + + do + { + for( p = gDNSResolverList; p; p = p->next ) + { + if( ( p->flags & kDNSResolverFlagAutoReleaseByName ) && SameDomainName( &p->info.name, inName ) ) + { + DNSResolverRelease( p, 0 ); + break; + } + } + + } while( p ); +} + +#if 0 +#pragma mark - +#pragma mark == Registration == +#endif + +//=========================================================================================================================== +// DNSRegistrationCreate +//=========================================================================================================================== + +DNSStatus + DNSRegistrationCreate( + DNSRegistrationFlags inFlags, + const char * inName, + const char * inType, + const char * inDomain, + DNSPort inPort, + const void * inTextRecord, + DNSCount inTextRecordSize, + const char * inHost, + const char * inInterfaceName, + DNSRegistrationCallBack inCallBack, + void * inCallBackContext, + DNSRegistrationRef * outRef ) +{ + DNSStatus err; + size_t size; + DNSRegistration * objectPtr; + mDNSInterfaceID interfaceID; + domainlabel name; + domainname type; + domainname domain; + mDNSIPPort port; + mDNSu8 textRecord[ 256 ]; + const mDNSu8 * textRecordPtr; + domainname * host; + domainname tempHost; + + objectPtr = mDNSNULL; + + // Check parameters. + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( ( inFlags & ~kDNSRegistrationCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + require_action( inType, exit, err = kDNSBadParamErr ); + require_action( inTextRecord || ( inTextRecordSize == 0 ), exit, err = kDNSBadParamErr ); + require_action( ( inFlags & kDNSRegistrationFlagPreFormattedTextRecord ) || + ( inTextRecordSize < sizeof( textRecord ) ), exit, err = kDNSBadParamErr ); + require_action( !inInterfaceName || + ( strlen( inInterfaceName ) < sizeof( objectPtr->interfaceName ) ), exit, err = kDNSBadParamErr ); + + // Default to the local domain when null is passed in. + + if( !inDomain ) + { + inDomain = kDNSLocalDomain; + } + + // Set up the text record. If the pre-formatted flag is used, the input text is assumed to be a valid text record + // and is used directly. Otherwise, the input text is assumed to be raw text and is converted to a text record. + + textRecordPtr = (const mDNSu8 *) inTextRecord; + if( !( inFlags & kDNSRegistrationFlagPreFormattedTextRecord ) ) + { + // Convert the raw input text to a length-prefixed text record. + + if( inTextRecordSize > 0 ) + { + textRecord[ 0 ] = (mDNSu8) inTextRecordSize; + memcpy( &textRecord[ 1 ], inTextRecord, inTextRecordSize ); + textRecordPtr = textRecord; + inTextRecordSize += 1; + } + } + + // Allocate the object and set it up. If the TXT record is larger than the standard RDataBody, allocate more space. + + size = sizeof( *objectPtr ); + if( inTextRecordSize > sizeof( RDataBody ) ) + { + size += ( inTextRecordSize - sizeof( RDataBody ) ); + } + + err = DNSMemAlloc( size, &objectPtr ); + require_noerr( err, exit ); + memset( objectPtr, 0, size ); + + objectPtr->flags = inFlags; + objectPtr->callback = inCallBack; + objectPtr->callbackContext = inCallBackContext; + + // Set up the interface for interface-specific operations. + + if( inInterfaceName && ( *inInterfaceName != '\0' ) ) + { + strcpy( objectPtr->interfaceName, inInterfaceName ); + + err = mDNSPlatformInterfaceNameToID( gMDNSPtr, inInterfaceName, &interfaceID ); + require_noerr( err, exit ); + } + else + { + interfaceID = mDNSInterface_Any; + } + + // Add the object to the list. + + objectPtr->next = gDNSRegistrationList; + gDNSRegistrationList = objectPtr; + + // Convert the name, type, domain, and port to a format suitable for mDNS. If the name is NULL or an empty string, + // use the UTF-8 name of the system as the service name to make it easy for clients to use the standard name. + // If we're using the system name (i.e. name is NULL), automatically rename on conflicts to keep things in sync. + + if( !inName || ( *inName == '\0' ) ) + { + name = gMDNSPtr->nicelabel; + inFlags |= kDNSRegistrationFlagAutoRenameOnConflict; + } + else + { + MakeDomainLabelFromLiteralString( &name, inName ); + } + MakeDomainNameFromDNSNameString( &type, inType ); + MakeDomainNameFromDNSNameString( &domain, inDomain ); + port.b[ 0 ] = ( mDNSu8 )( inPort >> 8 ); + port.b[ 1 ] = ( mDNSu8 )( inPort >> 0 ); + + // Set up the host name (if not using the default). + + host = mDNSNULL; + if( inHost ) + { + host = &tempHost; + MakeDomainNameFromDNSNameString( host, inHost ); + AppendDomainName( host, &domain ); + } + + // Register the service with mDNS. + + err = mDNS_RegisterService( gMDNSPtr, &objectPtr->set, &name, &type, &domain, host, port, textRecordPtr, + (mDNSu16) inTextRecordSize, NULL, 0, interfaceID, + DNSRegistrationPrivateCallBack, objectPtr ); + require_noerr( err, exit ); + + if( outRef ) + { + *outRef = objectPtr; + } + +exit: + if( err && objectPtr ) + { + DNSRegistrationRemoveObject( objectPtr ); + DNSMemFree( objectPtr ); + } + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSNoSuchServiceRegistrationCreate +//=========================================================================================================================== + +DNSStatus + DNSNoSuchServiceRegistrationCreate( + DNSRegistrationFlags inFlags, + const char * inName, + const char * inType, + const char * inDomain, + const char * inInterfaceName, + DNSRegistrationCallBack inCallBack, + void * inCallBackContext, + DNSRegistrationRef * outRef ) +{ + DNSStatus err; + size_t size; + DNSRegistration * objectPtr; + mDNSInterfaceID interfaceID; + domainlabel name; + domainname type; + domainname domain; + + objectPtr = mDNSNULL; + + // Check parameters. + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( ( inFlags & ~kDNSNoSuchServiceRegistrationCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + inFlags |= kDNSRegistrationFlagPrivateNoSuchService; + require_action( inType, exit, err = kDNSBadParamErr ); + require_action( !inInterfaceName || + ( strlen( inInterfaceName ) < sizeof( objectPtr->interfaceName ) ), exit, err = kDNSBadParamErr ); + + // Default to the local domain when null is passed in. + + if( !inDomain ) + { + inDomain = kDNSLocalDomain; + } + + // Allocate the object and set it up. If the TXT record is larger than the standard RDataBody, allocate more space. + + size = sizeof( *objectPtr ); + + err = DNSMemAlloc( size, &objectPtr ); + require_noerr( err, exit ); + memset( objectPtr, 0, size ); + + objectPtr->flags = inFlags; + objectPtr->callback = inCallBack; + objectPtr->callbackContext = inCallBackContext; + + // Set up the interface for interface-specific operations. + + if( inInterfaceName && ( *inInterfaceName != '\0' ) ) + { + strcpy( objectPtr->interfaceName, inInterfaceName ); + + err = mDNSPlatformInterfaceNameToID( gMDNSPtr, inInterfaceName, &interfaceID ); + require_noerr( err, exit ); + } + else + { + interfaceID = mDNSInterface_Any; + } + + // Add the object to the list. + + objectPtr->next = gDNSRegistrationList; + gDNSRegistrationList = objectPtr; + + // Convert the name, type, domain, and port to a format suitable for mDNS. If the name is NULL or an empty string, + // use the UTF-8 name of the system as the service name to make it easy for clients to use the standard name. + + if( !inName || ( *inName == '\0' ) ) + { + name = gMDNSPtr->nicelabel; + } + else + { + MakeDomainLabelFromLiteralString( &name, inName ); + } + MakeDomainNameFromDNSNameString( &type, inType ); + MakeDomainNameFromDNSNameString( &domain, inDomain ); + + // Register the service with mDNS. + + err = mDNS_RegisterNoSuchService( gMDNSPtr, &objectPtr->set.RR_SRV, &name, &type, &domain, mDNSNULL, + interfaceID, DNSNoSuchServiceRegistrationPrivateCallBack, objectPtr ); + require_noerr( err, exit ); + + if( outRef ) + { + *outRef = objectPtr; + } + +exit: + if( err && objectPtr ) + { + DNSRegistrationRemoveObject( objectPtr ); + DNSMemFree( objectPtr ); + } + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSRegistrationRelease +//=========================================================================================================================== + +DNSStatus DNSRegistrationRelease( DNSRegistrationRef inRef, DNSRegistrationFlags inFlags ) +{ + DNSStatus err; + DNSRegistrationEvent event; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( inRef, exit, err = kDNSBadReferenceErr ); + require_action( ( inFlags & ~kDNSRegistrationReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + + // Notify the client of the registration release. Remove the object first so they cannot try to use it in the callback. + + inRef = DNSRegistrationRemoveObject( inRef ); + require_action( inRef, exit, err = kDNSBadReferenceErr ); + + if( inRef->callback ) + { + memset( &event, 0, sizeof( event ) ); + event.type = kDNSRegistrationEventTypeRelease; + inRef->callback( inRef->callbackContext, inRef, kDNSNoErr, &event ); + } + + // Deregister from mDNS after everything else since it will call us back to free the memory. + + if( !( inRef->flags & kDNSRegistrationFlagPrivateNoSuchService ) ) + { + err = mDNS_DeregisterService( gMDNSPtr, &inRef->set ); + require_noerr( err, exit ); + } + else + { + err = mDNS_DeregisterNoSuchService( gMDNSPtr, &inRef->set.RR_SRV ); + require_noerr( err, exit ); + } + + // Note: Don't free here. Wait for mDNS to call us back with a mem free result. + +exit: + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSRegistrationUpdate +//=========================================================================================================================== + +DNSStatus + DNSRegistrationUpdate( + DNSRegistrationRef inRef, + DNSRecordFlags inFlags, + DNSRegistrationRecordRef inRecord, + const void * inData, + DNSCount inSize, + DNSUInt32 inNewTTL ) +{ + DNSStatus err; + AuthRecord * rr; + size_t maxRDLength; + RData * newRData; + + newRData = mDNSNULL; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( DNSRegistrationFindObject( inRef ), exit, err = kDNSBadReferenceErr ); + require_action( ( inFlags & ~kDNSRegistrationUpdateValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + require_action( inData || ( inSize == 0 ), exit, err = kDNSBadParamErr ); + + // If a non-NULL record is specified, update it. Otherwise, use the standard TXT record. + + if( inRecord ) + { + // $$$ TO DO: Add support for updating extra records (support adding and removing them too). + + rr = mDNSNULL; + err = kDNSUnsupportedErr; + require_noerr( err, exit ); + } + else + { + rr = &inRef->set.RR_TXT; + } + + // Allocate storage for the new data and set it up. + + maxRDLength = sizeof( RDataBody ); + if( inSize > maxRDLength ) + { + maxRDLength = inSize; + } + err = DNSMemAlloc( ( sizeof( *newRData ) - sizeof( RDataBody ) ) + maxRDLength, &newRData ); + require_noerr( err, exit ); + + newRData->MaxRDLength = (mDNSu16) maxRDLength; + memcpy( &newRData->u, inData, inSize ); + + // Update the record with mDNS. + + err = mDNS_Update( gMDNSPtr, rr, inNewTTL, (mDNSu16) inSize, newRData, DNSRegistrationUpdateCallBack ); + require_noerr( err, exit ); + + newRData = mDNSNULL; + +exit: + if( newRData ) + { + DNSMemFree( newRData ); + } + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSRegistrationPrivateCallBack +//=========================================================================================================================== + +mDNSlocal void DNSRegistrationPrivateCallBack( mDNS * const inMDNS, ServiceRecordSet * const inSet, mStatus inResult ) +{ + DNSRegistrationRef object; + DNSRegistrationEvent event; + + DNS_UNUSED( inMDNS ); + + DNSServicesLock(); + + // Exit if object is no longer valid. Should never happen. + + object = (DNSRegistrationRef) inSet->ServiceContext; + require( object, exit ); + + // Dispatch based on the status code. + + switch( inResult ) + { + case mStatus_NoError: + debugf( DEBUG_NAME "registration callback: \"%##s\" name successfully registered", inSet->RR_SRV.resrec.name.c ); + + // Notify the client of a successful registration. + + if( object->callback ) + { + memset( &event, 0, sizeof( event ) ); + event.type = kDNSRegistrationEventTypeRegistered; + object->callback( object->callbackContext, object, kDNSNoErr, &event ); + } + break; + + case mStatus_NameConflict: + { + DNSStatus err; + mDNSBool remove; + + debugf( DEBUG_NAME "registration callback: \"%##s\" name conflict", inSet->RR_SRV.resrec.name.c ); + + // Name conflict. If the auto-rename option is enabled, uniquely rename the service and re-register it. Otherwise, + // remove the object so they cannot try to use it in the callback and notify the client of the name conflict. + + remove = mDNStrue; + if( object->flags & kDNSRegistrationFlagAutoRenameOnConflict ) + { + err = mDNS_RenameAndReregisterService( inMDNS, inSet, mDNSNULL ); + check_noerr( err ); + if( err == mStatus_NoError ) + { + debugf( DEBUG_NAME "registration callback: auto-renamed to \"%##s\"", inSet->RR_SRV.resrec.name.c ); + remove = mDNSfalse; + } + } + if( remove ) + { + object = DNSRegistrationRemoveObject( object ); + require( object, exit ); + + // Notify the client of the name collision. + + if( object->callback ) + { + memset( &event, 0, sizeof( event ) ); + event.type = kDNSRegistrationEventTypeNameCollision; + object->callback( object->callbackContext, object, kDNSNoErr, &event ); + } + + // Notify the client that the registration is being released. + + if( object->callback ) + { + memset( &event, 0, sizeof( event ) ); + event.type = kDNSRegistrationEventTypeRelease; + object->callback( object->callbackContext, object, kDNSNoErr, &event ); + } + + // When a name conflict occurs, mDNS will not send a separate mem free result so free the memory here. + + DNSMemFree( object ); + } + break; + } + + case mStatus_MemFree: + debugf( DEBUG_NAME "registration callback: \"%##s\" memory free", inSet->RR_SRV.resrec.name.c ); + + if( object->set.RR_TXT.resrec.rdata != &object->set.RR_TXT.rdatastorage ) + { + // Standard TXT record was updated with new data so free that data separately. + + DNSMemFree( object->set.RR_TXT.resrec.rdata ); + } + DNSMemFree( object ); + break; + + default: + debugf( DEBUG_NAME "registration callback: \"%##s\" unknown result %d", inSet->RR_SRV.resrec.name.c, inResult ); + break; + } + +exit: + DNSServicesUnlock(); +} + +//=========================================================================================================================== +// DNSNoSuchServiceRegistrationPrivateCallBack +//=========================================================================================================================== + +mDNSlocal void DNSNoSuchServiceRegistrationPrivateCallBack( mDNS * const inMDNS, AuthRecord * const inRR, mStatus inResult ) +{ + DNSRegistrationRef object; + DNSRegistrationEvent event; + + DNS_UNUSED( inMDNS ); + + DNSServicesLock(); + + // Exit if object is no longer valid. Should never happen. + + object = (DNSRegistrationRef) inRR->RecordContext; + require( object, exit ); + + // Dispatch based on the status code. + + switch( inResult ) + { + case mStatus_NoError: + debugf( DEBUG_NAME "registration callback: \"%##s\" name successfully registered", inRR->resrec.name.c ); + + // Notify the client of a successful registration. + + if( object->callback ) + { + memset( &event, 0, sizeof( event ) ); + event.type = kDNSRegistrationEventTypeRegistered; + object->callback( object->callbackContext, object, kDNSNoErr, &event ); + } + break; + + case mStatus_NameConflict: + { + debugf( DEBUG_NAME "registration callback: \"%##s\" name conflict", inRR->resrec.name.c ); + + // Name conflict. Name conflicts for no-such-service registrations often do not make sense since the main goal + // is to assert that no other service exists with a name. Because of this, name conflicts should be handled by + // the code registering the no-such-service since it is likely that if another service is already using the + // name that the service registering the no-such-service should rename its other services as well. The name + // collision client callback invoked here can do any of this client-specific behavior. It may be worth adding + // support for the auto-rename feature in the future though, if that becomes necessary. + + object = DNSRegistrationRemoveObject( object ); + require( object, exit ); + + // Notify the client of the name collision. + + if( object->callback ) + { + memset( &event, 0, sizeof( event ) ); + event.type = kDNSRegistrationEventTypeNameCollision; + object->callback( object->callbackContext, object, kDNSNoErr, &event ); + } + + // Notify the client that the registration is being released. + + if( object->callback ) + { + memset( &event, 0, sizeof( event ) ); + event.type = kDNSRegistrationEventTypeRelease; + object->callback( object->callbackContext, object, kDNSNoErr, &event ); + } + + // When a name conflict occurs, mDNS will not send a separate mem free result so free the memory here. + + DNSMemFree( object ); + break; + } + + case mStatus_MemFree: + debugf( DEBUG_NAME "registration callback: \"%##s\" memory free", inRR->resrec.name.c ); + + DNSMemFree( object ); + break; + + default: + debugf( DEBUG_NAME "registration callback: \"%##s\" unknown result %d", inRR->resrec.name.c, inResult ); + break; + } + +exit: + DNSServicesUnlock(); +} + +//=========================================================================================================================== +// DNSRegistrationUpdateCallBack +//=========================================================================================================================== + +mDNSlocal void DNSRegistrationUpdateCallBack( mDNS * const inMDNS, AuthRecord * const inRR, RData *inOldData ) +{ + DNS_UNUSED( inMDNS ); + + check( inRR ); + check( inOldData ); + + if( inOldData != &inRR->rdatastorage ) + { + DNSMemFree( inOldData ); + } +} + +//=========================================================================================================================== +// DNSRegistrationFindObject +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSRegistrationRef * DNSRegistrationFindObject( DNSRegistrationRef inRef ) +{ + DNSRegistration ** p; + + for( p = &gDNSRegistrationList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + return( p ); +} + +//=========================================================================================================================== +// DNSRegistrationRemoveObject +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSRegistrationRef DNSRegistrationRemoveObject( DNSRegistrationRef inRef ) +{ + DNSRegistration ** p; + DNSRegistration * found; + + for( p = &gDNSRegistrationList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + found = *p; + if( found ) + { + *p = found->next; + } + return( found ); +} + +#if 0 +#pragma mark - +#pragma mark == Domain Registration == +#endif + +//=========================================================================================================================== +// DNSDomainRegistrationCreate +//=========================================================================================================================== + +DNSStatus + DNSDomainRegistrationCreate( + DNSDomainRegistrationFlags inFlags, + const char * inName, + DNSDomainRegistrationType inType, + DNSDomainRegistrationRef * outRef ) +{ + DNSStatus err; + DNSDomainRegistration * objectPtr; + + objectPtr = mDNSNULL; + + // Check parameters. + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( ( inFlags & ~kDNSDomainRegistrationCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + require_action( inName, exit, err = kDNSBadParamErr ); + require_action( inType < kDNSDomainRegistrationTypeMax, exit, err = kDNSBadParamErr ); + + // Allocate the object and set it up. + + err = DNSMemAlloc( sizeof( *objectPtr ), &objectPtr ); + require_noerr( err, exit ); + memset( objectPtr, 0, sizeof( *objectPtr ) ); + + objectPtr->flags = inFlags; + + // Add the object to the list. + + objectPtr->next = gDNSDomainRegistrationList; + gDNSDomainRegistrationList = objectPtr; + + // Register the domain with mDNS. + + err = mDNS_AdvertiseDomains( gMDNSPtr, &objectPtr->rr, (mDNS_DomainType) inType, mDNSInterface_Any, (char *) inName ); + require_noerr( err, exit ); + + if( outRef ) + { + *outRef = objectPtr; + } + +exit: + if( err && objectPtr ) + { + DNSDomainRegistrationRemoveObject( objectPtr ); + DNSMemFree( objectPtr ); + } + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSDomainRegistrationRelease +//=========================================================================================================================== + +DNSStatus DNSDomainRegistrationRelease( DNSDomainRegistrationRef inRef, DNSDomainRegistrationFlags inFlags ) +{ + DNSStatus err; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( inRef, exit, err = kDNSBadReferenceErr ); + require_action( ( inFlags & ~kDNSDomainRegistrationReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + + // Remove the object and deregister the domain with mDNS. + + inRef = DNSDomainRegistrationRemoveObject( inRef ); + require_action( inRef, exit, err = kDNSBadReferenceErr ); + + mDNS_StopAdvertiseDomains( gMDNSPtr, &inRef->rr ); + + // Release the memory used by the object. + + DNSMemFree( inRef ); + err = kDNSNoErr; + +exit: + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSDomainRegistrationRemoveObject +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSDomainRegistrationRef DNSDomainRegistrationRemoveObject( DNSDomainRegistrationRef inRef ) +{ + DNSDomainRegistration ** p; + DNSDomainRegistration * found; + + for( p = &gDNSDomainRegistrationList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + found = *p; + if( found ) + { + *p = found->next; + } + return( found ); +} + +#if 0 +#pragma mark - +#pragma mark == Domain Registration == +#endif + +//=========================================================================================================================== +// DNSHostRegistrationCreate +//=========================================================================================================================== + +DNSStatus + DNSHostRegistrationCreate( + DNSHostRegistrationFlags inFlags, + const char * inName, + const char * inDomain, + const DNSNetworkAddress * inAddr, + const char * inInterfaceName, + DNSHostRegistrationCallBack inCallBack, + void * inCallBackContext, + DNSHostRegistrationRef * outRef ) +{ + DNSStatus err; + domainname name; + DNSHostRegistration * object; + mDNSInterfaceID interfaceID; + mDNSv4Addr ip; + char buffer[ 64 ]; + + object = mDNSNULL; + + // Check parameters. + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( ( inFlags & ~kDNSHostRegistrationCreateValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + require_action( inName, exit, err = kDNSBadParamErr ); + require_action( inAddr && ( inAddr->addressType == kDNSNetworkAddressTypeIPv4 ), exit, err = kDNSUnsupportedErr ); + require_action( !inInterfaceName || + ( strlen( inInterfaceName ) < sizeof( object->interfaceName ) ), exit, err = kDNSBadParamErr ); + + // Default to the local domain when null is passed in. + + if( !inDomain ) + { + inDomain = kDNSLocalDomain; + } + + // If the caller only wants to add if not found, check if a host with this name was already registered. + + MakeDomainNameFromDNSNameString( &name, inName ); + AppendDNSNameString( &name, inDomain ); + + if( inFlags & kDNSHostRegistrationFlagOnlyIfNotFound ) + { + object = DNSHostRegistrationFindObjectByName( &name ); + if( object ) + { + ++object->refCount; + if( outRef ) + { + *outRef = object; + } + object = mDNSNULL; + err = kDNSNoErr; + goto exit; + } + } + + // Allocate the object and set it up. + + err = DNSMemAlloc( sizeof( *object ), &object ); + require_noerr( err, exit ); + memset( object, 0, sizeof( *object ) ); + + MakeDomainLabelFromLiteralString( &object->name, inName ); + MakeDomainLabelFromLiteralString( &object->domain, inDomain ); + object->refCount = 1; + object->flags = inFlags; + object->callback = inCallBack; + object->callbackContext = inCallBackContext; + + // Set up the interface for interface-specific operations. + + if( inInterfaceName && ( *inInterfaceName != '\0' ) ) + { + strcpy( object->interfaceName, inInterfaceName ); + + err = mDNSPlatformInterfaceNameToID( gMDNSPtr, inInterfaceName, &interfaceID ); + require_noerr( err, exit ); + } + else + { + interfaceID = mDNSInterface_Any; + } + + // Convert the IP address to a format suitable for mDNS. + + ip.NotAnInteger = inAddr->u.ipv4.addr.v32; + + // Set up the resource records and name. + + mDNS_SetupResourceRecord( &object->RR_A, mDNSNULL, interfaceID, kDNSType_A, 60, kDNSRecordTypeUnique, + DNSHostRegistrationPrivateCallBack, object ); + mDNS_SetupResourceRecord( &object->RR_PTR, mDNSNULL, interfaceID, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, + DNSHostRegistrationPrivateCallBack, object ); + + AssignDomainName( object->RR_A.resrec.name, name ); + + mDNS_snprintf( buffer, sizeof( buffer ), "%d.%d.%d.%d.in-addr.arpa.", ip.b[ 3 ], ip.b[ 2 ], ip.b[ 1 ], ip.b[ 0 ] ); + MakeDomainNameFromDNSNameString( &object->RR_PTR.resrec.name, buffer ); + + object->RR_A.resrec.rdata->u.ip = ip; + AssignDomainName( object->RR_PTR.resrec.rdata->u.name, object->RR_A.resrec.name ); + + // Add the object to the list. + + object->next = gDNSHostRegistrationList; + gDNSHostRegistrationList = object; + + // Register with mDNS. + + err = mDNS_Register( gMDNSPtr, &object->RR_A ); + require_noerr( err, exit ); + + err = mDNS_Register( gMDNSPtr, &object->RR_PTR ); + if( err != mStatus_NoError ) + { + mDNS_Deregister( gMDNSPtr, &object->RR_A ); + } + require_noerr( err, exit ); + + if( outRef ) + { + *outRef = object; + } + +exit: + if( err && object ) + { + DNSHostRegistration ** p; + + p = DNSHostRegistrationFindObject( object ); + *p = object->next; + DNSMemFree( object ); + } + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSHostRegistrationRelease +//=========================================================================================================================== + +DNSStatus DNSHostRegistrationRelease( DNSHostRegistrationRef inRef, DNSHostRegistrationFlags inFlags ) +{ + DNSStatus err; + DNSHostRegistrationRef * p; + + DNSServicesLock(); + require_action( gMDNSPtr, exit, err = kDNSNotInitializedErr ); + require_action( inRef, exit, err = kDNSBadReferenceErr ); + require_action( ( inFlags & ~kDNSHostRegistrationReleaseValidFlags ) == 0, exit, err = kDNSBadFlagsErr ); + + // Decrement the reference count and if it drops to 0, remove the object and deregister with mDNS. + + p = DNSHostRegistrationFindObject( inRef ); + inRef = *p; + require_action( inRef, exit, err = kDNSBadReferenceErr ); + + check( inRef->refCount > 0 ); + if( --inRef->refCount == 0 ) + { + *p = inRef->next; + + mDNS_Deregister( gMDNSPtr, &inRef->RR_A ); + mDNS_Deregister( gMDNSPtr, &inRef->RR_PTR ); + + // Release the memory used by the object. + + DNSMemFree( inRef ); + } + err = kDNSNoErr; + +exit: + DNSServicesUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSHostRegistrationFindObject +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSHostRegistrationRef * DNSHostRegistrationFindObject( DNSHostRegistrationRef inRef ) +{ + DNSHostRegistration ** p; + + for( p = &gDNSHostRegistrationList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + return( p ); +} + +//=========================================================================================================================== +// DNSHostRegistrationFindObjectByName +// +// Warning: Assumes the DNS lock is held. +//=========================================================================================================================== + +mDNSlocal DNSHostRegistrationRef DNSHostRegistrationFindObjectByName( const domainname *inName ) +{ + DNSHostRegistration * p; + + check( inName ); + + for( p = gDNSHostRegistrationList; p; p = p->next ) + { + if( SameDomainName( &p->RR_A.resrec.name, inName ) ) + { + break; + } + } + return( p ); +} + +//=========================================================================================================================== +// DNSHostRegistrationPrivateCallBack +//=========================================================================================================================== + +mDNSlocal void DNSHostRegistrationPrivateCallBack( mDNS * const inMDNS, AuthRecord *const inRR, mStatus inResult ) +{ + DNSHostRegistrationRef object; + + DNS_UNUSED( inMDNS ); + + DNSServicesLock(); + + // Exit if object is no longer valid. Should never happen. + + object = (DNSHostRegistrationRef) inRR->RecordContext; + require( object, exit ); + + // Dispatch based on the status code. + + if( inResult == mStatus_NoError ) + { + debugf( DEBUG_NAME "host registration callback: \"%##s\" name successfully registered", inRR->resrec.name.c ); + if( object->callback ) + { + object->callback( object->callbackContext, object, kDNSNoErr, mDNSNULL ); + } + } + else if( inResult == mStatus_NameConflict ) + { + debugf( DEBUG_NAME "host registration callback: \"%##s\" name conflict", inRR->resrec.name.c ); + + if( object->flags & kDNSHostRegistrationFlagAutoRenameOnConflict ) + { + DNSStatus err; + domainname name; + + // De-register any resource records still registered. + + if( object->RR_A.resrec.RecordType ) + { + mDNS_Deregister( gMDNSPtr, &object->RR_A ); + } + if( object->RR_PTR.resrec.RecordType ) + { + mDNS_Deregister( gMDNSPtr, &object->RR_PTR ); + } + + // Rename the host and re-register to try again. + + IncrementLabelSuffix( &object->name, mDNSfalse ); + name.c[ 0 ] = 0; + AppendDomainLabel( &name, &object->name ); + AppendDomainLabel( &name, &object->domain ); + AssignDomainName( object->RR_PTR.resrec.name, name ); + + err = mDNS_Register( gMDNSPtr, &object->RR_A ); + check_noerr( err ); + + err = mDNS_Register( gMDNSPtr, &object->RR_PTR ); + check_noerr( err ); + } + else + { + if( object->callback ) + { + object->callback( object->callbackContext, object, kDNSNameConflictErr, mDNSNULL ); + } + } + } + else + { + debugf( DEBUG_NAME "host registration callback: \"%##s\" unknown result", inRR->resrec.name.c, inResult ); + } + +exit: + DNSServicesUnlock(); +} + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +//=========================================================================================================================== +// DNSMemAlloc +//=========================================================================================================================== + +mDNSlocal DNSStatus DNSMemAlloc( size_t inSize, void *outMem ) +{ + void * mem; + + check( inSize > 0 ); + check( outMem ); + + mem = malloc( inSize ); + *( (void **) outMem ) = mem; + if( mem ) + { + return( kDNSNoErr ); + } + return( kDNSNoMemoryErr ); +} + +//=========================================================================================================================== +// DNSMemFree +//=========================================================================================================================== + +mDNSlocal void DNSMemFree( void *inMem ) +{ + check( inMem ); + + free( inMem ); +} + +//=========================================================================================================================== +// DNSDynamicTextRecordBuildEscaped +//=========================================================================================================================== + +DNSStatus DNSDynamicTextRecordBuildEscaped( const char *inFormat, void *outTextRecord, size_t *outSize ) +{ + DNSStatus err; + size_t size; + void * textRecord; + + textRecord = NULL; + + // Calculate the size of the built text record, allocate a buffer for it, then build it in that buffer. + + err = DNSTextRecordValidate( inFormat, 0x7FFFFFFF, NULL, &size ); + require_noerr( err, exit ); + + textRecord = malloc( size ); + require_action( textRecord, exit, err = kDNSNoMemoryErr ); + + err = DNSTextRecordValidate( inFormat, size, textRecord, &size ); + require_noerr( err, exit ); + + // Success! + + if( outTextRecord ) + { + *( (void **) outTextRecord ) = textRecord; + textRecord = NULL; + } + if( outSize ) + { + *outSize = size; + } + +exit: + if( textRecord ) + { + free( textRecord ); + } + return( err ); +} + +//=========================================================================================================================== +// DNSDynamicTextRecordAppendCString +//=========================================================================================================================== + +DNSStatus DNSDynamicTextRecordAppendCString( void *ioTxt, size_t *ioTxtSize, const char *inName, const char *inValue ) +{ + DNSStatus err; + size_t valueSize; + + require_action( inName, exit, err = kDNSBadParamErr ); + require_action( inValue, exit, err = kDNSBadParamErr ); + + if( inValue != kDNSTextRecordStringNoValue ) + { + valueSize = strlen( inValue ); + } + else + { + valueSize = kDNSTextRecordNoSize; + } + err = DNSDynamicTextRecordAppendData( ioTxt, ioTxtSize, inName, inValue, valueSize ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSDynamicTextRecordAppendData +//=========================================================================================================================== + +DNSStatus + DNSDynamicTextRecordAppendData( + void * ioTxt, + size_t * ioTxtSize, + const char * inName, + const void * inValue, + size_t inValueSize ) +{ + DNSStatus err; + size_t oldSize; + size_t newSize; + int hasName; + int hasValue; + void ** bufferPtr; + void * newBuffer; + + require_action( ioTxt, exit, err = kDNSBadParamErr ); + require_action( ioTxtSize, exit, err = kDNSBadParamErr ); + require_action( inName, exit, err = kDNSBadParamErr ); + + // Check for special flags to indicate no name or no value is used (e.g. "color" instead of "color="). + + hasName = ( inName != kDNSTextRecordStringNoValue ) && ( *inName != '\0' ); + hasValue = ( inValue != kDNSTextRecordNoValue ) && ( inValueSize != kDNSTextRecordNoSize ); + require_action( hasName || hasValue, exit, err = kDNSUnsupportedErr ); + + // Calculate the size needed for the new data (old size + length byte + name size + '=' + value size). + + oldSize = *ioTxtSize; + newSize = oldSize + 1; // add length byte size + if( hasName ) + { + newSize += strlen( inName ); // add name size + if( hasValue ) + { + newSize += 1; // add '=' size + } + } + if( hasValue ) + { + newSize += inValueSize; // add value size + } + + // Reallocate the buffer to make room for the new data. + + bufferPtr = (void **) ioTxt; + newBuffer = realloc( *bufferPtr, newSize ); + require_action( newBuffer, exit, err = kDNSNoMemoryErr ); + *bufferPtr = newBuffer; + + err = DNSTextRecordAppendData( newBuffer, oldSize, newSize, inName, inValue, inValueSize, &newSize ); + require_noerr( err, exit ); + + // Success! + + *ioTxtSize = newSize; + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSDynamicTextRecordRelease +//=========================================================================================================================== + +void DNSDynamicTextRecordRelease( void *inTxt ) +{ + if( inTxt ) + { + free( inTxt ); + } +} + +//=========================================================================================================================== +// DNSTextRecordAppendCString +//=========================================================================================================================== + +DNSStatus + DNSTextRecordAppendCString( + void * inTxt, + size_t inTxtSize, + size_t inTxtMaxSize, + const char * inName, + const char * inValue, + size_t * outTxtSize ) +{ + DNSStatus err; + size_t valueSize; + + require_action( inName, exit, err = kDNSBadParamErr ); + require_action( inValue, exit, err = kDNSBadParamErr ); + + if( inValue != kDNSTextRecordStringNoValue ) + { + valueSize = strlen( inValue ); + } + else + { + valueSize = kDNSTextRecordNoSize; + } + err = DNSTextRecordAppendData( inTxt, inTxtSize, inTxtMaxSize, inName, inValue, valueSize, outTxtSize ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSTextRecordAppendData +//=========================================================================================================================== + +DNSStatus + DNSTextRecordAppendData( + void * inTxt, + size_t inTxtSize, + size_t inTxtMaxSize, + const char * inName, + const void * inValue, + size_t inValueSize, + size_t * outTxtSize ) +{ + DNSStatus err; + mDNSu8 * p; + int hasName; + int hasValue; + size_t size; + size_t newSize; + const mDNSu8 * q; + + require_action( inTxt, exit, err = kDNSBadParamErr ); + require_action( inName, exit, err = kDNSBadParamErr ); + + // Check for special flags to indicate no name or no value is used (e.g. "color" instead of "color="). + + hasName = ( inName != kDNSTextRecordStringNoValue ) && ( *inName != '\0' ); + hasValue = ( inValue != kDNSTextRecordNoValue ) && ( inValueSize != kDNSTextRecordNoSize ); + require_action( hasName || hasValue, exit, err = kDNSUnsupportedErr ); + + // Calculate the size and make sure there is enough total room and enough room in an individual segment. + + size = 0; + if( hasName ) + { + size += strlen( inName ); // add name size + if( hasValue ) + { + size += 1; // add '=' size + } + } + if( hasValue ) + { + size += inValueSize; // add value size + } + newSize = inTxtSize + 1 + size; // old size + length byte + new data + + require_action( size < 256, exit, err = kDNSNoMemoryErr ); + require_action( newSize <= inTxtMaxSize, exit, err = kDNSNoMemoryErr ); + + // Write the length-prefix byte containing the size of this segment. + + p = ( (mDNSu8 *) inTxt ) + inTxtSize; + *p++ = (mDNSu8) size; + + // Copy the name. + + if( hasName ) + { + q = (const mDNSu8 *) inName; + while( *q != '\0' ) + { + *p++ = *q++; + } + if( hasValue ) + { + *p++ = '='; + } + } + if( hasValue ) + { + // Copy the value. + + q = (const mDNSu8 *) inValue; + while( inValueSize-- > 0 ) + { + *p++ = *q++; + } + } + + // Success! + + if( outTxtSize ) + { + *outTxtSize = newSize; + } + err = kDNSNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSTextRecordEscape +//=========================================================================================================================== + +DNSStatus DNSTextRecordEscape( const void *inTextRecord, size_t inTextSize, char **outEscapedString ) +{ + DNSStatus err; + const DNSUInt8 * src; + const DNSUInt8 * end; + DNSUInt8 * dstStorage; + DNSUInt8 * dst; + int size; + + check( inTextRecord || ( inTextSize == 0 ) ); + + // Mac OS X uses a single null-terminated string to hold all the text record data with a \001 byte to delimit + // individual records within the entire block. The following code converts a packed array of length-prefixed + // records into a single \001-delimited, null-terminated string. Allocate size + 1 for the null terminator. + + dstStorage = (DNSUInt8 *) malloc( inTextSize + 1 ); + require_action( dstStorage, exit, err = kDNSNoMemoryErr ); + dst = dstStorage; + + if( inTextSize > 0 ) + { + src = (const DNSUInt8 *) inTextRecord; + end = src + inTextSize; + while( src < end ) + { + size = *src++; + if( ( src + size ) > end ) + { + // Malformed TXT record. Most likely an old-style TXT record. + + src = NULL; + break; + } + while( size-- > 0 ) + { + *dst++ = *src++; + } + *dst++ = '\001'; // \001 record separator. May be overwritten later if this is the last record. + } + check( ( dst - dstStorage ) <= inTextSize ); + if( src != end ) + { + // Malformed TXT record. Assume an old-style TXT record and use the TXT record as a whole. + + memcpy( dstStorage, inTextRecord, inTextSize ); + dstStorage[ inTextSize ] = '\0'; + } + else + { + dstStorage[ inTextSize - 1 ] = '\0'; + } + } + else + { + // No text record data so just return an empty string. + + *dst = '\0'; + } + + // Success! + + if( outEscapedString ) + { + *outEscapedString = (char *) dstStorage; + dstStorage = NULL; + } + err = kDNSNoErr; + +exit: + if( dstStorage ) + { + free( dstStorage ); + } + return( err ); +} + +//=========================================================================================================================== +// DNSNameValidate +//=========================================================================================================================== + +DNSStatus DNSNameValidate( const char *inName ) +{ + DNSStatus err; + mDNSu8 * p; + domainname name; + + p = MakeDomainNameFromDNSNameString( &name, inName ); + if( p ) + { + err = kDNSNoErr; + } + else + { + err = kDNSBadParamErr; + } + return( err ); +} + +//=========================================================================================================================== +// DNSServiceTypeValidate +//=========================================================================================================================== + +DNSStatus DNSServiceTypeValidate( const char *inServiceType ) +{ + DNSStatus err; + mDNSu8 * p; + domainname type; + domainname domain; + domainname fqdn; + + // Construct a fake fully-qualified domain name with a known good domain and the service type to be verified since + // there is currently no canned way to test just a service type by itself. + + p = MakeDomainNameFromDNSNameString( &type, inServiceType ); + if( !p ) + { + err = kDNSBadParamErr; + goto exit; + } + + p = MakeDomainNameFromDNSNameString( &domain, "local." ); + if( !p ) + { + err = kDNSBadParamErr; + goto exit; + } + + p = ConstructServiceName( &fqdn, mDNSNULL, &type, &domain ); + if( !p ) + { + err = kDNSBadParamErr; + goto exit; + } + + err = kDNSNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSTextRecordValidate +//=========================================================================================================================== + +DNSStatus DNSTextRecordValidate( const char *inText, size_t inMaxSize, void *outRecord, size_t *outActualSize ) +{ + DNSStatus err; + const mDNSu8 * p; + size_t totalSize; + mDNSu8 sectionSize; + mDNSu8 * dst; + mDNSu8 * section; + + require_action( inText, exit, err = kDNSBadParamErr ); + + // A DNS TXT record consists of a packed block of length-prefixed strings of up to 255 characters each. To allow + // this to be described with a null-terminated C-string, a special escape sequence of \001 is used to separate + // individual character strings within the C-string. + + totalSize = 0; + sectionSize = 0; + dst = (mDNSu8 *) outRecord; + section = dst; + + p = (const mDNSu8 *) inText; + while( *p != '\0' ) + { + ++totalSize; + if( totalSize >= inMaxSize ) + { + err = kDNSBadParamErr; + goto exit; + } + + if( *p == '\001' ) + { + // Separator Escape sequence, start a new string section. + + if( sectionSize <= 0 ) + { + err = kDNSBadParamErr; + goto exit; + } + sectionSize = 0; + if( section ) + { + section = &dst[ totalSize ]; + section[ 0 ] = 0; + } + } + else + { + if( sectionSize >= 255 ) + { + err = kDNSBadParamErr; + goto exit; + } + ++sectionSize; + if( section ) + { + section[ 0 ] = sectionSize; + section[ sectionSize ] = *p; + } + } + ++p; + } + ++totalSize; + + // Success! + + if( outActualSize ) + { + *outActualSize = totalSize; + } + err = kDNSNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// MDNSAddrToDNSAddress +//=========================================================================================================================== + +mDNSlocal void MDNSAddrToDNSAddress( const mDNSAddr *inAddr, DNSNetworkAddress *outAddr ) +{ + switch( inAddr->type ) + { + case mDNSAddrType_IPv4: + outAddr->addressType = kDNSNetworkAddressTypeIPv4; + outAddr->u.ipv4.addr.v32 = inAddr->ip.v4.NotAnInteger; + break; + + case mDNSAddrType_IPv6: + outAddr->addressType = kDNSNetworkAddressTypeIPv6; + outAddr->u.ipv6.addr.v32[ 0 ] = inAddr->ip.v6.l[ 0 ]; + outAddr->u.ipv6.addr.v32[ 1 ] = inAddr->ip.v6.l[ 1 ]; + outAddr->u.ipv6.addr.v32[ 2 ] = inAddr->ip.v6.l[ 2 ]; + outAddr->u.ipv6.addr.v32[ 3 ] = inAddr->ip.v6.l[ 3 ]; + break; + + default: + outAddr->addressType = kDNSNetworkAddressTypeInvalid; + break; + } +} + +#ifdef __cplusplus + } +#endif diff --git a/mDNSWindows/DNSServices/DNSServices.h b/mDNSWindows/DNSServices/DNSServices.h new file mode 100755 index 0000000..f5be4aa --- /dev/null +++ b/mDNSWindows/DNSServices/DNSServices.h @@ -0,0 +1,1912 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSServices.h,v $ +Revision 1.8 2003/08/20 06:44:24 bradley +Updated to latest internal version of the Rendezvous for Windows code: Added support for interface +specific registrations; Added support for no-such-service registrations; Added support for host +name registrations; Added support for host proxy and service proxy registrations; Added support for +registration record updates (e.g. TXT record updates); Added support for using either a single C +string TXT record, a raw, pre-formatted TXT record potentially containing multiple character string +entries, or a C-string containing a Mac OS X-style \001-delimited set of TXT record character +strings; Added support in resolve callbacks for providing both a simplified C-string for TXT records +and a ptr/size for the raw TXT record data; Added utility routines for dynamically building TXT +records from a variety of sources (\001-delimited, individual strings, etc.) and converting TXT +records to various formats for use in apps; Added utility routines to validate DNS names, DNS +service types, and TXT records; Moved to portable address representation unions (byte-stream vs host +order integer) for consistency, to avoid swapping between host and network byte order, and for IPv6 +support; Removed dependence on modified mDNSCore: define structures and prototypes locally; Added +support for automatically renaming services on name conflicts; Detect and correct TXT records from +old versions of mDNS that treated a TXT record as an arbitrary block of data, but prevent other +malformed TXT records from being accepted; Added many more error codes; Added complete HeaderDoc for +all constants, structures, typedefs, macros, and functions. Various other minor cleanup and fixes. + +Revision 1.7 2003/08/12 19:56:29 cheshire +Update to APSL 2.0 + +Revision 1.6 2003/07/02 21:20:10 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.5 2003/03/22 02:57:45 cheshire +Updated mDNSWindows to use new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") + +Revision 1.4 2003/02/20 00:59:05 cheshire +Brought Windows code up to date so it complies with +Josh Graessley's interface changes for IPv6 support. +(Actual support for IPv6 on Windows will come later.) + +Revision 1.3 2002/09/21 20:44:57 zarzycki +Added APSL info + +Revision 1.2 2002/09/20 05:58:02 bradley +DNS Services for Windows + +*/ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header DNSServices + + @abstract DNS Services interfaces. + + @discussion + + DNS Services provides DNS service registration, domain and service discovery, and name resolving services. +*/ + +#ifndef __DNS_SERVICES__ +#define __DNS_SERVICES__ + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == General == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined dns_check_compile_time + + @abstract Performs a compile-time check of something such as the size of an int. + + @discussion + + This declares a unique array with a size that is determined by dividing 1 by the result of the compile-time expression + passed to the macro. If the expression evaluates to 0, this expression results in a divide by zero, which is illegal + and generates a compile-time error. + + For example: + + dns_check_compile_time( sizeof( int ) == 4 ); + + Note: This only works with compile-time expressions. + Note: This only works in places where extern declarations are allowed (e.g. global scope). + + References: + + + + + Note: The following macros differ from the macros on the www.jaggersoft.com web site because those versions do not + work with GCC due to GCC allow a zero-length array. Using a divide-by-zero condition turned out to be more portable. +*/ + +#define dns_check_compile_time( X ) extern int dns_unique_name[ 1 / (int)( ( X ) ) ] + +#define dns_unique_name dns_make_name_wrapper( __LINE__ ) +#define dns_make_name_wrapper( X ) dns_make_name( X ) +#define dns_make_name( X ) dns_check_compile_time_ ## X + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined dns_check_compile_time_code + + @abstract Perform a compile-time check, suitable for placement in code, of something such as the size of an int. + + @discussion + + This creates a switch statement with an existing case for 0 and an additional case using the result of a + compile-time expression. A switch statement cannot have two case labels with the same constant so if the + compile-time expression evaluates to 0, it is illegal and generates a compile-time error. If the compile-time + expression does not evaluate to 0, the resulting value is used as the case label and it compiles without error. + + For example: + + dns_check_compile_time_code( sizeof( int ) == 4 ); + + Note: This only works with compile-time expressions. + Note: This does not work in a global scope so it must be inside a function. + + References: + + + +*/ + +#define dns_check_compile_time_code( X ) switch( 0 ) { case 0: case X:; } + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DNS_LOCAL + + @abstract Macro to make variables and functions static when debugging is off, but exported when debugging is on. + + @discussion + + Rather than using "static" directly, using this macros allows you to access these variables external while + debugging without being penalized for production builds. +*/ + +#if( DEBUG ) + #define DNS_LOCAL +#else + #define DNS_LOCAL static +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DNS_EXPORT + + @abstract Macro to provide a visual clue that a variable or function is globally visible. +*/ + +#define DNS_EXPORT + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DNS_DEBUG_USE_ONLY + @abstract Macro to mark a variable as unused when debugging is turned off. + @discussion + + Variables are sometimes needed only for debugging. When debugging is turned off, these debug-only variables + generate compiler warnings about unused variables. To eliminate these warnings, use the DNS_DEBUG_USE_ONLY macro + to indicate the variables are for debugging only. +*/ + +#if( DEBUG ) + #define DNS_DEBUG_USE_ONLY( X ) +#else + #define DNS_DEBUG_USE_ONLY( X ) (void)( X ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DNS_UNUSED + @abstract Macro to mark a variable as unused. + @discussion + + There is no universally supported pragma/attribute for indicating a variable is unused. DNS_UNUSED lets + indicate a variable is unused in a manner that is supported by most compilers. +*/ + +#define DNS_UNUSED( X ) (void)( X ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSUInt8 + + @abstract 8-bit unsigned data type. +*/ + +typedef unsigned char DNSUInt8; + +dns_check_compile_time( sizeof( DNSUInt8 ) == 1 ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSUInt16 + + @abstract 16-bit unsigned data type. +*/ + +typedef unsigned short DNSUInt16; + +dns_check_compile_time( sizeof( DNSUInt16 ) == 2 ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSUInt32 + + @abstract 32-bit unsigned data type. +*/ + +typedef unsigned long DNSUInt32; + +dns_check_compile_time( sizeof( DNSUInt32 ) == 4 ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSSInt32 + + @abstract 32-bit signed data type. +*/ + +typedef signed long DNSSInt32; + +dns_check_compile_time( sizeof( DNSSInt32 ) == 4 ); + + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSOpaque16 + + @abstract 16-bit opaque data type with 8-bit and 16-bit accessors. +*/ + +typedef union DNSOpaque16 DNSOpaque16; +union DNSOpaque16 +{ + DNSUInt8 v8[ 2 ]; + DNSUInt16 v16; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSOpaque32 + + @abstract 32-bit opaque data type with 8-bit, 16-bit, and 32-bit accessors. +*/ + +typedef union DNSOpaque32 DNSOpaque32; +union DNSOpaque32 +{ + DNSUInt8 v8[ 4 ]; + DNSUInt16 v16[ 2 ]; + DNSUInt32 v32; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSOpaque128 + + @abstract 128-bit opaque data type with 8-bit, 16-bit, and 32-bit accessors. +*/ + +typedef union DNSOpaque128 DNSOpaque128; +union DNSOpaque128 +{ + DNSUInt8 v8[ 16 ]; + DNSUInt16 v16[ 8 ]; + DNSUInt32 v32[ 4 ]; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSCount + + @abstract Count of at least 32-bits. +*/ + +typedef DNSUInt32 DNSCount; + +#if 0 +#pragma mark == Errors == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSStatus + + @abstract DNS Service status code. + + @constant kDNSNoErr (0) Success. No error occurred. + @constant kDNSUnknownErr (-65537) An unknown error occurred. + @constant kDNSNoSuchNameErr (-65538) The name could not be found on the network. + @constant kDNSNoMemoryErr (-65539) Not enough memory was available. + @constant kDNSBadParamErr (-65540) A invalid or inappropriate parameter was specified. + @constant kDNSBadReferenceErr (-65541) A invalid or inappropriate reference was specified. + @constant kDNSBadStateErr (-65542) The current state does not allow the specified operation. + @constant kDNSBadFlagsErr (-65543) An invalid, inappropriate, or unsupported flag was specified. + @constant kDNSUnsupportedErr (-65544) The specified feature is not currently supported. + @constant kDNSNotInitializedErr (-65545) DNS Service has not been initialized. + @constant kDNSNoCacheErr (-65546) No cache was specified. + @constant kDNSAlreadyRegisteredErr (-65547) Service or host name is already registered. + @constant kDNSNameConflictErr (-65548) Name conflicts with another on the network. + @constant kDNSInvalidErr (-65549) A general error to indicate something is invalid. + @constant kDNSGrowCache (-65550) Cache needs to be grown (not used). + @constant kDNSIncompatibleErr (-65551) Version is incompatible. + + @constant kDNSSizeErr (-65600) Size was too small or too big. + @constant kDNSMismatchErr (-65601) A data, version, etc. mismatch occurred. + @constant kDNSReadErr (-65602) Read failed. + @constant kDNSWriteErr (-65603) Write failed. + @constant kDNSCanceledErr (-65604) Operation was canceled. + @constant kDNSTimeoutErr (-65605) Operation timed out. + @constant kDNSConnectionErr (-65606) A disconnect or other connection error occurred. + @constant kDNSInUseErr (-65607) Object is in use (e.g. cannot reuse active param blocks). + @constant kDNSNoResourcesErr (-65608) Resources unavailable to perform the operation. + @constant kDNSEndingErr (-65609) Connection, session, or something is ending. + + @constant kDNSConfigChanged (-65791) Configuration changed (not used). + @constant kDNSMemFree (-65792) Memory can be freed. +*/ + +typedef DNSSInt32 DNSStatus; +enum +{ + kDNSNoErr = 0, + + // DNS Services error codes are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537). + + kDNSStartErr = -65537, // 0xFFFE FFFF + + kDNSUnknownErr = -65537, + kDNSNoSuchNameErr = -65538, + kDNSNoMemoryErr = -65539, + kDNSBadParamErr = -65540, + kDNSBadReferenceErr = -65541, + kDNSBadStateErr = -65542, + kDNSBadFlagsErr = -65543, + kDNSUnsupportedErr = -65544, + kDNSNotInitializedErr = -65545, + kDNSNoCacheErr = -65546, + kDNSAlreadyRegisteredErr = -65547, + kDNSNameConflictErr = -65548, + kDNSInvalidErr = -65549, + kDNSGrowCache = -65550, // Reserved for mDNSCore + kDNSIncompatibleErr = -65551, + + kDNSSizeErr = -65600, + kDNSMismatchErr = -65601, + kDNSReadErr = -65602, + kDNSWriteErr = -65603, + kDNSCanceledErr = -65604, + kDNSTimeoutErr = -65605, + kDNSConnectionErr = -65606, + kDNSInUseErr = -65607, + kDNSNoResourcesErr = -65608, + kDNSEndingErr = -65609, + + kDNSConfigChanged = -65791, // Reserved for mDNSCore + kDNSMemFree = -65792, // Reserved for mDNSCore + + kDNSEndErr = -65792 // 0xFFFE FF00 +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSFlags + + @abstract Flags used control DNS Services. + + @constant kDNSFlagAdvertise + Indicates that interfaces should be advertised on the network. Software that only performs searches + do not need to set this flag. +*/ + +typedef DNSUInt32 DNSFlags; +enum +{ + kDNSFlagAdvertise = ( 1 << 0 ) +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSPort + + @abstract UDP/TCP port for DNS services. + + @constant kDNSPortInvalid + Invalid port. + + @constant kDNSPortUnicastDNS + TCP/UDP port for normal unicast DNS (see RFC 1035). + + @constant kDNSPortMulticastDNS + TCP/UDP port for Multicast DNS (see ). +*/ + +typedef DNSUInt16 DNSPort; +enum +{ + kDNSPortInvalid = 0, + kDNSPortUnicastDNS = 53, + kDNSPortMulticastDNS = 5353 +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSNetworkAddressType + + @abstract Type of address data within a DNSNetworkAddress. + + @constant kDNSNetworkAddressTypeInvalid + Invalid type. + + @constant kDNSNetworkAddressTypeIPv4 + IPv4 address data. + + @constant kDNSNetworkAddressTypeIPv6 + IPv6 address data. +*/ + +typedef DNSUInt32 DNSNetworkAddressType; + +#define kDNSNetworkAddressTypeInvalid 0 +#define kDNSNetworkAddressTypeIPv4 4 +#define kDNSNetworkAddressTypeIPv6 6 +#define kDNSNetworkAddressTypeAny 0xFFFFFFFF + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSNetworkAddressIPv4 + + @field addr + 32-bit IPv4 address in network byte order. + + @field port + 16-bit port number in network byte order. +*/ + +typedef struct DNSNetworkAddressIPv4 DNSNetworkAddressIPv4; +struct DNSNetworkAddressIPv4 +{ + DNSOpaque32 addr; + DNSOpaque16 port; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSNetworkAddressIPv6 + + @field addr + 128-bit IPv6 address in network byte order. + + @field port + 16-bit port number in network byte order. +*/ + +typedef struct DNSNetworkAddressIPv6 DNSNetworkAddressIPv6; +struct DNSNetworkAddressIPv6 +{ + DNSOpaque128 addr; + DNSOpaque16 port; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSNetworkAddress + + @field addressType + Type of data contained within the address structure. + + @field ipv4 + IPv4 address data. + + @field reserved + Reserved data (pads structure to allow for future growth). Unused portions must be zero. +*/ + +typedef struct DNSNetworkAddress DNSNetworkAddress; +struct DNSNetworkAddress +{ + DNSNetworkAddressType addressType; + union + { + DNSNetworkAddressIPv4 ipv4; + DNSNetworkAddressIPv6 ipv6; + } u; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kDNSLocalDomain + + @abstract Local DNS domain name (local.). +*/ + +#define kDNSLocalDomain "local." + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServicesInitialize + + @abstract Initializes DNS Services. This must be called before DNS Services functions can be used. + + @param inFlags + Flags to control DNS Services. + + @param inCacheEntryCount + Number of entries in the DNS record cache. Specify 0 to use the default. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSServicesInitialize( DNSFlags inFlags, DNSCount inCacheEntryCount ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServicesFinalize + + @abstract Finalizes DNS Services. No DNS Services functions may be called after this function is called. +*/ + +void DNSServicesFinalize( void ); + +#if 0 +#pragma mark == Resolving == +#endif + +//=========================================================================================================================== +// Resolving +//=========================================================================================================================== + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSBrowserRef + + @abstract Reference to a DNS browser object. + + @discussion + + A browser object is typically used by a graphical user application in a manner similar to the Macintosh "Chooser" + application. The application creates a browser object then starts domain and/or service searches to begin browsing. + When domains and/or services are found, added, or removed, the application is notified via a callback routine. +*/ + +typedef struct DNSBrowser * DNSBrowserRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSResolverRef + + @abstract Reference to a DNS resolver object. + + @discussion + + A resolver object is used to resolve service names to IP addresses. +*/ + +typedef struct DNSResolver * DNSResolverRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSResolverFlags + + @abstract Flags used to control resolve operations. + + @constant kDNSResolverFlagOneShot + Used to indicate the resolver object should be automatically released after the first resolve. + + @constant kDNSResolverFlagOnlyIfUnique + Used to indicate the resolver object should only be created if it is unique. This makes it easy for + resolver management to be handled automatically. For example, some software needs to keep active + resolving operations open constantly to detect things like the IP address changing (e.g. if + displaying it to the user), but when a service goes away then comes back, a new resolver object + will often be created, leaving two resolvers for the same name. + + @constant kDNSResolverFlagAutoReleaseByName + Used to indicate the resolver object should be automatically released when the service name + that is associated with it is no longer on the network. When a service is added to the network, + a resolver object may be created and kept around to detect things like IP address changes. When + the service goes off the network, this option causes the resolver associated with that service + name to be automatically released. +*/ + +typedef DNSUInt32 DNSResolverFlags; +enum +{ + kDNSResolverFlagOneShot = ( 1 << 0 ), + kDNSResolverFlagOnlyIfUnique = ( 1 << 1 ), + kDNSResolverFlagAutoReleaseByName = ( 1 << 2 ) +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSResolverEventType + + @abstract Type of resolver event being delivered. + + @constant kDNSResolverEventTypeInvalid + Invalid event type. Here for completeness. + + @constant kDNSResolverEventTypeRelease + Object is being released. No additional data is associated with this event. + + @constant kDNSResolverEventTypeResolved + Name resolved. +*/ + +typedef long DNSResolverEventType; +enum +{ + kDNSResolverEventTypeInvalid = 0, + kDNSResolverEventTypeRelease = 1, + kDNSResolverEventTypeResolved = 10 +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSResolverEventResolveData + + @abstract Data structure passed to callback routine when a resolve-related event occurs. + + @field name + Ptr to UTF-8 string containing the resolved name of the service. + + @field type + Ptr to UTF-8 string containing the resolved type of the service. + + @field domain + Ptr to UTF-8 string containing the resolved domain of the service. + + @field interfaceID + Network interface that received the event. + + @field interfaceName + Network interface that received the event. May be empty if interface is no longer available. + + @field interfaceIP + IP of network interface that received the event. May be invalid if interface is no longer available. + + @field address + Network address of the service. Used to communicate with the service. + + @field textRecord + Ptr to UTF-8 string containing any additional text information supplied by the service provider. + + @field flags + Flags used to augment the event data. + + @field textRecordRaw + Ptr to raw TXT record data. May be needed if a custom TXT record format is used. + + @field textRecordRawSize + Number of bytes in raw TXT record. May be needed if a custom TXT record format is used. +*/ + +typedef struct DNSResolverEventResolveData DNSResolverEventResolveData; +struct DNSResolverEventResolveData +{ + const char * name; + const char * type; + const char * domain; + void * interfaceID; + const char * interfaceName; + DNSNetworkAddress interfaceIP; + DNSNetworkAddress address; + const char * textRecord; + DNSResolverFlags flags; + const void * textRecordRaw; + DNSCount textRecordRawSize; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSResolverEvent + + @abstract Data structure passed to callback routines when a resolver event occurs. + + @field type + Type of event. The type determines which portion of the data union to use. Types and data union + fields are named such as the data union field is the same as the event type. For example, a + "resolved" event type (kDNSResolverEventTypeResolved) would refer to data union field "resolved". + + @field resolved + Data associated with kDNSResolverEventTypeResolved event. +*/ + +typedef struct DNSResolverEvent DNSResolverEvent; +struct DNSResolverEvent +{ + DNSResolverEventType type; + + union + { + DNSResolverEventResolveData resolved; + + } data; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSResolverCallBack + + @abstract CallBack routine used to indicate a resolver event. + + @param inContext + User-supplied context for callback (specified when browser is created). + + @param inRef + Reference to resolver object generating the event. + + @param inStatusCode + Status of the event. + + @param inEvent + Data associated with the event. +*/ + +typedef void + ( *DNSResolverCallBack )( + void * inContext, + DNSResolverRef inRef, + DNSStatus inStatusCode, + const DNSResolverEvent * inEvent ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSResolverCreate + + @abstract Creates a resolver object and start resolving a service name. + + @param inFlags + Flags to control the resolving process. + + @param inName + Ptr to UTF-8 string containing the service name to resolve (e.g. "My Printer"). + + @param inType + Ptr to UTF-8 string containing the service type of the service to resolve (e.g. "_printer._tcp"). + + @param inDomain + Ptr to UTF-8 string containing the domain of the service to resolve (e.g. "apple.com"). Use NULL + to indicate the local domain. + + @param inCallBack + CallBack routine to call when a resolver event occurs. + + @param inCallBackContext + Context pointer to pass to CallBack routine when an event occurs. Not inspected by DNS Services. + + @param inOwner + Reference to browser object related to this resolver. If a browser object is specified and is + later released, this resolver object will automatically be released too. May be null. + + @param outRef + Ptr to receive reference to resolver object. If the kDNSResolverFlagOnlyIfUnique flag is specified + and there is already a resolver for the name, a NULL reference is returned in this parameter to let + the caller know that no resolver was created. May be null. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSResolverCreate( + DNSResolverFlags inFlags, + const char * inName, + const char * inType, + const char * inDomain, + DNSResolverCallBack inCallBack, + void * inCallBackContext, + DNSBrowserRef inOwner, + DNSResolverRef * outRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSResolverRelease + + @abstract Releases a resolver object. + + @param inRef + Reference to the resolver object to release. + + @param inFlags + Flags to control the release process. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSResolverRelease( DNSResolverRef inRef, DNSResolverFlags inFlags ); + +#if 0 +#pragma mark == Browsing == +#endif + +//=========================================================================================================================== +// Browsing +//=========================================================================================================================== + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSBrowserFlags + + @abstract Flags used to control browser operations. + + @constant kDNSBrowserFlagRegistrationDomainsOnly + Used to indicate the client is browsing only for domains to publish services. When the client wishes + to publish a service, a domain browse operation would be started, with this flag specified, to find + the domain used to register the service. Only valid when passed to DNSBrowserStartDomainSearch. + + @constant kDNSBrowserFlagAutoResolve + Used to indicate discovered names should be automatically resolved. This eliminates the need to + manually create a resolver to get the IP address and other information. Only valid when passed to + DNSBrowserStartServiceSearch. When this option is used, it is important to avoid manually resolving + names because this option causes DNS Services to automatically resolve and multiple resolvers for + the same name will lead to unnecessary network bandwidth usage. It is also important to note that + the notification behavior of the browser is otherwise not affected by this option so browser callback + will still receive the same add/remove domain/service events it normally would. +*/ + +typedef DNSUInt32 DNSBrowserFlags; +enum +{ + kDNSBrowserFlagRegistrationDomainsOnly = ( 1 << 0 ), + kDNSBrowserFlagAutoResolve = ( 1 << 1 ) +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSBrowserEventType + + @abstract Type of browser event being delivered. + + @constant kDNSBrowserEventTypeInvalid + Invalid event type. Here for completeness. + + @constant kDNSBrowserEventTypeRelease + Object is being released. No additional data is associated with this event. + + @constant kDNSBrowserEventTypeAddDomain + Domain added/found. + + @constant kDNSBrowserEventTypeAddDefaultDomain + Default domain added/found. This domain should be selected as the default. + + @constant kDNSBrowserEventTypeRemoveDomain + Domain removed. + + @constant kDNSBrowserEventTypeAddService + Service added/found. + + @constant kDNSBrowserEventTypeRemoveService + Service removed. + + @constant kDNSBrowserEventTypeResolved + Name resolved. This is only delivered if the kDNSBrowserFlagAutoResolve option is used with + DNSBrowserStartServiceSearch. +*/ + +typedef long DNSBrowserEventType; +enum +{ + kDNSBrowserEventTypeInvalid = 0, + kDNSBrowserEventTypeRelease = 1, + kDNSBrowserEventTypeAddDomain = 10, + kDNSBrowserEventTypeAddDefaultDomain = 11, + kDNSBrowserEventTypeRemoveDomain = 12, + kDNSBrowserEventTypeAddService = 20, + kDNSBrowserEventTypeRemoveService = 21, + kDNSBrowserEventTypeResolved = 30 +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSBrowserEventDomainData + + @abstract Data structure referenced by callback routines when a domain-related event occurs. + + @field interfaceID + Network interface that received the event. + + @field interfaceName + Network interface that received the event. May be empty if interface is no longer available. + + @field interfaceIP + IP of network interface that received the event. May be invalid if interface is no longer available. + + @field domain + Ptr to UTF-8 string containing the domain name. NULL if no domain name is available or applicable. + + @field flags + Flags used to augment the event data. +*/ + +typedef struct DNSBrowserEventDomainData DNSBrowserEventDomainData; +struct DNSBrowserEventDomainData +{ + void * interfaceID; + const char * interfaceName; + DNSNetworkAddress interfaceIP; + const char * domain; + DNSBrowserFlags flags; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSBrowserEventServiceData + + @abstract Data structure passed to callback routines when a service-related event occurs. + + @field interfaceID + Network interface that received the event. + + @field interfaceName + Network interface that received the event. May be empty if interface is no longer available. + + @field interfaceIP + IP of network interface that received the event. May be invalid if interface is no longer available. + + @field name + Ptr to UTF-8 string containing the service name. NULL if no service name is available or applicable. + + @field type + Ptr to UTF-8 string containing the service type. NULL if no service type is available or applicable. + + @field domain + Ptr to UTF-8 string containing the domain name. NULL if no domain name is available or applicable. + + @field flags + Flags used to augment the event data. +*/ + +typedef struct DNSBrowserEventServiceData DNSBrowserEventServiceData; +struct DNSBrowserEventServiceData +{ + void * interfaceID; + const char * interfaceName; + DNSNetworkAddress interfaceIP; + const char * name; + const char * type; + const char * domain; + DNSBrowserFlags flags; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSBrowserEvent + + @abstract Data structure passed to callback routines when a browser event occurs. + + @field type + Type of event. The type determines which portion of the data union to use. Types and data union + fields are named such as the data union field is the same as the event type. For example, an + "add domain" event type (kDNSBrowserEventTypeAddDomain) would refer to data union field "addDomain". + + @field addDomain + Data associated with kDNSBrowserEventTypeAddDomain event. + + @field addDefaultDomain + Data associated with kDNSBrowserEventTypeAddDefaultDomain event. + + @field removeDomain + Data associated with kDNSBrowserEventTypeRemoveDomain event. + + @field addService + Data associated with kDNSBrowserEventTypeAddService event. + + @field removeService + Data associated with kDNSBrowserEventTypeRemoveService event. + + @field resolved + Data associated with kDNSBrowserEventTypeResolved event. +*/ + +typedef struct DNSBrowserEvent DNSBrowserEvent; +struct DNSBrowserEvent +{ + DNSBrowserEventType type; + + union + { + DNSBrowserEventDomainData addDomain; + DNSBrowserEventDomainData addDefaultDomain; + DNSBrowserEventDomainData removeDomain; + DNSBrowserEventServiceData addService; + DNSBrowserEventServiceData removeService; + const DNSResolverEventResolveData * resolved; + + } data; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSBrowserCallBack + + @abstract CallBack routine used to indicate a browser event. + + @param inContext + User-supplied context for callback (specified when browser is created). + + @param inRef + Reference to browser object generating the event. + + @param inStatusCode + Status of the event. + + @param inEvent + Data associated with the event. +*/ + +typedef void + ( *DNSBrowserCallBack )( + void * inContext, + DNSBrowserRef inRef, + DNSStatus inStatusCode, + const DNSBrowserEvent * inEvent ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSBrowserCreate + + @abstract Creates a browser object. + + @param inFlags + Flags to control the creation process. + + @param inCallBack + CallBack routine to call when a browser event occurs. + + @param inCallBackContext + Context pointer to pass to CallBack routine when an event occurs. Not inspected by DNS Services. + + @param outRef + Ptr to receive reference to the created browser object. May be null. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSBrowserCreate( + DNSBrowserFlags inFlags, + DNSBrowserCallBack inCallBack, + void * inCallBackContext, + DNSBrowserRef * outRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSBrowserRelease + + @abstract Releases a browser object. + + @param inRef + Reference to the browser object to release. + + @param inFlags + Flags to control the release process. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSBrowserRelease( DNSBrowserRef inRef, DNSBrowserFlags inFlags ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSBrowserStartDomainSearch + + @abstract Starts a domain name search. + + @param inRef + Reference to browser object to start the search on. + + @param inFlags + Flags to control the search process. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSBrowserStartDomainSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSBrowserStopDomainSearch + + @abstract Stops a domain name search. + + @param inRef + Reference to browser object to stop the search on. + + @param inFlags + Flags to control the stopping process. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSBrowserStopDomainSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSBrowserStartServiceSearch + + @abstract Starts a service search. + + @param inRef + Reference to browser object to start the search on. + + @param inFlags + Flags to control the search process. + + @param inType + Ptr to UTF-8 string containing the service type to search for (e.g. "_printer._tcp"). + + @param inDomain + Ptr to UTF-8 string containing the domain to search in (e.g. "apple.com"). Use NULL to indicate + the local domain. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSBrowserStartServiceSearch( + DNSBrowserRef inRef, + DNSBrowserFlags inFlags, + const char * inType, + const char * inDomain ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSBrowserStopServiceSearch + + @abstract Stops a service search. + + @param inRef + Reference to browser object to stop the search on. + + @param inFlags + Flags to control the stopping process. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSBrowserStopServiceSearch( DNSBrowserRef inRef, DNSBrowserFlags inFlags ); + +#if 0 +#pragma mark == Registration == +#endif + +//=========================================================================================================================== +// Registration +//=========================================================================================================================== + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSRegistrationRef + + @abstract Reference to a DNS registration object. +*/ + +typedef struct DNSRegistration * DNSRegistrationRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSRegistrationRecordRef + + @abstract Reference to a DNS record object. +*/ + +typedef struct DNSRegistrationRecord * DNSRegistrationRecordRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSRegistrationFlags + + @abstract Flags used to control registration operations. + + @constant kDNSRegistrationFlagPreFormattedTextRecord + Text record is pre-formatted and should be used directly without interpretation. + + @constant kDNSRegistrationFlagAutoRenameOnConflict + Automatically uniquely rename and re-register the service when a name conflict occurs. +*/ + +typedef DNSUInt32 DNSRegistrationFlags; +enum +{ + kDNSRegistrationFlagPreFormattedTextRecord = ( 1 << 0 ), + kDNSRegistrationFlagAutoRenameOnConflict = ( 1 << 1 ) +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSRecordFlags + + @abstract Flags used to control record operations. +*/ + +typedef DNSUInt32 DNSRecordFlags; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSRegistrationEventType + + @abstract Type of registration event being delivered. + + @constant kDNSResolverEventTypeInvalid + Invalid event type. Here for completeness. + + @constant kDNSRegistrationEventTypeRelease + Object is being released. No additional data is associated with this event. + + @constant kDNSRegistrationEventTypeRegistered + Name has been successfully registered. + + @constant kDNSRegistrationEventTypeNameCollision + Name collision. The registration is no longer valid. A new registration must be created if needed. +*/ + +typedef long DNSRegistrationEventType; +enum +{ + kDNSRegistrationEventTypeInvalid = 0, + kDNSRegistrationEventTypeRelease = 1, + kDNSRegistrationEventTypeRegistered = 10, + kDNSRegistrationEventTypeNameCollision = 11 +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSRegistrationEvent + + @abstract Data structure passed to callback routines when a registration event occurs. + + @field type + Type of event. The type determines which portion of the data union to use. Types and data union + fields are named such as the data union field is the same as the event type. + + @field reserved + Reserved for future use. +*/ + +typedef struct DNSRegistrationEvent DNSRegistrationEvent; +struct DNSRegistrationEvent +{ + DNSRegistrationEventType type; + + union + { + DNSUInt32 reserved; + + } data; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSRegistrationCallBack + + @abstract CallBack routine used to indicate a registration event. + + @param inContext + User-supplied context for callback (specified when registration is created). + + @param inRef + Reference to registration object generating the event. + + @param inStatusCode + Status of the event. + + @param inEvent + Data associated with the event. +*/ + +typedef void + ( *DNSRegistrationCallBack )( + void * inContext, + DNSRegistrationRef inRef, + DNSStatus inStatusCode, + const DNSRegistrationEvent * inEvent ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSRegistrationCreate + + @abstract Creates a registration object and publish the registration. + + @param inFlags + Flags to control the registration process. + + @param inName + Ptr to UTF-8 string containing the service name to register (e.g. "My Printer"). + + @param inType + Ptr to UTF-8 string containing the service type of the service to registration (e.g. "_printer._tcp"). + + @param inDomain + Ptr to UTF-8 string containing the domain of the service to register (e.g. "apple.com"). Use NULL + to indicate the local domain. + + @param inPort + TCP/UDP port where the service is being offered (e.g. 80 for an HTTP service). + + @param inTextRecord + Ptr to UTF-8 string containing any additional text to provide when the service is resolved. + + @param inTextRecordSize + Size to text record. + + @param inHost + Name of the host to associate with the registration. Use NULL to use the default host name. + + @field inInterfaceName + Name of an interface to restrict service registration to. Use NULL to register service on all interfaces. + + @param inCallBack + CallBack routine to call when a registration event occurs. + + @param inCallBackContext + Context pointer to pass to CallBack routine when an event occurs. Not inspected by DNS Services. + + @param outRef + Ptr to receive reference to registration object. May be null. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSRegistrationCreate( + DNSRegistrationFlags inFlags, + const char * inName, + const char * inType, + const char * inDomain, + DNSPort inPort, + const void * inTextRecord, + DNSCount inTextRecordSize, + const char * inHost, + const char * inInterfaceName, + DNSRegistrationCallBack inCallBack, + void * inCallBackContext, + DNSRegistrationRef * outRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSNoSuchServiceRegistrationCreate + + @abstract Creates a registration object and publish the registration to assert non-existance of a particular service. + + @param inFlags + Flags to control the registration process. + + @param inName + Ptr to UTF-8 string containing the service name to register (e.g. "My Printer"). + + @param inType + Ptr to UTF-8 string containing the service type of the service to registration (e.g. "_printer._tcp"). + + @param inDomain + Ptr to UTF-8 string containing the domain of the service to register (e.g. "apple.com"). Use NULL + to indicate the local domain. + + @field inInterfaceName + Name of an interface to restrict service registration to. Use NULL to register service on all interfaces. + + @param inCallBack + CallBack routine to call when a registration event occurs. + + @param inCallBackContext + Context pointer to pass to CallBack routine when an event occurs. Not inspected by DNS Services. + + @param outRef + Ptr to receive reference to registration object. May be null. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSNoSuchServiceRegistrationCreate( + DNSRegistrationFlags inFlags, + const char * inName, + const char * inType, + const char * inDomain, + const char * inInterfaceName, + DNSRegistrationCallBack inCallBack, + void * inCallBackContext, + DNSRegistrationRef * outRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSRegistrationRelease + + @abstract Releases a registration object. + + @param inRef + Reference to the registration object to release. + + @param inFlags + Flags to control the release process. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSRegistrationRelease( DNSRegistrationRef inRef, DNSRegistrationFlags inFlags ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSRegistrationUpdate + + @abstract Updates an individual record for a registration. + + @param inRef + Reference to the registration object to update. + + @param inRecord + Record to update. Use NULL for the standard TXT record. + + @param inData + New record data. + + @param inSize + Size of new record data. + + @param inNewTTL + New time-to-live (TTL) in seconds for the updated data (e.g. 120 for 2 minutes). + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSRegistrationUpdate( + DNSRegistrationRef inRef, + DNSRecordFlags inFlags, + DNSRegistrationRecordRef inRecord, + const void * inData, + DNSCount inSize, + DNSUInt32 inNewTTL ); + +#if 0 +#pragma mark == Domain Registration == +#endif + +//=========================================================================================================================== +// Domain Registration +//=========================================================================================================================== + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSDomainRegistrationRef + + @abstract Reference to a DNS registration object. +*/ + +typedef struct DNSDomainRegistration * DNSDomainRegistrationRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSDomainRegistrationFlags + + @abstract Flags used to control registration operations. +*/ + +typedef DNSUInt32 DNSDomainRegistrationFlags; +enum +{ + kDNSDomainRegistrationFlagNone = 0 +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSDomainRegistrationType + + @abstract Type of domain registration. + + @constant kDNSDomainRegistrationTypeBrowse + Registration for domain browsing. + + @constant kDNSDomainRegistrationTypeBrowseDefault + Registration for the domain browsing domain. + + @constant kDNSDomainRegistrationTypeRegistration + Registration for domain registration. + + @constant kDNSDomainRegistrationTypeRegistrationDefault + Registration for the domain registration domain. +*/ + +typedef DNSUInt32 DNSDomainRegistrationType; +enum +{ + kDNSDomainRegistrationTypeBrowse = 0, + kDNSDomainRegistrationTypeBrowseDefault = 1, + kDNSDomainRegistrationTypeRegistration = 2, + kDNSDomainRegistrationTypeRegistrationDefault = 3, + + kDNSDomainRegistrationTypeMax = 4 +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSDomainRegistrationCreate + + @abstract Creates a domain registration object and publish the domain. + + @param inFlags + Flags to control the registration process. + + @param inName + Ptr to string containing the domain name to register (e.g. "apple.com"). + + @param inType + Type of domain registration. + + @param outRef + Ptr to receive reference to domain registration object. May be null. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSDomainRegistrationCreate( + DNSDomainRegistrationFlags inFlags, + const char * inName, + DNSDomainRegistrationType inType, + DNSDomainRegistrationRef * outRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSDomainRegistrationRelease + + @abstract Releases a domain registration object. + + @param inRef + Reference to the domain registration object to release. + + @param inFlags + Flags to control the release process. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSDomainRegistrationRelease( DNSDomainRegistrationRef inRef, DNSDomainRegistrationFlags inFlags ); + +#if 0 +#pragma mark == Host Registration == +#endif + +//=========================================================================================================================== +// Host Registration +//=========================================================================================================================== + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSHostRegistrationRef + + @abstract Reference to a DNS host registration object. +*/ + +typedef struct DNSHostRegistration * DNSHostRegistrationRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSHostRegistrationFlags + + @abstract Flags used to control registration operations. + + @constant kDNSHostRegistrationFlagOnlyIfNotFound + Only creates the object and registers the host if it was not already found in the list. + + @constant kDNSHostRegistrationFlagAutoRenameOnConflict + Automatically uniquely rename and re-register the host when a name conflict occurs. + +*/ + +typedef DNSUInt32 DNSHostRegistrationFlags; +enum +{ + kDNSHostRegistrationFlagNone = 0, + kDNSHostRegistrationFlagOnlyIfNotFound = ( 1 << 0 ), + kDNSHostRegistrationFlagAutoRenameOnConflict = ( 1 << 1 ) +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSHostRegistrationCallBack + + @abstract CallBack routine used to indicate a host registration event. + + @param inContext + User-supplied context for callback (specified when browser is created). + + @param inRef + Reference to resolver object generating the event. + + @param inStatusCode + Status of the event. + + @param inData + Data associated with the event. +*/ + +typedef void + ( *DNSHostRegistrationCallBack )( + void * inContext, + DNSHostRegistrationRef inRef, + DNSStatus inStatusCode, + void * inData ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSHostRegistrationCreate + + @abstract Creates a host registration object and publishes the host. + + @param inFlags + Flags to control the registration process. + + @param inName + Name of the host to register (e.g. "My Web Server"). + + @param inDomain + Domain of the host to register (e.g. "apple.com"). Use NULL to indicate the local domain. + + @param inAddr + IP address of host to register. + + @field inInterfaceName + Name of an interface to restrict registration to. Use NULL to register on all interfaces. + + @param inCallBack + CallBack routine to call when an event occurs. + + @param inCallBackContext + Context pointer to pass to callback routine when an event occurs. Not inspected by DNS Services. + + @param outRef + Ptr to receive reference to host registration object. May be null. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSHostRegistrationCreate( + DNSHostRegistrationFlags inFlags, + const char * inName, + const char * inDomain, + const DNSNetworkAddress * inAddr, + const char * inInterfaceName, + DNSHostRegistrationCallBack inCallBack, + void * inCallBackContext, + DNSHostRegistrationRef * outRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSHostRegistrationRelease + + @abstract Releases a host registration object. + + @param inRef + Reference to the host registration object to release. + + @param inFlags + Flags to control the release process. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSHostRegistrationRelease( DNSHostRegistrationRef inRef, DNSHostRegistrationFlags inFlags ); + +#if 0 +#pragma mark == Utilities == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kDNSTextRecordNoValue + + @abstract Value to use when no value is desired for a name/value pair (e.g. "color" instead of "color="). +*/ + +#define kDNSTextRecordNoValue ( (const void *) -1 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kDNSTextRecordStringNoValue + + @abstract Value to use when no value is desired for a name/value pair (e.g. "color" instead of "color="). +*/ + +#define kDNSTextRecordStringNoValue ( (const char *) -1 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kDNSTextRecordNoValue + + @abstract Size value to use when no value is desired for a name/value pair (e.g. "color" instead of "color="). +*/ + +#define kDNSTextRecordNoSize ( (size_t) -1 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSDynamicTextRecordBuildEscaped + + @abstract Builds a TXT record from a string with \001 escape sequences to separate strings within the TXT record. + + @param inFormat C-string TXT record with \001 escape sequences as record separators. + @param outTextRecord Receives a ptr to a built TXT record. Must free with DNSDynamicTextRecordRelease. + @param outSize Receive actual size of the built TXT record. Use NULL if you don't need the size. + + @result Error code indicating failure reason or kDNSNoErr if successful. + + @discussion + + A DNS TXT record consists of a packed array of length-prefixed strings with each string being up to 255 characters. + To allow this to be described with a null-terminated C-string, a special escape sequence of \001 is used to separate + individual character strings within the C-string. + + For example, to represent the following 3 segments "test1=1", "test2=2", and "test3=3", you would use the following: + + DNSUInt8 * txt; + size_t size; + + txt = NULL; + + err = DNSDynamicTextRecordBuildEscaped( "test1=1\001test2=2\001test3=3", &txt, &size ); + require_noerr( err, exit ); + + ... use text record + +exit: + DNSDynamicTextRecordRelease( txt ); +*/ + +DNSStatus DNSDynamicTextRecordBuildEscaped( const char *inFormat, void *outTextRecord, size_t *outSize ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSDynamicTextRecordAppendCString + + @abstract Appends a name/value pair with the value being a C-string to a dynamic DNS TXT record data section. + + @param ioTxt Input: Ptr to a ptr to TXT record to append to. + Output: Receives newly allocated ptr to the new TXT record. + Note: Use a ptr to NULL the first time this is called. + + @param ioTxtSize Input: Ptr to size of existing TXT record. + Output: Receives new size of TXT record. + + @param inName C-string name in the name/value pair (e.g. "path" for HTTP). + + @param inValue C-string value in the name/value pair (e.g. "/index.html for HTTP). + + @result Error code indicating failure reason or kDNSNoErr if successful. + + @discussion + + This can be used to easily build dynamically-resized TXT records containing multiple name/value pairs of C-strings. + For example, the following adds "name=Ryknow", "age=30", and "job=Musician": + + DNSUInt8 * txt; + size_t size; + + txt = NULL; + size = 0; + + err = DNSDynamicTextRecordAppendCString( &txt, &size, "name", "Ryknow" ); + require_noerr( err, exit ); + + err = DNSDynamicTextRecordAppendCString( &txt, &size, "age", "30" ); + require_noerr( err, exit ); + + err = DNSDynamicTextRecordAppendCString( &txt, &size, "job", "Musician" ); + require_noerr( err, exit ); + + ... use text record + +exit: + DNSDynamicTextRecordRelease( txt ); +*/ + +DNSStatus DNSDynamicTextRecordAppendCString( void *ioTxt, size_t *ioTxtSize, const char *inName, const char *inValue ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSDynamicTextRecordAppendData + + @abstract Appends a name/value pair to a dynamic DNS TXT record data section. + + @param ioTxt Input: Ptr to a ptr to TXT record to append to. + Output: Receives newly allocated ptr to the new TXT record. + Note: Use a ptr to NULL the first time this is called. + + @param ioTxtSize Input: Ptr to size of existing TXT record. + Output: Receives new size of TXT record. + + @param inName C-string name in the name/value pair (e.g. "path" for HTTP). + + @param inValue Value data to associate with the name. Use kDNSTextRecordNoValue for no value. + + @param inValueSize Size of value data. Use kDNSTextRecordNoSize for no value. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSDynamicTextRecordAppendData( + void * ioTxt, + size_t * ioTxtSize, + const char * inName, + const void * inValue, + size_t inValueSize ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSDynamicTextRecordRelease + + @abstract Releases a dynamically allocated TXT record. + + @param inTxt Dynamic TXT record to release. + + @discussion + + This API may only be used with TXT records generated with DNSDynamicTextRecordAppendCString and + DNSDynamicTextRecordAppendData. +*/ + +void DNSDynamicTextRecordRelease( void *inTxt ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSTextRecordAppendCString + + @abstract Appends a name/value pair with the value being a C-string to DNS TXT record data section. + + @param inTxt TXT record to append to. + @param inTxtSize Size of existing TXT record. + @param inTxtMaxSize Maximum size of TXT record (i.e. size of buffer). + @param inName C-string name in the name/value pair (e.g. "path" for HTTP). + @param inValue C-string value in the name/value pair (e.g. "/index.html for HTTP). + @param outTxtSize Receives resulting size of TXT record. Pass NULL if not needed. + + @result Error code indicating failure reason or kDNSNoErr if successful. + + @discussion + + This can be used to easily build TXT records containing multiple name/value pairs of C-strings. For example, the + following adds "name=Ryknow", "age=30", and "job=Musician": + + DNSUInt8 txt[ 256 ]; + size_t size; + + size = 0; + + err = DNSTextRecordAppendCString( txt, size, sizeof( txt ), "name", "Ryknow", &size ); + require_noerr( err, exit ); + + err = DNSTextRecordAppendCString( txt, size, sizeof( txt ), "age", "30", &size ); + require_noerr( err, exit ); + + err = DNSTextRecordAppendCString( txt, size, sizeof( txt ), "job", "Musician", &size ); + require_noerr( err, exit ); +*/ + +DNSStatus + DNSTextRecordAppendCString( + void * inTxt, + size_t inTxtSize, + size_t inTxtMaxSize, + const char * inName, + const char * inValue, + size_t * outTxtSize ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSTextRecordAppendData + + @abstract Appends a name/value pair to a DNS TXT record data section. + + @param inTxt TXT record to append to. + @param inTxtSize Size of existing TXT record. + @param inTxtMaxSize Maximum size of TXT record (i.e. size of buffer). + @param inName C-string name in the name/value pair (e.g. "path" for HTTP). + @param inValue Value data to associate with the name. Use kDNSTextRecordNoValue for no value. + @param inValueSize Size of value data. Use kDNSTextRecordNoSize for no value. + @param outTxtSize Receives resulting size of TXT record. Pass NULL if not needed. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus + DNSTextRecordAppendData( + void * inTxt, + size_t inTxtSize, + size_t inTxtMaxSize, + const char * inName, + const void * inValue, + size_t inValueSize, + size_t * outTxtSize ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSTextRecordEscape + + @abstract Converts a raw TXT record into a single, null-terminated string with \001 to delimit records. + + @param inTextRecord Raw TXT record to escape. + @param inTextSize Number of bytes in the raw TXT record to escape. + @param outEscapedString Receives ptr to escaped, \001-delimited, null-terminated string. + + @result Error code indicating failure reason or kDNSNoErr if successful. +*/ + +DNSStatus DNSTextRecordEscape( const void *inTextRecord, size_t inTextSize, char **outEscapedString ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSNameValidate + + @abstract Validates a DNS name for correctness. + + @param inName C-string DNS name to validate. + + @result Error code indicating failure reason or kDNSNoErr if valid. +*/ + +DNSStatus DNSNameValidate( const char *inName ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceTypeValidate + + @abstract Validates a service type for correctness. + + @param inServiceType C-string service type to validate. + + @result Error code indicating failure reason or kDNSNoErr if valid. +*/ + +DNSStatus DNSServiceTypeValidate( const char *inServiceType ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSTextRecordValidate + + @abstract Validates a text record for correctness and optionally builds the TXT reocrd, and returns the actual size. + + @param inText C-string TXT record to validate. Use \001 escape sequence as record separator. + @param inMaxSize Maximum size of the TXT record. Use a large number if a max size is not appropriate. + @param outRecord Buffer to receive built TXT record. Use NULL if you don't need a built TXT record. + @param outActualSize Ptr to receive actual size of TXT record. Use NULL if you don't need the actual size. + + @result Error code indicating failure reason or kDNSNoErr if valid. + + @discussion + + A DNS TXT record consists of a packed array of length-prefixed strings with each string being up to 255 characters. + To allow this to be described with a null-terminated C-string, a special escape sequence of \001 is used to separate + individual character strings within the C-string. + + For example, to represent the following 3 segments "test1=1", "test2=2", and "test3=3", you would use the following: + + "test1=1\001test2=2\001test3=3" +*/ + +DNSStatus DNSTextRecordValidate( const char *inText, size_t inMaxSize, void *outRecord, size_t *outActualSize ); + +#ifdef __cplusplus + } +#endif + +#endif // __DNS_SERVICES__ diff --git a/mDNSWindows/README.txt b/mDNSWindows/README.txt new file mode 100644 index 0000000..52c48b1 --- /dev/null +++ b/mDNSWindows/README.txt @@ -0,0 +1,44 @@ +This directory contains support files for running mDNS on Microsoft Windows +and Windows CE/PocketPC. + +mDNSWin32.c & mDNSWin32.h are the Platform Support files that go below +mDNS Core. These work on both Windows and Windows CE/PocketPC. + +DNSServices is a higher-level API for using mDNS. It manages memory, tracks +browers and registrations, etc. + +DNSServiceDiscovery is an emulation layer that sits on top of DNSServices +and provides the Mac OS X DNS Service Discovery API's on any platform. + +Tool.c is an example client that uses the services of mDNS Core. + +ToolWin32.mcp is a CodeWarrior project (CodeWarrior for Windows version 8). +ToolWin32.vcproj is a Visual Studio .NET 7 project. These projects builds +Tool.c to make rendezvous.exe, a small Windows command-line tool to do all +the standard Rendezvous stuff on Windows. It has the following features: + +- Browse for browsing and/or registration domains. +- Browse for services. +- Lookup Service Instances. +- Register domains for browsing and/or registration. +- Register services. + +For example, if you have a Windows machine running a Web server, +then you can make it advertise that it is offering HTTP on port 80 +with the following command: + +rendezvous -rs "Windows Web Server" "_http._tcp." "local." 80 "" + +To search for AFP servers, use this: + +rendezvous -bs "_afpovertcp._tcp." "local." + +You can also do multiple things at once (e.g. register a service and +browse for it so one instance of the app can be used for testing). +Multiple instances can also be run on the same machine to discover each +other. There is a -help command to show all the commands, their +parameters, and some examples of using it. + +RendezvousBrowser contains the source code for a graphical browser application +for Windows CE/PocketPC. The Windows CE/PocketPC version requires Microsoft +eMbedded C++ 4.0 with SP2 installed and the PocketPC 2003 SDK. diff --git a/mDNSWindows/mDNSWin32.c b/mDNSWindows/mDNSWin32.c new file mode 100755 index 0000000..161e635 --- /dev/null +++ b/mDNSWindows/mDNSWin32.c @@ -0,0 +1,2267 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSWin32.c,v $ +Revision 1.22 2003/08/20 06:21:25 bradley +Updated to latest internal version of the Rendezvous for Windows platform plugin: Added support +for Windows CE/PocketPC 2003; re-did interface-related code to emulate getifaddrs/freeifaddrs for +restricting usage to only active, multicast-capable, and non-point-to-point interfaces and to ease +the addition of IPv6 support in the future; Changed init code to serialize thread initialization to +enable ThreadID improvement to wakeup notification; Define platform support structure locally to +allow portable mDNS_Init usage; Removed dependence on modified mDNSCore: define interface ID<->name +structures/prototypes locally; Changed to use _beginthreadex()/_endthreadex() on non-Windows CE +platforms (re-mapped to CreateThread on Window CE) to avoid a leak in the Microsoft C runtime; +Added IPv4/IPv6 string<->address conversion routines; Cleaned up some code and added HeaderDoc. + +Revision 1.21 2003/08/18 23:09:57 cheshire + mDNSResponder divide by zero in mDNSPlatformTimeNow() + +Revision 1.20 2003/08/12 19:56:27 cheshire +Update to APSL 2.0 + +Revision 1.19 2003/08/05 23:58:18 cheshire +Update code to compile with the new mDNSCoreReceive() function that requires a TTL +Right now this platform layer just reports 255 instead of returning the real value -- we should fix this + +Revision 1.18 2003/07/23 21:16:30 cheshire +Removed a couple of debugfs + +Revision 1.17 2003/07/23 02:23:01 cheshire +Updated mDNSPlatformUnlock() to work correctly, now that +"ScheduleNextTask needs to be smarter" has refined the way m->NextScheduledEvent is set + +Revision 1.16 2003/07/19 03:15:16 cheshire +Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, +and add the obvious trivial implementations to each platform support layer + +Revision 1.15 2003/07/02 21:20:04 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.14 2003/05/26 03:21:30 cheshire +Tidy up address structure naming: +mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) +mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 +mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 + +Revision 1.13 2003/05/26 03:01:28 cheshire + sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead + +Revision 1.12 2003/05/06 21:06:05 cheshire + mDNSWindows needs a wakeupEvent object to signal the main thread + +Revision 1.11 2003/05/06 00:00:51 cheshire + Rationalize naming of domainname manipulation functions + +Revision 1.10 2003/04/29 00:06:09 cheshire + mDNSWindows needs a wakeupEvent object to signal the main thread + +Revision 1.9 2003/04/26 02:40:01 cheshire +Add void LogMsg( const char *format, ... ) + +Revision 1.8 2003/03/22 02:57:44 cheshire +Updated mDNSWindows to use new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") + +Revision 1.7 2003/03/15 04:40:38 cheshire +Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" + +Revision 1.6 2003/02/21 01:54:10 cheshire +Bug #: 3099194 mDNSResponder needs performance improvements +Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") + +Revision 1.5 2003/02/20 00:59:03 cheshire +Brought Windows code up to date so it complies with +Josh Graessley's interface changes for IPv6 support. +(Actual support for IPv6 on Windows will come later.) + +Revision 1.4 2002/09/21 20:44:54 zarzycki +Added APSL info + +Revision 1.3 2002/09/20 05:50:45 bradley +Multicast DNS platform plugin for Win32 + +*/ + +#if( defined( _MSC_VER ) ) + #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. +#endif + +#if( !defined( WIN32_LEAN_AND_MEAN ) ) + #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces. +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#if( !defined( _WIN32_WCE ) ) // Windows CE does not have process.h. + #include +#endif + +#if( DEBUG ) + #define mDNSlocal +#endif + +#include "mDNSClientAPI.h" +#include "mDNSPlatformFunctions.h" + +#include "mDNSWin32.h" + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[mDNS] " + +#if( !defined( MDNS_DEBUG_SIGNATURE ) ) + #define MDNS_DEBUG_SIGNATURE "mDNS" +#endif + +#define kMDNSDefaultName "My Computer" + +#define kWinSockMajorMin 2 +#define kWinSockMinorMin 2 + +#define kWaitListCancelEvent ( WAIT_OBJECT_0 + 0 ) +#define kWaitListInterfaceListChangedEvent ( WAIT_OBJECT_0 + 1 ) +#define kWaitListWakeupEvent ( WAIT_OBJECT_0 + 2 ) +#define kWaitListFixedItemCount 3 + +#if 0 +#pragma mark == Macros - Debug == +#endif + +//=========================================================================================================================== +// Macros - Debug +//=========================================================================================================================== + +#define MDNS_UNUSED( X ) (void)( X ) + +#define kDebugLevelMask 0x0000FFFFL +#define kDebugLevelChatty 100L +#define kDebugLevelVerbose 500L +#define kDebugLevelTrace 800L +#define kDebugLevelInfo 1000L +#define kDebugLevelRareInfo 2000L +#define kDebugLevelNotice 3000L +#define kDebugLevelWarning 4000L +#define kDebugLevelAllowedError 5000L +#define kDebugLevelAssert 6000L +#define kDebugLevelRequire 7000L +#define kDebugLevelError 8000L +#define kDebugLevelCritical 9000L +#define kDebugLevelCriticalError kDebugLevelCritical // DEPRECATED +#define kDebugLevelAlert 10000L +#define kDebugLevelEmergency 11000L +#define kDebugLevelTragic 12000L +#define kDebugLevelAny 0x0000FFFFL + +#if( defined( __MWERKS__ ) || defined( __GNUC__ ) ) + #define __ROUTINE__ __FUNCTION__ +#else + // Apple and Symantec compilers don't support the C99/GCC extensions yet. + + #define __ROUTINE__ NULL +#endif + +#if( MDNS_DEBUGMSGS ) + #define debug_print_assert( ASSERT_STRING, FILENAME, LINE_NUMBER, FUNCTION ) \ + mDNSPlatformPrintAssert( MDNS_DEBUG_SIGNATURE, 0, ( ASSERT_STRING ), NULL, ( FILENAME ), ( LINE_NUMBER ), ( FUNCTION ) ) + + #define debug_print_assert_err( ERR, ASSERT_STRING, ERROR_STRING, FILENAME, LINE_NUMBER, FUNCTION ) \ + mDNSPlatformPrintAssert( MDNS_DEBUG_SIGNATURE, ( ERR ), ( ASSERT_STRING ), ( ERROR_STRING ), \ + ( FILENAME ), ( LINE_NUMBER ), ( FUNCTION ) ) + + #define dlog mDNSPlatformDebugLog +#else + #define debug_print_assert( ASSERT_STRING, FILENAME, LINE_NUMBER, FUNCTION ) + + #define debug_print_assert_err( ERR, ASSERT_STRING, ERROR_STRING, FILENAME, LINE_NUMBER, FUNCTION ) + + #define dlog while( 0 ) +#endif + +/// +/// The following debugging macros emulate those available on Mac OS in AssertMacros.h/Debugging.h. +/// + +// checks + +#define check( X ) \ + do { \ + if( !( X ) ) { \ + debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + } while( 0 ) + +#define check_noerr( ERR ) \ + do { \ + if( ( ERR ) != 0 ) { \ + debug_print_assert_err( ( ERR ), #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + } while( 0 ) + +#define check_errno( ERR, ERRNO ) \ + do { \ + int localErr; \ + \ + localErr = (int)( ERR ); \ + if( localErr < 0 ) { \ + int localErrno; \ + \ + localErrno = ( ERRNO ); \ + localErr = ( localErrno != 0 ) ? localErrno : localErr; \ + debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + } while( 0 ) + +// requires + +#define require( X, LABEL ) \ + do { \ + if( !( X ) ) { \ + debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + } while( 0 ) + +#define require_quiet( X, LABEL ) \ + do { \ + if( !( X ) ) { \ + goto LABEL; \ + } \ + } while( 0 ) + +#define require_action( X, LABEL, ACTION ) \ + do { \ + if( !( X ) ) { \ + debug_print_assert( #X, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + } while( 0 ) + +#define require_action_quiet( X, LABEL, ACTION ) \ + do { \ + if( !( X ) ) { \ + { ACTION; } \ + goto LABEL; \ + } \ + } while( 0 ) + +#define require_noerr( ERR, LABEL ) \ + do { \ + if( ( ERR ) != 0 ) { \ + debug_print_assert_err( ( ERR ), #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + } while( 0 ) + +#define require_noerr_quiet( ERR, LABEL ) \ + do { \ + if( ( ERR ) != 0 ) { \ + goto LABEL; \ + } \ + } while( 0 ) + +#define require_errno( ERR, ERRNO, LABEL ) \ + do { \ + int localErr; \ + \ + localErr = (int)( ERR ); \ + if( localErr < 0 ) { \ + int localErrno; \ + \ + localErrno = ( ERRNO ); \ + localErr = ( localErrno != 0 ) ? localErrno : localErr; \ + debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + } while( 0 ) + +#define require_errno_action( ERR, ERRNO, LABEL, ACTION ) \ + do { \ + int localErr; \ + \ + localErr = (int)( ERR ); \ + if( localErr < 0 ) { \ + int localErrno; \ + \ + localErrno = ( ERRNO ); \ + localErr = ( localErrno != 0 ) ? localErrno : localErr; \ + debug_print_assert_err( localErr, #ERR, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + } while( 0 ) + +#if 0 +#pragma mark == Macros - General == +#endif + +//=========================================================================================================================== +// Macros - General +//=========================================================================================================================== + +#define kInvalidSocketRef INVALID_SOCKET +#define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET ) +#define close_compat( X ) closesocket( X ) +#define errno_compat() WSAGetLastError() + +// _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking +// resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to +// CreateThread on Windows CE. + +#if( defined( _WIN32_WCE ) ) + #define _beginthreadex( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \ + CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \ + (LPDWORD) THREAD_ID_PTR ) + + #define _endthreadex( RESULT ) +#endif + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +#if( MDNS_DEBUGMSGS ) + mDNSlocal void mDNSPlatformDebugLog( unsigned long inLevel, const char *inFormat, ... ); + mDNSlocal void + mDNSPlatformPrintAssert( + const char * inSignature, + long inError, + const char * inAssertionString, + const char * inErrorString, + const char * inFileName, + unsigned long inLineNumber, + const char * inFunction ); +#endif + +mDNSlocal mStatus SetupSynchronizationObjects( mDNS * const inMDNS ); +mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS ); +mDNSlocal mStatus SetupName( mDNS * const inMDNS ); +mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ); +mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ); +mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct sockaddr_in *inAddress, mDNSInterfaceData **outIFD ); +mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD ); +mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, + const struct sockaddr_in * inAddress, + mDNSIPPort inPort, + SocketRef * outSocketRef ); +mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS ); +mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS ); + +mDNSlocal mStatus SetupThread( mDNS * const inMDNS ); +mDNSlocal mStatus TearDownThread( const mDNS * const inMDNS ); +mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ); +mDNSlocal mStatus ProcessingThreadInitialize( mDNS * const inMDNS ); +mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount ); +mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSocketRef ); +mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ); + +// Platform Accessors + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo; +struct mDNSPlatformInterfaceInfo +{ + const char * name; + mDNSAddr ip; +}; + +mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ); +mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ); + +#ifdef __cplusplus + } +#endif + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport; +mDNSs32 mDNSPlatformOneSecond = 0; + +#if( MDNS_DEBUGMSGS ) + mDNSlocal unsigned long gDebugLevel = kDebugLevelInfo + 1; +#endif + +#if 0 +#pragma mark - +#pragma mark == Platform Support APIs == +#endif + +//=========================================================================================================================== +// mDNSPlatformInit +//=========================================================================================================================== + +mStatus mDNSPlatformInit( mDNS * const inMDNS ) +{ + mStatus err; + WSADATA wsaData; + int supported; + + dlog( kDebugLevelVerbose, DEBUG_NAME "platform init\n" ); + + // Initialize variables. + + memset( &gMDNSPlatformSupport, 0, sizeof( gMDNSPlatformSupport ) ); + inMDNS->p = &gMDNSPlatformSupport; + inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef; + mDNSPlatformOneSecond = 1000; // Use milliseconds as the quantum of time. + + // Set everything up. + + err = WSAStartup( MAKEWORD( kWinSockMajorMin, kWinSockMinorMin ), &wsaData ); + require_noerr( err, exit ); + + supported = ( ( LOBYTE( wsaData.wVersion ) == kWinSockMajorMin ) && ( HIBYTE( wsaData.wVersion ) == kWinSockMinorMin ) ); + require_action( supported, exit, err = mStatus_UnsupportedErr ); + + err = SetupSynchronizationObjects( inMDNS ); + require_noerr( err, exit ); + + err = SetupThread( inMDNS ); + require_noerr( err, exit ); + + // Success! + + mDNSCoreInitComplete( inMDNS, err ); + +exit: + if( err ) + { + mDNSPlatformClose( inMDNS ); + } + dlog( kDebugLevelVerbose, DEBUG_NAME "platform init done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformClose +//=========================================================================================================================== + +void mDNSPlatformClose( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelVerbose, DEBUG_NAME "platform close\n" ); + check( inMDNS ); + + // Tear everything down in reverse order to how it was set up. + + err = TearDownThread( inMDNS ); + check_noerr( err ); + + err = TearDownInterfaceList( inMDNS ); + check_noerr( err ); + + err = TearDownSynchronizationObjects( inMDNS ); + check_noerr( err ); + + WSACleanup(); + + dlog( kDebugLevelVerbose, DEBUG_NAME "platform close done (err=%ld)\n", err ); +} + +//=========================================================================================================================== +// mDNSPlatformSendUDP +//=========================================================================================================================== + +mStatus + mDNSPlatformSendUDP( + const mDNS * const inMDNS, + const DNSMessage * const inMsg, + const mDNSu8 * const inMsgEnd, + mDNSInterfaceID inInterfaceID, + mDNSIPPort inSrcPort, + const mDNSAddr * inDstIP, + mDNSIPPort inDstPort ) +{ + mStatus err; + mDNSInterfaceData * ifd; + struct sockaddr_in addr; + int n; + + MDNS_UNUSED( inSrcPort ); + dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP\n" ); + + // Check parameters. + + check( inMDNS ); + check( inMsg ); + check( inMsgEnd ); + check( inInterfaceID ); + check( inDstIP ); + if( inDstIP->type != mDNSAddrType_IPv4 ) + { + err = mStatus_BadParamErr; + goto exit; + } + + // Send the packet. + + ifd = (mDNSInterfaceData *) inInterfaceID; + check( IsValidSocket( ifd->multicastSocketRef ) ); + + addr.sin_family = AF_INET; + addr.sin_port = inDstPort.NotAnInteger; + addr.sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger; + + n = (int)( inMsgEnd - ( (const mDNSu8 * const) inMsg ) ); + n = sendto( ifd->multicastSocketRef, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) ); + check_errno( n, errno_compat() ); + + ifd->sendErrorCounter += ( n < 0 ); + ifd->sendMulticastCounter += ( inDstPort.NotAnInteger == MulticastDNSPort.NotAnInteger ); + ifd->sendUnicastCounter += ( inDstPort.NotAnInteger != MulticastDNSPort.NotAnInteger ); + err = mStatus_NoError; + +exit: + dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP done\n" ); + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformLock +//=========================================================================================================================== + +void mDNSPlatformLock( const mDNS * const inMDNS ) +{ + EnterCriticalSection( &inMDNS->p->lock ); +} + +//=========================================================================================================================== +// mDNSPlatformUnlock +//=========================================================================================================================== + +void mDNSPlatformUnlock( const mDNS * const inMDNS ) +{ + check( inMDNS ); + check( inMDNS->p ); + check( inMDNS->p->threadID ); + + // When an API routine is called, "m->NextScheduledEvent" is reset to "timenow" before calling mDNSPlatformUnlock() + // Since our main mDNS_Execute() loop is on a different thread, we need to wake up that thread to: + // (a) handle immediate work (if any) resulting from this API call + // (b) calculate the next sleep time between now and the next interesting event + + if( ( mDNSPlatformTimeNow() - inMDNS->NextScheduledEvent ) >= 0 ) + { + // We only need to case a wakeup event when called from a task other than the mDNS task since if we are + // called from mDNS task, we'll loop back and call mDNS_Execute. This avoids filling up the command queue. + + if( GetCurrentThreadId() != inMDNS->p->threadID ) + { + BOOL wasSet; + + wasSet = SetEvent( inMDNS->p->wakeupEvent ); + check( wasSet ); + } + } + LeaveCriticalSection( &inMDNS->p->lock ); +} + +//=========================================================================================================================== +// mDNSPlatformStrLen +//=========================================================================================================================== + +mDNSu32 mDNSPlatformStrLen( const void *inSrc ) +{ + check( inSrc ); + + return( (mDNSu32) strlen( (const char *) inSrc ) ); +} + +//=========================================================================================================================== +// mDNSPlatformStrCopy +//=========================================================================================================================== + +void mDNSPlatformStrCopy( const void *inSrc, void *inDst ) +{ + check( inSrc ); + check( inDst ); + + strcpy( (char *) inDst, (const char*) inSrc ); +} + +//=========================================================================================================================== +// mDNSPlatformMemCopy +//=========================================================================================================================== + +void mDNSPlatformMemCopy( const void *inSrc, void *inDst, mDNSu32 inSize ) +{ + check( inSrc ); + check( inDst ); + + memcpy( inDst, inSrc, inSize ); +} + +//=========================================================================================================================== +// mDNSPlatformMemSame +//=========================================================================================================================== + +mDNSBool mDNSPlatformMemSame( const void *inSrc, const void *inDst, mDNSu32 inSize ) +{ + check( inSrc ); + check( inDst ); + + return( (mDNSBool)( memcmp( inSrc, inDst, inSize ) == 0 ) ); +} + +//=========================================================================================================================== +// mDNSPlatformMemZero +//=========================================================================================================================== + +void mDNSPlatformMemZero( void *inDst, mDNSu32 inSize ) +{ + check( inDst ); + + memset( inDst, 0, inSize ); +} + +//=========================================================================================================================== +// mDNSPlatformMemAllocate +//=========================================================================================================================== + +mDNSexport void * mDNSPlatformMemAllocate( mDNSu32 inSize ) +{ + void * mem; + + check( inSize > 0 ); + + mem = malloc( inSize ); + check( mem ); + + return( mem ); +} + +//=========================================================================================================================== +// mDNSPlatformMemFree +//=========================================================================================================================== + +mDNSexport void mDNSPlatformMemFree( void *inMem ) +{ + check( inMem ); + + free( inMem ); +} + +//=========================================================================================================================== +// mDNSPlatformTimeInit +//=========================================================================================================================== + +mDNSexport mStatus mDNSPlatformTimeInit( mDNSs32 *outTimeNow ) +{ + check( outTimeNow ); + + // No special setup is required on Windows -- we just use GetTickCount(). + + *outTimeNow = mDNSPlatformTimeNow(); + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// mDNSPlatformTimeNow +//=========================================================================================================================== + +mDNSs32 mDNSPlatformTimeNow( void ) +{ + return( (mDNSs32) GetTickCount() ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceNameToID +//=========================================================================================================================== + +mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ) +{ + mStatus err; + mDNSInterfaceData * ifd; + + check( inMDNS ); + check( inMDNS->p ); + check( inName ); + + // Search for an interface with the specified name, + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( strcmp( ifd->name, inName ) == 0 ) + { + break; + } + } + require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr ); + + // Success! + + if( outID ) + { + *outID = (mDNSInterfaceID) ifd; + } + err = mStatus_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceIDToInfo +//=========================================================================================================================== + +mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ) +{ + mStatus err; + mDNSInterfaceData * ifd; + + check( inMDNS ); + check( inID ); + check( outInfo ); + + // Search for an interface with the specified ID, + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( ifd == (mDNSInterfaceData *) inID ) + { + break; + } + } + require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr ); + + // Success! + + outInfo->name = ifd->name; + outInfo->ip = ifd->hostSet.ip; + err = mStatus_NoError; + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// debugf_ +//=========================================================================================================================== + +#if( MDNS_DEBUGMSGS ) +void debugf_( const char *inFormat, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, inFormat ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); + va_end( args ); + + dlog( kDebugLevelInfo, "%s\n", buffer ); +} +#endif + +//=========================================================================================================================== +// verbosedebugf_ +//=========================================================================================================================== + +#if( MDNS_DEBUGMSGS > 1 ) +void verbosedebugf_( const char *inFormat, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, inFormat ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); + va_end( args ); + + dlog( kDebugLevelVerbose, "%s\n", buffer ); +} +#endif + +//=========================================================================================================================== +// LogMsg +//=========================================================================================================================== + +void LogMsg( const char *inFormat, ... ) +{ + char buffer[ 512 ]; + va_list args; + mDNSu32 length; + + va_start( args, inFormat ); + length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args ); + va_end( args ); + + dlog( kDebugLevelWarning, "%s\n", buffer ); +} + +#if( MDNS_DEBUGMSGS ) +//=========================================================================================================================== +// mDNSPlatformDebugLog +//=========================================================================================================================== + +mDNSlocal void mDNSPlatformDebugLog( unsigned long inLevel, const char *inFormat, ... ) +{ + if( inLevel >= gDebugLevel ) + { + va_list args; + + va_start( args, inFormat ); + vfprintf( stderr, inFormat, args ); + fflush( stderr ); + va_end( args ); + } +} + +//=========================================================================================================================== +// mDNSPlatformPrintAssert +//=========================================================================================================================== + +mDNSlocal void + mDNSPlatformPrintAssert( + const char * inSignature, + long inError, + const char * inAssertionString, + const char * inErrorString, + const char * inFileName, + unsigned long inLineNumber, + const char * inFunction ) +{ + char * dataPtr; + char buffer[ 512 ]; + char tempSignatureChar; + + if( !inSignature ) + { + tempSignatureChar = '\0'; + inSignature = &tempSignatureChar; + } + dataPtr = buffer; + dataPtr += sprintf( dataPtr, "\n" ); + if( inError != 0 ) + { + dataPtr += sprintf( dataPtr, "[%s] Error: %ld\n", inSignature, inError ); + } + else + { + dataPtr += sprintf( dataPtr, "[%s] Assertion failed", inSignature ); + if( inAssertionString ) + { + dataPtr += sprintf( dataPtr, ": %s", inAssertionString ); + } + dataPtr += sprintf( dataPtr, "\n" ); + } + if( inErrorString ) + { + dataPtr += sprintf( dataPtr, "[%s] %s\n", inSignature, inErrorString ); + } + if( inFileName ) + { + dataPtr += sprintf( dataPtr, "[%s] file: \"%s\"\n", inSignature, inFileName ); + } + if( inLineNumber ) + { + dataPtr += sprintf( dataPtr, "[%s] line: %ld\n", inSignature, inLineNumber ); + } + if( inFunction ) + { + dataPtr += sprintf( dataPtr, "[%s] function: \"%s\"\n", inSignature, inFunction ); + } + dataPtr += sprintf( dataPtr, "\n" ); + fprintf( stderr, "%s", buffer ); + fflush( stderr ); +} +#endif // MDNS_DEBUGMSGS + +#if 0 +#pragma mark - +#pragma mark == Platform Internals == +#endif + +//=========================================================================================================================== +// SetupSynchronizationObjects +//=========================================================================================================================== + +mDNSlocal mStatus SetupSynchronizationObjects( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up synchronization objects\n" ); + + InitializeCriticalSection( &inMDNS->p->lock ); + inMDNS->p->lockInitialized = mDNStrue; + + inMDNS->p->cancelEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( inMDNS->p->cancelEvent, exit, err = mStatus_NoMemoryErr ); + + inMDNS->p->quitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr ); + + inMDNS->p->interfaceListChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( inMDNS->p->interfaceListChangedEvent, exit, err = mStatus_NoMemoryErr ); + + inMDNS->p->wakeupEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( inMDNS->p->wakeupEvent, exit, err = mStatus_NoMemoryErr ); + + err = mStatus_NoError; + +exit: + if( err ) + { + TearDownSynchronizationObjects( inMDNS ); + } + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up synchronization objects done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownSynchronizationObjects +//=========================================================================================================================== + +mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down synchronization objects\n" ); + + if( inMDNS->p->quitEvent ) + { + CloseHandle( inMDNS->p->quitEvent ); + inMDNS->p->quitEvent = 0; + } + if( inMDNS->p->cancelEvent ) + { + CloseHandle( inMDNS->p->cancelEvent ); + inMDNS->p->cancelEvent = 0; + } + if( inMDNS->p->interfaceListChangedEvent ) + { + CloseHandle( inMDNS->p->interfaceListChangedEvent ); + inMDNS->p->interfaceListChangedEvent = 0; + } + if( inMDNS->p->wakeupEvent ) + { + CloseHandle( inMDNS->p->wakeupEvent ); + inMDNS->p->wakeupEvent = 0; + } + if( inMDNS->p->lockInitialized ) + { + DeleteCriticalSection( &inMDNS->p->lock ); + inMDNS->p->lockInitialized = mDNSfalse; + } + err = mStatus_NoError; + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down synchronization objects done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// SetupName +//=========================================================================================================================== + +mDNSlocal mStatus SetupName( mDNS * const inMDNS ) +{ + mStatus err; + char tempString[ 256 ]; + + check( inMDNS ); + + // Get the name of this machine. + + tempString[ 0 ] = '\0'; + err = gethostname( tempString, sizeof( tempString ) - 1 ); + check_errno( err, errno_compat() ); + if( err || ( tempString[ 0 ] == '\0' ) ) + { + // Invalidate name so fall back to a default name. + + strcpy( tempString, kMDNSDefaultName ); + } + tempString[ sizeof( tempString ) - 1 ] = '\0'; + + // Set up the host name with mDNS. + + inMDNS->nicelabel.c[ 0 ] = (mDNSu8) strlen( tempString ); + memcpy( &inMDNS->nicelabel.c[ 1 ], tempString, inMDNS->nicelabel.c[ 0 ] ); + ConvertUTF8PstringToRFC1034HostLabel( inMDNS->nicelabel.c, &inMDNS->hostlabel ); + if( inMDNS->hostlabel.c[ 0 ] == 0 ) + { + // Nice name has no characters that are representable as an RFC1034 name (e.g. Japanese) so use the default. + + MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName ); + } + check( inMDNS->nicelabel.c[ 0 ] != 0 ); + check( inMDNS->hostlabel.c[ 0 ] != 0 ); + + mDNS_GenerateFQDN( inMDNS ); + + dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] ); + dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] ); + return( err ); +} + +//=========================================================================================================================== +// SetupInterfaceList +//=========================================================================================================================== + +mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) +{ + mStatus err; + mDNSInterfaceData ** next; + mDNSInterfaceData * ifd; + struct ifaddrs * addrs; + struct ifaddrs * p; + struct ifaddrs * loopback; + u_int flagMask; + u_int flagTest; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list\n" ); + check( inMDNS ); + check( inMDNS->p ); + + addrs = NULL; + + // Tear down any existing interfaces that may be set up. + + TearDownInterfaceList( inMDNS ); + + // Set up the name of this machine. + + err = SetupName( inMDNS ); + check_noerr( err ); + + // Set up the interface list change notification. + + err = SetupNotifications( inMDNS ); + check_noerr( err ); + + // Set up each interface that is active, multicast-capable, and not the loopback interface or point-to-point. + + flagMask = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTTOPOINT; + flagTest = IFF_UP | IFF_MULTICAST; + + loopback = NULL; + next = &inMDNS->p->interfaceList; + + err = getifaddrs( &addrs ); + require_noerr( err, exit ); + + for( p = addrs; p; p = p->ifa_next ) + { + if( ( p->ifa_flags & flagMask ) == flagTest ) + { + if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) ) // $$$ TO DO: Update for IPv6. + { + continue; + } + + err = SetupInterface( inMDNS, (struct sockaddr_in *) p->ifa_addr, &ifd ); + require_noerr( err, exit ); + + strcpy( ifd->name, p->ifa_name ); + + *next = ifd; + next = &ifd->next; + ++inMDNS->p->interfaceCount; + } + } + +exit: + if( err ) + { + TearDownInterfaceList( inMDNS ); + } + if( addrs ) + { + freeifaddrs( addrs ); + } + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownInterfaceList +//=========================================================================================================================== + +mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ) +{ + mStatus err; + mDNSInterfaceData * ifd; + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list\n" ); + check( inMDNS ); + check( inMDNS->p ); + + // Tear down interface list change notifications. + + err = TearDownNotifications( inMDNS ); + check_noerr( err ); + + // Tear down all the interfaces. + + while( inMDNS->p->interfaceList ) + { + ifd = inMDNS->p->interfaceList; + inMDNS->p->interfaceList = ifd->next; + + TearDownInterface( inMDNS, ifd ); + } + inMDNS->p->interfaceCount = 0; + + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list done\n" ); + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// SetupInterface +//=========================================================================================================================== + +mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct sockaddr_in *inAddress, mDNSInterfaceData **outIFD ) +{ + mStatus err; + mDNSInterfaceData * ifd; + SocketRef socketRef; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface\n" ); + check( inMDNS ); + check( inMDNS->p ); + check( inAddress ); + check( outIFD ); + + // Allocate memory for the info item. + + ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) ); + require_action( ifd, exit, err = mStatus_NoMemoryErr ); + ifd->multicastSocketRef = kInvalidSocketRef; + ifd->unicastSocketRef = kInvalidSocketRef; + + /// + /// Set up multicast portion of interface. + /// + + // Set up the multicast DNS (port 5353) socket for this interface. + + err = SetupSocket( inMDNS, inAddress, MulticastDNSPort, &socketRef ); + require_noerr( err, exit ); + ifd->multicastSocketRef = socketRef; + + // Set up the read pending event and associate it so we can block until data is available for this socket. + + ifd->multicastReadPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( ifd->multicastReadPendingEvent, exit, err = mStatus_NoMemoryErr ); + + err = WSAEventSelect( ifd->multicastSocketRef, ifd->multicastReadPendingEvent, FD_READ ); + require_noerr( err, exit ); + + /// + /// Set up unicast portion of interface. + /// + + // Set up the unicast DNS (port 53) socket for this interface (to handle normal DNS requests). + + err = SetupSocket( inMDNS, inAddress, UnicastDNSPort, &socketRef ); + require_noerr( err, exit ); + ifd->unicastSocketRef = socketRef; + + // Set up the read pending event and associate it so we can block until data is available for this socket. + + ifd->unicastReadPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( ifd->unicastReadPendingEvent, exit, err = mStatus_NoMemoryErr ); + + err = WSAEventSelect( ifd->unicastSocketRef, ifd->unicastReadPendingEvent, FD_READ ); + require_noerr( err, exit ); + + // Register this interface with mDNS. + + ifd->hostSet.InterfaceID = (mDNSInterfaceID) ifd; + ifd->hostSet.ip.type = mDNSAddrType_IPv4; + ifd->hostSet.ip.ip.v4.NotAnInteger = inAddress->sin_addr.s_addr; + ifd->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses; + + err = mDNS_RegisterInterface( inMDNS, &ifd->hostSet ); + require_noerr( err, exit ); + ifd->hostRegistered = mDNStrue; + + dlog( kDebugLevelInfo, DEBUG_NAME "Registered IP address: %u.%u.%u.%u\n", + ifd->hostSet.ip.ip.v4.b[ 0 ], + ifd->hostSet.ip.ip.v4.b[ 1 ], + ifd->hostSet.ip.ip.v4.b[ 2 ], + ifd->hostSet.ip.ip.v4.b[ 3 ] ); + + // Success! + + *outIFD = ifd; + ifd = NULL; + +exit: + if( ifd ) + { + TearDownInterface( inMDNS, ifd ); + } + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownInterface +//=========================================================================================================================== + +mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD ) +{ + SocketRef socketRef; + + check( inMDNS ); + check( inIFD ); + + // Deregister this interface with mDNS. + + dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering IP address: %u.%u.%u.%u\n", + inIFD->hostSet.ip.ip.v4.b[ 0 ], + inIFD->hostSet.ip.ip.v4.b[ 1 ], + inIFD->hostSet.ip.ip.v4.b[ 2 ], + inIFD->hostSet.ip.ip.v4.b[ 3 ] ); + + if( inIFD->hostRegistered ) + { + inIFD->hostRegistered = mDNSfalse; + mDNS_DeregisterInterface( inMDNS, &inIFD->hostSet ); + } + + // Tear down the multicast socket. + + if( inIFD->multicastReadPendingEvent ) + { + CloseHandle( inIFD->multicastReadPendingEvent ); + inIFD->multicastReadPendingEvent = 0; + } + + socketRef = inIFD->multicastSocketRef; + inIFD->multicastSocketRef = kInvalidSocketRef; + if( IsValidSocket( socketRef ) ) + { + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down multicast socket %d\n", socketRef ); + close_compat( socketRef ); + } + + // Tear down the unicast socket. + + if( inIFD->unicastReadPendingEvent ) + { + CloseHandle( inIFD->unicastReadPendingEvent ); + inIFD->unicastReadPendingEvent = 0; + } + + socketRef = inIFD->unicastSocketRef; + inIFD->unicastSocketRef = kInvalidSocketRef; + if( IsValidSocket( socketRef ) ) + { + dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down unicast socket %d\n", socketRef ); + close_compat( socketRef ); + } + + // Free the memory used by the interface info. + + free( inIFD ); + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// SetupSocket +//=========================================================================================================================== + +mDNSlocal mStatus + SetupSocket( + mDNS * const inMDNS, + const struct sockaddr_in * inAddress, + mDNSIPPort inPort, + SocketRef * outSocketRef ) +{ + mStatus err; + SocketRef socketRef; + int option; + struct ip_mreq mreq; + struct sockaddr_in addr; + mDNSv4Addr ip; + + MDNS_UNUSED( inMDNS ); + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done\n" ); + check( inMDNS ); + check( outSocketRef ); + + // Set up a UDP socket. + + socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + require_action( IsValidSocket( socketRef ), exit, err = mStatus_NoMemoryErr ); + + // Turn on reuse address option so multiple servers can listen for Multicast DNS packets. + + option = 1; + err = setsockopt( socketRef, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) ); + check_errno( err, errno_compat() ); + + // Bind to the specified port (53 for unicast or 5353 for multicast). + + ip.NotAnInteger = inAddress->sin_addr.s_addr; + memset( &addr, 0, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = inPort.NotAnInteger; + addr.sin_addr.s_addr = ip.NotAnInteger; + err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) ); + if( err && ( inPort.NotAnInteger == UnicastDNSPort.NotAnInteger ) ) + { + // Some systems prevent code without root permissions from binding to the DNS port so ignore this + // error since it is not critical. This should only occur with non-root processes. + + err = 0; + } + check_errno( err, errno_compat() ); + + // Join the all-DNS multicast group so we receive Multicast DNS packets. + + if( inPort.NotAnInteger == MulticastDNSPort.NotAnInteger ) + { + mreq.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; + mreq.imr_interface.s_addr = ip.NotAnInteger; + err = setsockopt( socketRef, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof( mreq ) ); + check_errno( err, errno_compat() ); + } + + // Direct multicast packets to the specified interface. + + addr.sin_addr.s_addr = ip.NotAnInteger; + err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr.sin_addr, sizeof( addr.sin_addr ) ); + check_errno( err, errno_compat() ); + + // Set the TTL of outgoing unicast packets to 255 (helps against spoofing). + + option = 255; + err = setsockopt( socketRef, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) ); + check_errno( err, errno_compat() ); + + // Set the TTL of outgoing multicast packets to 255 (helps against spoofing). + + option = 255; + err = setsockopt( socketRef, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &option, sizeof( option ) ); + check_errno( err, errno_compat() ); + + // Success! + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done (%u.%u.%u.%u:%u, %d)\n", + ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], ntohs( inPort.NotAnInteger ), socketRef ); + + *outSocketRef = socketRef; + socketRef = kInvalidSocketRef; + err = mStatus_NoError; + +exit: + if( IsValidSocket( socketRef ) ) + { + close_compat( socketRef ); + } + return( err ); +} + +//=========================================================================================================================== +// SetupNotifications +//=========================================================================================================================== + +mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS ) +{ + mStatus err; + SocketRef socketRef; + unsigned long param; + int inBuffer; + int outBuffer; + DWORD outSize; + + // Register to listen for address list changes. + + socketRef = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + require_action( IsValidSocket( socketRef ), exit, err = mStatus_NoMemoryErr ); + inMDNS->p->interfaceListChangedSocketRef = socketRef; + + // Make the socket non-blocking so the WSAIoctl returns immediately with WSAEWOULDBLOCK. It will set the event + // when a change to the interface list is detected. + + param = 1; + err = ioctlsocket( socketRef, FIONBIO, ¶m ); + require_errno( err, errno_compat(), exit ); + + inBuffer = 0; + outBuffer = 0; + err = WSAIoctl( socketRef, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL ); + if( err < 0 ) + { + check( errno_compat() == WSAEWOULDBLOCK ); + } + + err = WSAEventSelect( socketRef, inMDNS->p->interfaceListChangedEvent, FD_ADDRESS_LIST_CHANGE ); + require_errno( err, errno_compat(), exit ); + +exit: + if( err ) + { + TearDownNotifications( inMDNS ); + } + return( err ); +} + +//=========================================================================================================================== +// TearDownNotifications +//=========================================================================================================================== + +mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS ) +{ + SocketRef socketRef; + + socketRef = inMDNS->p->interfaceListChangedSocketRef; + inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef; + if( IsValidSocket( socketRef ) ) + { + close_compat( socketRef ); + } + return( mStatus_NoError ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// SetupThread +//=========================================================================================================================== + +mDNSlocal mStatus SetupThread( mDNS * const inMDNS ) +{ + mStatus err; + HANDLE threadHandle; + unsigned threadID; + DWORD result; + + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread\n" ); + + // To avoid a race condition with the thread ID needed by the unlocking code, we need to make sure the + // thread has fully initialized. To do this, we create the thread then wait for it to signal it is ready. + + inMDNS->p->initEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + require_action( inMDNS->p->initEvent, exit, err = mStatus_NoMemoryErr ); + inMDNS->p->initStatus = mStatus_Invalid; + + // Create thread with _beginthreadex() instead of CreateThread() to avoid memory leaks when using static run-time + // libraries. See . + + threadHandle = (HANDLE) _beginthreadex( NULL, 0, ProcessingThread, inMDNS, 0, &threadID ); + require_action( threadHandle, exit, err = mStatus_NoMemoryErr ); + + result = WaitForSingleObject( inMDNS->p->initEvent, INFINITE ); + require_action( result == WAIT_OBJECT_0, exit, err = mStatus_UnknownErr ); + err = inMDNS->p->initStatus; + require_noerr( err, exit ); + +exit: + if( inMDNS->p->initEvent ) + { + CloseHandle( inMDNS->p->initEvent ); + inMDNS->p->initEvent = 0; + } + dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// TearDownThread +//=========================================================================================================================== + +mDNSlocal mStatus TearDownThread( const mDNS * const inMDNS ) +{ + DWORD result; + + // Signal the cancel event to cause the thread to exit. Then wait for the quit event to be signal indicating it did + // exit. If the quit event is not signal in 5 seconds, just give up and close anyway sinec the thread is probably hung. + + if( inMDNS->p->cancelEvent ) + { + BOOL wasSet; + + wasSet = SetEvent( inMDNS->p->cancelEvent ); + check( wasSet ); + + if( inMDNS->p->quitEvent ) + { + result = WaitForSingleObject( inMDNS->p->quitEvent, 5 * 1000 ); + check( result == WAIT_OBJECT_0 ); + } + } + return( mStatus_NoError ); +} + +//=========================================================================================================================== +// ProcessingThread +//=========================================================================================================================== + +mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) +{ + mDNS * m; + int done; + mStatus err; + HANDLE * waitList; + int waitListCount; + DWORD result; + BOOL wasSet; + + check( inParam ); + + m = (mDNS *) inParam; + err = ProcessingThreadInitialize( m ); + require_noerr( err, exit ); + + done = 0; + while( !done ) + { + // Set up the list of objects we'll be waiting on. + + waitList = NULL; + waitListCount = 0; + err = ProcessingThreadSetupWaitList( m, &waitList, &waitListCount ); + require_noerr( err, exit ); + + // Main processing loop. + + for( ;; ) + { + // Give the mDNS core a chance to do its work and determine next event time. + + mDNSs32 interval = mDNS_Execute(m) - mDNSPlatformTimeNow(); + if (interval < 0) interval = 0; + else if (interval > (0x7FFFFFFF / 1000)) interval = 0x7FFFFFFF / mDNSPlatformOneSecond; + else interval = (interval * 1000) / mDNSPlatformOneSecond; + + // Wait until something occurs (e.g. cancel, incoming packet, or timeout). + + result = WaitForMultipleObjects( (DWORD) waitListCount, waitList, FALSE, (DWORD) interval ); + if( result == WAIT_TIMEOUT ) + { + // Next task timeout occurred. Loop back up to give mDNS core a chance to work. + + dlog( kDebugLevelChatty - 1, DEBUG_NAME "timeout\n" ); + continue; + } + else if( result == kWaitListCancelEvent ) + { + // Cancel event. Set the done flag and break to exit. + + dlog( kDebugLevelVerbose, DEBUG_NAME "canceling...\n" ); + done = 1; + break; + } + else if( result == kWaitListInterfaceListChangedEvent ) + { + // Interface list changed event. Break out of the inner loop to re-setup the wait list. + + ProcessingThreadInterfaceListChanged( m ); + break; + } + else if( result == kWaitListWakeupEvent ) + { + // Wakeup event due to an mDNS API call. Loop back to call mDNS_Execute. + + dlog( kDebugLevelChatty - 1, DEBUG_NAME "wakeup\n" ); + continue; + } + else + { + int waitItemIndex; + + // Socket data available event. Determine which socket and process the packet. + + waitItemIndex = (int)( ( (int) result ) - WAIT_OBJECT_0 ); + dlog( kDebugLevelChatty, DEBUG_NAME "socket data available on socket index %d\n", waitItemIndex ); + check( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ); + if( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) ) + { + HANDLE signaledObject; + int n; + mDNSInterfaceData * ifd; + + signaledObject = waitList[ waitItemIndex ]; + + n = 0; + for( ifd = m->p->interfaceList; ifd; ifd = ifd->next ) + { + if( ifd->multicastReadPendingEvent == signaledObject ) + { + ProcessingThreadProcessPacket( m, ifd, ifd->multicastSocketRef ); + ++n; + } + if( ifd->unicastReadPendingEvent == signaledObject ) + { + ProcessingThreadProcessPacket( m, ifd, ifd->unicastSocketRef ); + ++n; + } + } + check( n > 0 ); + } + else + { + // Unexpected wait result. + + dlog( kDebugLevelAllowedError, DEBUG_NAME "unexpected wait result (result=0x%08X)\n", result ); + } + } + } + + // Release the wait list. + + if( waitList ) + { + free( waitList ); + waitList = NULL; + waitListCount = 0; + } + } + + // Signal the quit event to indicate that the thread is finished. + +exit: + wasSet = SetEvent( m->p->quitEvent ); + check( wasSet ); + + // Call _endthreadex() explicitly instead of just exiting normally to avoid memory leaks when using static run-time + // libraries. See . + + _endthreadex( 0 ); + return( 0 ); +} + +//=========================================================================================================================== +// ProcessingThreadInitialize +//=========================================================================================================================== + +mDNSlocal mStatus ProcessingThreadInitialize( mDNS * const inMDNS ) +{ + mStatus err; + BOOL wasSet; + + inMDNS->p->threadID = GetCurrentThreadId(); + + err = SetupInterfaceList( inMDNS ); + require_noerr( err, exit ); + +exit: + if( err ) + { + TearDownInterfaceList( inMDNS ); + } + inMDNS->p->initStatus = err; + wasSet = SetEvent( inMDNS->p->initEvent ); + check( wasSet ); + + return( err ); +} + +//=========================================================================================================================== +// ProcessingThreadSetupWaitList +//=========================================================================================================================== + +mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount ) +{ + mStatus err; + int waitListCount; + HANDLE * waitList; + HANDLE * waitItemPtr; + mDNSInterfaceData * ifd; + + dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list\n" ); + check( inMDNS ); + check( inMDNS->p ); + check( outWaitList ); + check( outWaitListCount ); + + // Allocate an array to hold all the objects to wait on. + + waitListCount = kWaitListFixedItemCount + ( 2 * inMDNS->p->interfaceCount ); + waitList = (HANDLE *) malloc( waitListCount * sizeof( *waitList ) ); + require_action( waitList, exit, err = mStatus_NoMemoryErr ); + waitItemPtr = waitList; + + // Add the fixed wait items to the beginning of the list. + + *waitItemPtr++ = inMDNS->p->cancelEvent; + *waitItemPtr++ = inMDNS->p->interfaceListChangedEvent; + *waitItemPtr++ = inMDNS->p->wakeupEvent; + + // Append all the dynamic wait items to the list. + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + *waitItemPtr++ = ifd->multicastReadPendingEvent; + *waitItemPtr++ = ifd->unicastReadPendingEvent; + } + + *outWaitList = waitList; + *outWaitListCount = waitListCount; + waitList = NULL; + err = mStatus_NoError; + +exit: + if( waitList ) + { + free( waitList ); + } + dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list done (err=%ld)\n", err ); + return( err ); +} + +//=========================================================================================================================== +// ProcessingThreadProcessPacket +//=========================================================================================================================== + +mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSocketRef ) +{ + int n; + mDNSBool isMulticast; + DNSMessage packet; + struct sockaddr_in addr; + int addrSize; + mDNSu8 * packetEndPtr; + mDNSAddr srcAddr; + mDNSIPPort srcPort; + mDNSAddr dstAddr; + mDNSIPPort dstPort; + + isMulticast = (mDNSBool)( inSocketRef == inIFD->multicastSocketRef ); + + // Receive the packet. + + addrSize = sizeof( addr ); + n = recvfrom( inSocketRef, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize ); + check( n >= 0 ); + if( n >= 0 ) + { + // Set up the src/dst/interface info. + + srcAddr.type = mDNSAddrType_IPv4; + srcAddr.ip.v4.NotAnInteger = addr.sin_addr.s_addr; + srcPort.NotAnInteger = addr.sin_port; + dstAddr.type = mDNSAddrType_IPv4; + dstAddr.ip.v4 = isMulticast ? AllDNSLinkGroup : inIFD->hostSet.ip.ip.v4; + dstPort = isMulticast ? MulticastDNSPort : UnicastDNSPort; + + dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" ); + dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", n ); + dlog( kDebugLevelChatty, DEBUG_NAME " src = %u.%u.%u.%u:%u\n", + srcAddr.ip.v4.b[ 0 ], srcAddr.ip.v4.b[ 1 ], srcAddr.ip.v4.b[ 2 ], srcAddr.ip.v4.b[ 3 ], + ntohs( srcPort.NotAnInteger ) ); + dlog( kDebugLevelChatty, DEBUG_NAME " dst = %u.%u.%u.%u:%u\n", + dstAddr.ip.v4.b[ 0 ], dstAddr.ip.v4.b[ 1 ], dstAddr.ip.v4.b[ 2 ], dstAddr.ip.v4.b[ 3 ], + ntohs( dstPort.NotAnInteger ) ); + dlog( kDebugLevelChatty, DEBUG_NAME " interface = %u.%u.%u.%u\n", + inIFD->hostSet.ip.ip.v4.b[ 0 ], inIFD->hostSet.ip.ip.v4.b[ 1 ], + inIFD->hostSet.ip.ip.v4.b[ 2 ], inIFD->hostSet.ip.ip.v4.b[ 3 ] ); + + dlog( kDebugLevelChatty, DEBUG_NAME "--\n" ); + + // Dispatch the packet to mDNS. + + packetEndPtr = ( (mDNSu8 *) &packet ) + n; + mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inIFD->hostSet.InterfaceID, 255 ); + } + + // Update counters. + + inIFD->recvMulticastCounter += isMulticast; + inIFD->recvUnicastCounter += !isMulticast; + inIFD->recvErrorCounter += ( n < 0 ); +} + +//=========================================================================================================================== +// ProcessingThreadInterfaceListChanged +//=========================================================================================================================== + +mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed event\n" ); + check( inMDNS ); + + mDNSPlatformLock( inMDNS ); + + // Tear down the existing interfaces and set up new ones using the new IP info. + + err = TearDownInterfaceList( inMDNS ); + check_noerr( err ); + + err = SetupInterfaceList( inMDNS ); + check_noerr( err ); + + mDNSPlatformUnlock( inMDNS ); + + // Inform clients of the change. + + if( inMDNS->MainCallback ) + { + inMDNS->MainCallback( inMDNS, mStatus_ConfigChanged ); + } + + // Force mDNS to update. + + mDNSCoreMachineSleep( inMDNS, mDNSfalse ); +} + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +#if( defined( _WIN32_WCE ) ) +//=========================================================================================================================== +// getifaddrs +//=========================================================================================================================== + +int getifaddrs( struct ifaddrs **outAddrs ) +{ + int err; + SocketRef sock; + DWORD size; + void * buffer; + SOCKET_ADDRESS_LIST * addressList; + struct ifaddrs * head; + struct ifaddrs ** next; + struct ifaddrs * ifa; + int n; + int i; + + sock = kInvalidSocketRef; + buffer = NULL; + head = NULL; + next = &head; + + // Open a temporary socket because one is needed to use WSAIoctl (we'll close it before exiting this function). + + sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + require_action( IsValidSocket( sock ), exit, err = mStatus_NoMemoryErr ); + + // Call WSAIoctl with SIO_ADDRESS_LIST_QUERY and pass a null buffer. This call will fail, but the size needed to + // for the request will be filled in. Once we know the size, allocate a buffer to hold the entire list. + // + // NOTE: Due to a bug in Windows CE, the size returned by WSAIoctl is not enough so double it as a workaround. + + size = 0; + WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0, &size, NULL, NULL ); + require_action( size > 0, exit, err = -1 ); + size *= 2; + + buffer = malloc( size ); + require_action( buffer, exit, err = -1 ); + + // We now know the size of the list and have a buffer to hold so call WSAIoctl again to get it. + + err = WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, buffer, size, &size, NULL, NULL ); + require_noerr( err, exit ); + addressList = (SOCKET_ADDRESS_LIST *) buffer; + + // Process the raw interface list and build a linked list of interfaces. + // + // NOTE: Due to a bug in Windows CE, the iAddressCount field is always 0 so use 1 in that case. + + n = addressList->iAddressCount; + if( n == 0 ) + { + n = 1; + } + for( i = 0; i < n; ++i ) + { + ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); + require_action( ifa, exit, err = WSAENOBUFS ); + + *next = ifa; + next = &ifa->ifa_next; + + // Fetch the name. $$$ TO DO: Get the real name of the interface. + + ifa->ifa_name = (char *) malloc( 16 ); + require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); + sprintf( ifa->ifa_name, "%d", i + 1 ); + + // Fetch flags. Note: SIO_ADDRESS_LIST_QUERY does not report flags so just fake IFF_UP and IFF_MULTICAST. + + ifa->ifa_flags = IFF_UP | IFF_MULTICAST; + + // Fetch addresses. + + switch( addressList->Address[ i ].lpSockaddr->sa_family ) + { + case AF_INET: + { + struct sockaddr_in * sinptr4; + + sinptr4 = (struct sockaddr_in *) addressList->Address[ i ].lpSockaddr; + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) ); + require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_addr, sinptr4, sizeof( *sinptr4 ) ); + break; + } + + default: + break; + } + } + + // Success! + + if( outAddrs ) + { + *outAddrs = head; + head = NULL; + } + err = 0; + +exit: + if( head ) + { + freeifaddrs( head ); + } + if( buffer ) + { + free( buffer ); + } + if( sock != INVALID_SOCKET ) + { + closesocket( sock ); + } + return( err ); +} +#endif // defined( _WIN32_WCE ) ) + +#if( !defined( _WIN32_WCE ) ) +//=========================================================================================================================== +// getifaddrs +//=========================================================================================================================== + +int getifaddrs( struct ifaddrs **outAddrs ) +{ + int err; + SOCKET sock; + DWORD size; + DWORD actualSize; + INTERFACE_INFO * buffer; + INTERFACE_INFO * tempBuffer; + INTERFACE_INFO * ifInfo; + int n; + int i; + struct ifaddrs * head; + struct ifaddrs ** next; + struct ifaddrs * ifa; + struct sockaddr_in * sinptr4; + struct sockaddr_in6_old * sinptr6; + struct sockaddr * sa; + + sock = INVALID_SOCKET; + buffer = NULL; + head = NULL; + next = &head; + + // Get the interface list. WSAIoctl is called with SIO_GET_INTERFACE_LIST, but since this does not provide a + // way to determine the size of the interface list beforehand, we have to start with an initial size guess and + // call WSAIoctl repeatedly with increasing buffer sizes until it succeeds. Limit this to 100 tries for safety. + + sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + require_action( sock != INVALID_SOCKET, exit, err = WSAEMFILE ); + + n = 0; + size = 16 * sizeof( INTERFACE_INFO ); + for( ;; ) + { + tempBuffer = (INTERFACE_INFO *) realloc( buffer, size ); + require_action( tempBuffer, exit, err = WSAENOBUFS ); + buffer = tempBuffer; + + err = WSAIoctl( sock, SIO_GET_INTERFACE_LIST, NULL, 0, buffer, size, &actualSize, NULL, NULL ); + if( err == 0 ) + { + break; + } + + ++n; + require_action( n < 100, exit, err = WSAEADDRNOTAVAIL ); + + size += ( 16 * sizeof( INTERFACE_INFO ) ); + } + check( actualSize <= size ); + check( ( actualSize % sizeof( INTERFACE_INFO ) ) == 0 ); + n = (int)( actualSize / sizeof( INTERFACE_INFO ) ); + + // Process the raw interface list and build a linked list of interfaces. + + for( i = 0; i < n; ++i ) + { + ifInfo = &buffer[ i ]; + + ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); + require_action( ifa, exit, err = WSAENOBUFS ); + + *next = ifa; + next = &ifa->ifa_next; + + // Fetch the name. $$$ TO DO: Get the real name of the interface. + + ifa->ifa_name = (char *) malloc( 16 ); + require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); + sprintf( ifa->ifa_name, "%d", i + 1 ); + + // Fetch interface flags. + + ifa->ifa_flags = (u_int) ifInfo->iiFlags; + + // Fetch addresses. + + switch( ifInfo->iiAddress.Address.sa_family ) + { + case AF_INET: + sinptr4 = &ifInfo->iiAddress.AddressIn; + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) ); + require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_addr, sinptr4, sizeof( *sinptr4 ) ); + + if( ifInfo->iiNetmask.Address.sa_family == AF_INET ) + { + sinptr4 = &ifInfo->iiNetmask.AddressIn; + ifa->ifa_netmask = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) ); + require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_netmask, sinptr4, sizeof( *sinptr4 ) ); + } + + if( ifInfo->iiBroadcastAddress.Address.sa_family == AF_INET ) + { + sinptr4 = &ifInfo->iiBroadcastAddress.AddressIn; + ifa->ifa_broadaddr = (struct sockaddr *) calloc( 1, sizeof( *sinptr4 ) ); + require_action( ifa->ifa_broadaddr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_broadaddr, sinptr4, sizeof( *sinptr4 ) ); + } + break; + + case AF_INET6: + sinptr6 = &ifInfo->iiAddress.AddressIn6; + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr6 ) ); + require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_addr, sinptr6, sizeof( *sinptr6 ) ); + break; + + default: + sa = &ifInfo->iiAddress.Address; + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa ) ); + require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_addr, sa, sizeof( *sa ) ); + break; + } + } + + // Success! + + if( outAddrs ) + { + *outAddrs = head; + head = NULL; + } + err = 0; + +exit: + if( head ) + { + freeifaddrs( head ); + } + if( buffer ) + { + free( buffer ); + } + if( sock != INVALID_SOCKET ) + { + closesocket( sock ); + } + return( err ); +} +#endif // !defined( _WIN32_WCE ) ) + +//=========================================================================================================================== +// freeifaddrs +//=========================================================================================================================== + +void freeifaddrs( struct ifaddrs *inAddrs ) +{ + struct ifaddrs * p; + struct ifaddrs * q; + + // Free each piece of the structure. Set to null after freeing to handle macro-aliased fields. + + for( p = inAddrs; p; p = q ) + { + q = p->ifa_next; + + if( p->ifa_name ) + { + free( p->ifa_name ); + p->ifa_name = NULL; + } + if( p->ifa_addr ) + { + free( p->ifa_addr ); + p->ifa_addr = NULL; + } + if( p->ifa_netmask ) + { + free( p->ifa_netmask ); + p->ifa_netmask = NULL; + } + if( p->ifa_broadaddr ) + { + free( p->ifa_broadaddr ); + p->ifa_broadaddr = NULL; + } + if( p->ifa_dstaddr ) + { + free( p->ifa_dstaddr ); + p->ifa_dstaddr = NULL; + } + if( p->ifa_data ) + { + free( p->ifa_data ); + p->ifa_data = NULL; + } + free( p ); + } +} + +#if( !defined( _WIN32_WCE ) ) +//=========================================================================================================================== +// sock_pton +//=========================================================================================================================== + +int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ) +{ + int err; + int size; + + if( inAddrSize == 0 ) + { + if( inFamily == AF_INET ) + { + inAddrSize = sizeof( struct sockaddr_in ); + } + else if( inFamily == AF_INET6 ) + { + inAddrSize = sizeof( struct sockaddr_in6 ); + } + else + { + err = WSAEAFNOSUPPORT; + goto exit; + } + } + size = (int) inAddrSize; + + err = WSAStringToAddressA( (char *) inString, inFamily, NULL, (LPSOCKADDR) outAddr, &size ); + if( err != 0 ) goto exit; + + if( outAddrSize ) + { + *outAddrSize = (size_t) size; + } + +exit: + return( err ); +} + +//=========================================================================================================================== +// sock_ntop +//=========================================================================================================================== + +char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ) +{ + DWORD size; + int err; + DWORD stringSize; + + if( inAddrSize == 0 ) + { + const struct sockaddr * addr; + + addr = (const struct sockaddr *) inAddr; + if( addr->sa_family == AF_INET ) + { + size = sizeof( struct sockaddr_in ); + } + else if( addr->sa_family == AF_INET6 ) + { + size = sizeof( struct sockaddr_in6 ); + } + else + { + WSASetLastError( WSAEAFNOSUPPORT ); + inBuffer = NULL; + goto exit; + } + } + else + { + size = (DWORD) inAddrSize; + } + + stringSize = (DWORD) inBufferSize; + err = WSAAddressToStringA( (LPSOCKADDR) inAddr, size, NULL, inBuffer, &stringSize ); + if( err ) + { + inBuffer = NULL; + } + +exit: + return( inBuffer ); +} +#endif // !defined( _WIN32_WCE ) diff --git a/mDNSWindows/mDNSWin32.h b/mDNSWindows/mDNSWin32.h new file mode 100755 index 0000000..2bf5ddd --- /dev/null +++ b/mDNSWindows/mDNSWin32.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSWin32.h,v $ +Revision 1.9 2003/08/20 06:21:25 bradley +Updated to latest internal version of the Rendezvous for Windows platform plugin: Added support +for Windows CE/PocketPC 2003; re-did interface-related code to emulate getifaddrs/freeifaddrs for +restricting usage to only active, multicast-capable, and non-point-to-point interfaces and to ease +the addition of IPv6 support in the future; Changed init code to serialize thread initialization to +enable ThreadID improvement to wakeup notification; Define platform support structure locally to +allow portable mDNS_Init usage; Removed dependence on modified mDNSCore: define interface ID<->name +structures/prototypes locally; Changed to use _beginthreadex()/_endthreadex() on non-Windows CE +platforms (re-mapped to CreateThread on Window CE) to avoid a leak in the Microsoft C runtime; +Added IPv4/IPv6 string<->address conversion routines; Cleaned up some code and added HeaderDoc. + +Revision 1.8 2003/08/12 19:56:27 cheshire +Update to APSL 2.0 + +Revision 1.7 2003/07/23 02:23:01 cheshire +Updated mDNSPlatformUnlock() to work correctly, now that +"ScheduleNextTask needs to be smarter" has refined the way m->NextScheduledEvent is set + +Revision 1.6 2003/07/02 21:20:04 cheshire + Update copyright notices, etc., in source code comments + +Revision 1.5 2003/04/29 00:06:09 cheshire + mDNSWindows needs a wakeupEvent object to signal the main thread + +Revision 1.4 2003/03/22 02:57:44 cheshire +Updated mDNSWindows to use new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt") + +Revision 1.3 2002/09/21 20:44:54 zarzycki +Added APSL info + +Revision 1.2 2002/09/20 05:55:16 bradley +Multicast DNS platform plugin for Win32 + +*/ + +#ifndef __MDNS_WIN32__ +#define __MDNS_WIN32__ + +#if( !defined( WIN32_LEAN_AND_MEAN ) ) + #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces. +#endif + +#include +#include +#include + +#include "mDNSClientAPI.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef SocketRef + + @abstract Socket file descriptor alias for improved readability. +*/ + +typedef SOCKET SocketRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct mDNSInterfaceData + + @abstract Structure containing interface-specific data. +*/ + +typedef struct mDNSInterfaceData mDNSInterfaceData; +struct mDNSInterfaceData +{ + mDNSInterfaceData * next; + char name[ 256 ]; + SocketRef multicastSocketRef; + HANDLE multicastReadPendingEvent; + SocketRef unicastSocketRef; + HANDLE unicastReadPendingEvent; + NetworkInterfaceInfo hostSet; + mDNSBool hostRegistered; + + int sendMulticastCounter; + int sendUnicastCounter; + int sendErrorCounter; + + int recvMulticastCounter; + int recvUnicastCounter; + int recvErrorCounter; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct mDNS_PlatformSupport_struct + + @abstract Structure containing platform-specific data. +*/ + +struct mDNS_PlatformSupport_struct +{ + CRITICAL_SECTION lock; + mDNSBool lockInitialized; + HANDLE cancelEvent; + HANDLE quitEvent; + HANDLE interfaceListChangedEvent; + HANDLE wakeupEvent; + HANDLE initEvent; + mStatus initStatus; + + SocketRef interfaceListChangedSocketRef; + int interfaceCount; + mDNSInterfaceData * interfaceList; + DWORD threadID; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct ifaddrs + + @abstract Interface information +*/ + +struct ifaddrs +{ + struct ifaddrs * ifa_next; + char * ifa_name; + u_int ifa_flags; + struct sockaddr * ifa_addr; + struct sockaddr * ifa_netmask; + struct sockaddr * ifa_broadaddr; + struct sockaddr * ifa_dstaddr; + void * ifa_data; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function getifaddrs + + @abstract Builds a linked list of interfaces. Caller must free using freeifaddrs if successful. +*/ + +int getifaddrs( struct ifaddrs **outAddrs ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function freeifaddrs + + @abstract Frees a linked list of interfaces built with getifaddrs. +*/ + +void freeifaddrs( struct ifaddrs *inAddrs ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function sock_pton + + @abstract Converts a 'p'resentation address string into a 'n'umeric sockaddr structure. + + @result 0 if successful or an error code on failure. +*/ + +int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function sock_ntop + + @abstract Converts a 'n'umeric sockaddr structure into a 'p'resentation address string. + + @result Ptr to 'p'resentation address string buffer if successful or NULL on failure. +*/ + +char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ); + +#ifdef __cplusplus + } +#endif + +#endif // __MDNS_WIN32__ -- 2.47.2