]> git.saurik.com Git - apple/mdnsresponder.git/commitdiff
mDNSResponder-58.tar.gz mac-os-x-103 mac-os-x-1031 v58
authorApple <opensource@apple.com>
Wed, 10 Sep 2003 00:51:47 +0000 (00:51 +0000)
committerApple <opensource@apple.com>
Wed, 10 Sep 2003 00:51:47 +0000 (00:51 +0000)
122 files changed:
APPLE_LICENSE [new file with mode: 0644]
CFSocket.c [deleted file]
DNSServiceDiscoveryDefines.h [deleted file]
DNSServiceDiscoveryReply.defs [deleted file]
DNSServiceDiscoveryRequest.defs [deleted file]
Makefile [new file with mode: 0644]
README.txt [new file with mode: 0644]
SamplemDNSClient.c [deleted file]
daemon.c [deleted file]
mDNSCore/Implementer Notes.txt [new file with mode: 0644]
mDNSCore/mDNS.c
mDNSCore/mDNSClientAPI.h
mDNSCore/mDNSDebug.h
mDNSCore/mDNSPlatformEnvironment.h [deleted file]
mDNSCore/mDNSPlatformFunctions.h
mDNSCore/mDNSsprintf.c [deleted file]
mDNSCore/mDNSsprintf.h [deleted file]
mDNSCore/mDNSvsprintf.h [deleted file]
mDNSMacOS9/CarbonResource.r [new file with mode: 0644]
mDNSMacOS9/Mac OS Test Responder.c [new file with mode: 0644]
mDNSMacOS9/Mac OS Test Searcher.c [new file with mode: 0644]
mDNSMacOS9/README.txt [new file with mode: 0644]
mDNSMacOS9/mDNS.mcp [new file with mode: 0644]
mDNSMacOS9/mDNSMacOS9.c [new file with mode: 0644]
mDNSMacOS9/mDNSMacOS9.h [new file with mode: 0755]
mDNSMacOS9/mDNSPrefixCarbon.h [new file with mode: 0644]
mDNSMacOS9/mDNSPrefixCarbonDebug.h [new file with mode: 0644]
mDNSMacOS9/mDNSPrefixClassic.h [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h [new file with mode: 0755]
mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m [new file with mode: 0755]
mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceBrowser/main.m [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/classes.nib [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/info.nib [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.h [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m [new file with mode: 0644]
mDNSMacOSX/Applications/DNSServiceRegistration/main.m [new file with mode: 0644]
mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h [new file with mode: 0644]
mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m [new file with mode: 0644]
mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj [new file with mode: 0644]
mDNSMacOSX/Applications/HAAutomounter/main.m [new file with mode: 0644]
mDNSMacOSX/CFSocket.c [new file with mode: 0644]
mDNSMacOSX/CFSocketPuma.c [new file with mode: 0644]
mDNSMacOSX/DNSServiceDiscoveryDefines.h [new file with mode: 0644]
mDNSMacOSX/DNSServiceDiscoveryReply.defs [new file with mode: 0644]
mDNSMacOSX/DNSServiceDiscoveryRequest.defs [new file with mode: 0644]
mDNSMacOSX/SampleUDSClient.c [new file with mode: 0755]
mDNSMacOSX/SamplemDNSClient.c [new file with mode: 0644]
mDNSMacOSX/daemon.c [new file with mode: 0644]
mDNSMacOSX/dns_sd.h [new file with mode: 0755]
mDNSMacOSX/dnssd_clientstub.c [new file with mode: 0755]
mDNSMacOSX/dnssd_ipc.c [new file with mode: 0644]
mDNSMacOSX/dnssd_ipc.h [new file with mode: 0644]
mDNSMacOSX/mDNSMacOSX.h [new file with mode: 0644]
mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj [new file with mode: 0644]
mDNSMacOSX/uds_daemon.c [new file with mode: 0644]
mDNSPosix/Client.c [new file with mode: 0755]
mDNSPosix/ExampleClientApp.c [new file with mode: 0644]
mDNSPosix/ExampleClientApp.h [new file with mode: 0644]
mDNSPosix/Identify.c [new file with mode: 0644]
mDNSPosix/Makefile [new file with mode: 0755]
mDNSPosix/NetMonitor.c [new file with mode: 0644]
mDNSPosix/ProxyResponder.c [new file with mode: 0644]
mDNSPosix/ReadMe.txt [new file with mode: 0755]
mDNSPosix/Responder.c [new file with mode: 0755]
mDNSPosix/Services.txt [new file with mode: 0755]
mDNSPosix/mDNSPosix.c [new file with mode: 0755]
mDNSPosix/mDNSPosix.h [new file with mode: 0755]
mDNSPosix/mDNSUNP.c [new file with mode: 0755]
mDNSPosix/mDNSUNP.h [new file with mode: 0755]
mDNSResponder.pbproj/project.pbxproj [deleted file]
mDNSVxWorks/README.txt [new file with mode: 0644]
mDNSVxWorks/mDNSVxWorks.c [new file with mode: 0644]
mDNSVxWorks/mDNSVxWorks.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Application.sln [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Application.vcproj [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc2 [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Resource.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.cpp [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/AboutDialog.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.cpp [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/Application.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.cpp [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/ChooserDialog.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.cpp [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/Windows/Sources/StdAfx.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcc [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcp [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Application.vcw [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.ico [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc2 [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/newres.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/resource.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.cpp [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/Application.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.cpp [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/BrowserDialog.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.cpp [new file with mode: 0644]
mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Sources/StdAfx.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousTest/Tool.c [new file with mode: 0644]
mDNSWindows/Applications/RendezvousTest/ToolPrefixWindows.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousTest/ToolPrefixWindowsDebug.h [new file with mode: 0644]
mDNSWindows/Applications/RendezvousTest/ToolWin32.mcp [new file with mode: 0644]
mDNSWindows/Applications/RendezvousTest/ToolWin32.sln [new file with mode: 0644]
mDNSWindows/Applications/RendezvousTest/ToolWin32.vcproj [new file with mode: 0644]
mDNSWindows/DNSServices/DNSServiceDiscovery.c [new file with mode: 0644]
mDNSWindows/DNSServices/DNSServiceDiscovery.h [new file with mode: 0644]
mDNSWindows/DNSServices/DNSServices.c [new file with mode: 0755]
mDNSWindows/DNSServices/DNSServices.h [new file with mode: 0755]
mDNSWindows/README.txt [new file with mode: 0644]
mDNSWindows/mDNSWin32.c [new file with mode: 0755]
mDNSWindows/mDNSWin32.h [new file with mode: 0755]

diff --git a/APPLE_LICENSE b/APPLE_LICENSE
new file mode 100644 (file)
index 0000000..fe81a60
--- /dev/null
@@ -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 (file)
index 292d586..0000000
+++ /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 <stdio.h>
-#include <stdarg.h>                  // For va_list support
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <sys/uio.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-
-// 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 <sys/ioctl.h>
-#include <sys/sockio.h>
-#define ifaddrs ifa_info
-#ifndef        ifa_broadaddr
-#define        ifa_broadaddr   ifa_dstaddr     /* broadcast address interface */
-#endif
-#include <sys/cdefs.h>
-
-#else
-
-#include <ifaddrs.h>
-
-#endif
-
-#include <IOKit/IOKitLib.h>
-#include <IOKit/IOMessage.h>
-
-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 <net/if.h> */
-#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 <net/if.h> */
-  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 (file)
index 1c8e8ea..0000000
+++ /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 <DNSServiceDiscovery/DNSServiceDiscoveryDefines.h>
-
diff --git a/DNSServiceDiscoveryReply.defs b/DNSServiceDiscoveryReply.defs
deleted file mode 100644 (file)
index c08e86f..0000000
+++ /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 (file)
index fc17bd3..0000000
+++ /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 (file)
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 (file)
index 0000000..60abf83
--- /dev/null
@@ -0,0 +1,145 @@
+What is mDNSResponder?
+----------------------
+
+The mDNSResponder project is a component of Rendezvous,
+Apple's ease-of-use IP networking initiative:
+<http://developer.apple.com/macosx/rendezvous/index.html>
+
+Apple's Rendezvous software derives from the ongoing standardization
+work of the IETF Zero Configuration Networking Working Group:
+<http://zeroconf.org/>
+
+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"
+<http://files.zeroconf.org/draft-ietf-zeroconf-ipv4-linklocal.txt>
+
+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/SamplemDNSClient.c b/SamplemDNSClient.c
deleted file mode 100644 (file)
index 8f14f2d..0000000
+++ /dev/null
@@ -1,329 +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 <http://www.kafejo.com/komp/1tbs.htm>,
- * 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 <libc.h>
-#include <arpa/nameser.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
-
-//*************************************************************************************************************
-// Globals
-
-typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
-
-static char operation;
-static dns_service_discovery_ref client = NULL;
-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 bigNULL[4096];
-
-//*************************************************************************************************************
-// Supporting Utility Functions
-//
-// This code takes care of:
-// 1. Extracting the mach_port_t from the dns_service_discovery_ref
-// 2. Making a CFMachPortRef from it
-// 3. Making a CFRunLoopSourceRef from that
-// 4. Adding that source to the current RunLoop
-// 5. and passing the resulting messages back to DNSServiceDiscovery_handleReply() for processing
-//
-// Code that's not based around a CFRunLoop will need its own mechanism to receive Mach messages
-// from the mDNSResponder daemon and pass them to the DNSServiceDiscovery_handleReply() routine.
-// (There is no way to automate this, because it varies depending on the application's existing
-// event handling model.)
-
-static void MyHandleMachMessage(CFMachPortRef port, void *msg, CFIndex size, void *info)
-       {
-       DNSServiceDiscovery_handleReply(msg);
-       }
-
-static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref client)
-    {
-       mach_port_t port = DNSServiceDiscoveryMachPort(client);
-    if (!port)
-        return(-1);
-    else
-        {
-        CFMachPortContext  context    = { 0, 0, NULL, NULL, NULL };
-        Boolean            shouldFreeInfo;
-        CFMachPortRef      cfMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault, port, MyHandleMachMessage, &context, &shouldFreeInfo);
-        CFRunLoopSourceRef rls        = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
-        CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
-        CFRelease(rls);
-        return(0);
-        }
-    }
-
-//*************************************************************************************************************
-// Sample callback functions for each of the operation types
-
-#define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain        ? "Added"     :          \
-                      (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" :          \
-                      (X) == DNSServiceDomainEnumerationReplyRemoveDomain     ? "Removed"   : "Unknown")
-
-static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
-    DNSServiceDiscoveryReplyFlags flags, void *context)
-       {
-       printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(resultType));
-       if (flags) printf(" Flags: %X", flags);
-       printf("\n");
-       }
-
-static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
-    DNSServiceDiscoveryReplyFlags flags, void *context)
-       {
-       printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(resultType));
-       if (flags) printf(" Flags: %X", flags);
-       printf("\n");
-       }
-
-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");
-       }
-
-static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context)
-       {
-       if (address->sa_family != AF_INET)
-               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)
-            {
-            char txtInfo[256];
-            char *dst = txtInfo;
-            const char *const lim = &txtInfo[sizeof(txtInfo)];
-            while (*src && *src != 1 && dst < lim-1) *dst++ = *src++;
-            *dst++ = 0;
-            printf(" TXT \"%s\"", txtInfo);
-            if (*src == 1) src++;
-            }
-               if (flags) printf(" Flags: %X", flags);
-               printf("\n");
-               }
-       }
-
-static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info)
-       {
-       (void)timer;    // Parameter not used
-       (void)info;             // Parameter not used
-    
-    switch (operation)
-        {
-        case 'A':
-            {
-            switch (addtest)
-                {
-                case 0: printf("Adding Test HINFO record\n");
-                        record = DNSServiceRegistrationAddRecord(client, T_HINFO, sizeof(myhinfo9), &myhinfo9[0], 120);
-                        addtest = 1;
-                        break;
-                case 1: printf("Updating Test HINFO record\n");
-                        DNSServiceRegistrationUpdateRecord(client, record, sizeof(myhinfoX), &myhinfoX[0], 120);
-                        addtest = 2;
-                        break;
-                case 2: printf("Removing Test HINFO record\n");
-                        DNSServiceRegistrationRemoveRecord(client, record);
-                        addtest = 0;
-                        break;
-                }
-            }
-            break;
-
-        case 'U':
-            {
-            if (updatetest[1] != 'Z') updatetest[1]++;
-            else                      updatetest[1] = 'A';
-            printf("Updating Test TXT record to %c\n", updatetest[1]);
-            DNSServiceRegistrationUpdateRecord(client, 0, sizeof(updatetest), &updatetest[0], 120);
-            }
-            break;
-
-        case 'N':
-            {
-            printf("Adding big NULL record\n");
-            DNSServiceRegistrationAddRecord(client, T_NULL, sizeof(bigNULL), &bigNULL[0], 120);
-            CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
-            }
-            break;
-        }
-    }
-
-static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
-       {
-    printf("Got a reply from the server: ");
-    switch (errorCode)
-        {
-        case kDNSServiceDiscoveryNoError:      printf("Name now registered and active\n"); break;
-        case kDNSServiceDiscoveryNameConflict: printf("Name in use, please choose another\n"); exit(-1);
-        default:                               printf("Error %d\n", errorCode); return;
-        }
-
-    if (operation == 'A' || operation == 'U' || operation == 'N')
-        {
-        CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, 0, NULL, NULL, NULL };
-        CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault,
-            CFAbsoluteTimeGetCurrent() + 5.0, 5.0, 0, 1,       // Next fire time, periodic interval, flags, and order
-                                myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext);
-        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
-        }
-       }
-
-//*************************************************************************************************************
-// The main test function
-
-int main(int argc, char **argv)
-       {
-       char *dom;
-
-       if (argc < 2) goto Fail;                // Minimum command line is the command name and one argument
-    operation = getopt(argc, (char * const *)argv, "EFBLRAUNTM");
-       if (operation == -1) goto Fail;
-
-    switch (operation)
-        {
-        case 'E':      printf("Looking for recommended registration domains:\n");
-                    client = DNSServiceDomainEnumerationCreate(1, regdom_reply, nil);
-                    break;
-
-        case 'F':      printf("Looking for recommended browsing domains:\n");
-                    client = DNSServiceDomainEnumerationCreate(0, browsedom_reply, nil);
-                    break;
-
-        case 'B':      if (argc < optind+1) goto Fail;
-                    dom = (argc < optind+2) ? "" : argv[optind+1];
-                    if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;      // We allow '.' on the command line as a synonym for empty string
-                    printf("Browsing for %s%s\n", argv[optind+0], dom);
-                    client = DNSServiceBrowserCreate(argv[optind+0], dom, browse_reply, nil);
-                    break;
-
-        case 'L':      if (argc < optind+2) goto Fail;
-                    dom = (argc < optind+3) ? "" : argv[optind+2];
-                    if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;      // We allow '.' on the command line as a synonym for empty string
-                    printf("Lookup %s.%s%s\n", argv[optind+0], argv[optind+1], dom);
-                    client = DNSServiceResolverResolve(argv[optind+0], argv[optind+1], dom, resolve_reply, nil);
-                    break;
-
-        case 'R':      if (argc < optind+4) goto Fail;
-                    {
-                    char *nam = argv[optind+0];
-                    char *typ = argv[optind+1];
-                    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] : "";
-                    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
-                    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;
-                    }
-
-        case 'A':
-        case 'U':
-        case 'N':      {
-                    Opaque16 registerPort = { { 0x12, 0x34 } };
-                    static const char TXT[] = "First String\001Second String\001Third String";
-                    printf("Registering Service Test._testupdate._tcp.local.\n");
-                    client = DNSServiceRegistrationCreate("Test", "_testupdate._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
-                    break;
-                    }
-
-        case 'T':      {
-                    Opaque16 registerPort = { { 0x23, 0x45 } };
-                    char TXT[512];
-                    int i;
-                    for (i=0; i<sizeof(TXT)-1; i++)
-                        if ((i & 0x1F) == 0x1F) TXT[i] = 1; else TXT[i] = 'A' + (i >> 5);
-                    TXT[i] = 0;
-                    printf("Registering Service Test._testlargetxt._tcp.local.\n");
-                    client = DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
-                    break;
-                    }
-
-        case 'M':      {
-                    Opaque16 registerPort = { { 0x23, 0x45 } };
-                    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);
-                    break;
-                    }
-
-        default: goto Exit;
-        }
-
-    if (!client) { fprintf(stderr, "DNSService call failed\n"); return (-1); }
-    if (AddDNSServiceClientToRunLoop(client) != 0) { fprintf(stderr, "AddDNSServiceClientToRunLoop failed\n"); return (-1); }
-    printf("Talking to DNS SD Daemon at Mach port %d\n", DNSServiceDiscoveryMachPort(client));
-       CFRunLoopRun();
-    
-    // Be sure to deallocate the dns_service_discovery_ref when you're finished
-    // Note: What other cleanup has to be done here?
-    // We should probably invalidate, remove and release our CFRunLoopSourceRef?
-    DNSServiceDiscoveryDeallocate(client);
-    
-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        <Type> <Domain>   (Browse for services instances)\n", argv[0]);
-       fprintf(stderr, "%s -L <Name> <Type> <Domain>      (Look up a service instance)\n", argv[0]);
-       fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> <TXT> (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/daemon.c b/daemon.c
deleted file mode 100644 (file)
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 <http://www.kafejo.com/komp/1tbs.htm>,
- * 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 <mach/mach.h>
-#include <mach/mach_error.h>
-#include <servers/bootstrap.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#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 <DNSServiceDiscovery/DNSServiceDiscovery.h>
-
-//*************************************************************************************************************
-// 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; i<query->info->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<argc; i++)
-               {
-               if (!strcmp(argv[i], "-d")) debug_mode = 1;
-               }
-
-       signal(SIGINT, HandleSIG);      // SIGINT is what you get for a Ctrl-C
-       signal(SIGTERM, HandleSIG);
-
-       // Register the server with mach_init for automatic restart only during debug mode
-    if (!debug_mode)
-               registerBootstrapService();
-
-       if (!debug_mode && !restarting_via_mach_init)
-               exit(0); /* mach_init will restart us immediately as a daemon */
-
-       fp = fopen(PID_FILE, "w");
-       if (fp != NULL)
-               {
-               fprintf(fp, "%d\n", getpid());
-               fclose(fp);
-               }
-       
-       LogErrorMessage("mDNSResponder (%s %s) starting", __DATE__, __TIME__);
-       status = start(NULL, NULL);
-
-       if (status == 0)
-               {
-               CFRunLoopRun();
-               LogErrorMessage("CFRunLoopRun Exiting. This is bad.");
-               mDNS_Close(&mDNSStorage);
-               }
-
-       destroyBootstrapService();
-
-       return(status);
-       }
diff --git a/mDNSCore/Implementer Notes.txt b/mDNSCore/Implementer Notes.txt
new file mode 100644 (file)
index 0000000..60c9b3a
--- /dev/null
@@ -0,0 +1,66 @@
+February 2002:
+
+The mDNSResponder code has a slight architectural change to improve
+efficiency.
+
+The mDNSResponder code previously called ScheduleNextTask() after every
+operation, to calculate the time at which it needed to be called back to
+perform its next timed operation. When the workload is light, and
+protocol operations are rare and far apart, this makes sense.
+
+However, on networks where there is a lot of mDNS traffic (or the CPU is
+slow), this leads to the following anomolous behaviour: mDNSResponder
+spends a lot of CPU time working out what to do next, when what it needs
+to do next should be obvious: Finish processing the big backlog of
+packets that have been received.
+
+To remedy this, mDNSResponder now only executes ScheduleNextTask() when
+there is no other obvious work waiting to be done. However, the
+mDNSResponder code does not have direct access to this knowledge. Only
+the platform layer below knows whether there are packets waiting to be
+processed. Only the client layer above knows whether it is in the
+process of performing a long sequence of back-to-back mDNS API calls.
+
+This means that the new architecture places an additional responsibility
+on the client layer and/or platform support layer. As long as they have
+immediate work to do, they should call the appropriate mDNSCore routines
+to accomplish that work. With each call, mDNSCore will do only what it
+immediately has to do to satisfy the call. Any optional work will be
+deferred. As soon as there is no more immediate work to do, the calling
+layer MUST call mDNS_Execute(). Failure to call mDNS_Execute() will lead
+to unreliable or incorrect operation.
+
+The value returned from mDNS_Execute() is the next time (in absolute
+platform time units) at which mDNS_Execute() MUST be called again to
+perform its next necessary operation (e.g. transmitting its next
+scheduled query packet, etc.) Note that the time returned is an absolute
+time, not the time *interval* between now and the next required call.
+For OS APIs that work in terms of intervals instead of absolute times,
+mDNSPlatformTimeNow() must be subtracted from the absolute time to get
+the interval between now and the next event.
+
+In a single-threaded application using a blocking select() call as its
+main synchronization point, this means that you should call
+mDNS_Execute() before calling select(), and the timeout value you pass
+to select() MUST NOT be larger than that indicated by the result
+returned from mDNS_Execute(). After the blocking select() call returns,
+you should do whatever work you have to do, and then, if mDNS packets
+were received, or mDNS API calls were made, be sure to call
+mDNS_Execute() again, and if necessary adjust your timeout value
+accordingly, before going back into the select() call.
+
+In an asynchronous or interrupt-driven application, there are three
+places that should call mDNS_Execute():
+
+1. After delivering received packets, the platform support layer should
+call mDNS_Execute(), and use the value returned to set the platform
+callback timer to fire at the indicated time.
+
+2. After making any mDNS API call or series of calls, the client layer
+should call mDNS_Execute(), and use the value returned to set the
+platform callback timer to fire at the indicated time.
+
+3. When the platform callback timer fires, it should call mDNS_Execute()
+(to allow mDNSCore to perform its necessary work) and then the timer
+routine use the result returned to reset itself to fire at the right
+time for the next scheduled event.
index 8d4ec196818ed01f95b4a4713c925de3c011f73d..748da6a3f6764aede9797caa01ff42aa12a7d2da 100755 (executable)
@@ -1,18 +1,33 @@
-// ***************************************************************************
-// mDNS.c
-// This file defines all of mDNS, including
-// mDNS Service Discovery, mDNS Responder, and mDNS Searcher.
-//
-// This code is completely 100% portable C. It does not depend on any external header files
-// from outside the mDNS project -- all the types it expects to find are defined right here.
-//
-// The previous point is very important: This file does not depend on any external
-// header files. It should complile on *any* platform that has a C compiler, without
-// making *any* assumptions about availability of so-called "standard" C functions,
-// routines, or types (which may or may not be present on any given platform).
-// ***************************************************************************
-
 /*
+ * 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 code is completely 100% portable C. It does not depend on any external header files
+ * from outside the mDNS project -- all the types it expects to find are defined right here.
+ * 
+ * The previous point is very important: This file does not depend on any external
+ * header files. It should complile on *any* platform that has a C compiler, without
+ * making *any* assumptions about availability of so-called "standard" C functions,
+ * routines, or types (which may or may not be present on any given platform).
+
  * 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 <http://www.kafejo.com/komp/1tbs.htm>,
  * 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
+<rdar://problem/3411105> 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
+<rdar://problem/3413099> 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
+<rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep
+
+Revision 1.304  2003/09/09 02:41:19  cheshire
+<rdar://problem/3411105> Don't send a Goodbye record if we never announced it
+
+Revision 1.303  2003/09/05 19:55:02  cheshire
+<rdar://problem/3409533> Include address records when announcing SRV records
+
+Revision 1.302  2003/09/05 00:01:36  cheshire
+<rdar://problem/3407549> Don't accelerate queries that have large KA lists
+
+Revision 1.301  2003/09/04 22:51:13  cheshire
+<rdar://problem/3398213> Group probes and goodbyes better
+
+Revision 1.300  2003/09/03 02:40:37  cheshire
+<rdar://problem/3404842> 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
+<rdar://problem/3404795> CacheRecordRmv ERROR
+Don't update m->NewQuestions until *after* CheckCacheExpiration();
+
+Revision 1.298  2003/09/03 01:47:01  cheshire
+<rdar://problem/3319418> 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
+<rdar://problem/3400967> 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
+<rdar://problem/3400986> 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
+<rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst
+
+Revision 1.294  2003/08/27 02:30:22  cheshire
+<rdar://problem/3395909> 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
+<rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
+
+Revision 1.292  2003/08/21 19:27:36  cheshire
+<rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
+
+Revision 1.291  2003/08/21 18:57:44  cheshire
+<rdar://problem/3387140> 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
+<rdar://problem/3386473> Efficiency: Reduce repeated queries
+
+Revision 1.288  2003/08/20 23:39:30  cheshire
+<rdar://problem/3344098> 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
+<rdar://problem/3344098> Cleanup: Review syslog messages
+
+Revision 1.285  2003/08/20 01:59:06  cheshire
+<rdar://problem/3384478> 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
+<rdar://problem/3376721> 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
+<rdar://problem/3376552> 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
+<rdar://problem/3368159> 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
+<rdar://problem/3378386> 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
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.277  2003/08/18 19:05:44  cheshire
+<rdar://problem/3382423> UpdateRecord not working right
+Added "newrdlength" field to hold new length of updated rdata
+
+Revision 1.276  2003/08/16 03:39:00  cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.275  2003/08/16 02:51:27  cheshire
+<rdar://problem/3366590> 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
+<rdar://problem/3366590> 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
+<rdar://problem/3366590> 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
+<rdar://problem/3378473> 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
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.270  2003/08/13 17:07:28  ksekar
+Bug #: <rdar://problem/3376458>: 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
+<rdar://problem/3374490> 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
+<rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache
+
+Revision 1.264  2003/08/09 00:55:02  cheshire
+<rdar://problem/3366553> 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
+<rdar://problem/3370332> Remove "Cache size now xxx" messages
+
+Revision 1.261  2003/08/08 19:18:45  cheshire
+<rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
+
+Revision 1.260  2003/08/08 18:55:48  cheshire
+<rdar://problem/3370365> Guard against time going backwards
+
+Revision 1.259  2003/08/08 18:36:04  cheshire
+<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
+
+Revision 1.258  2003/08/08 16:22:05  cheshire
+<rdar://problem/3335473> Need to check validity of TXT (and other) records
+Remove unneeded LogMsg
+
+Revision 1.257  2003/08/07 01:41:08  cheshire
+<rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones)
+
+Revision 1.256  2003/08/06 23:25:51  cheshire
+<rdar://problem/3290674> 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
+<rdar://problem/3335473> 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
+<rdar://problem/3366251> 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
+<rdar://problem/3330324> Need to check IP TTL on responses
+
+Revision 1.247  2003/08/05 00:56:39  cheshire
+<rdar://problem/3357075> 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
+<rdar://problem/3340584> 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
+<rdar://problem/3329099> 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
+<rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
+
+Revision 1.237  2003/07/19 03:23:13  cheshire
+<rdar://problem/2986147> 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
+<rdar://problem/3160248> 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
+<rdar://problem/3268878> 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
+<rdar://problem/3319418> 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
+<rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
+
+Revision 1.228  2003/07/16 20:50:27  cheshire
+<rdar://problem/3315761> 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
+<rdar://problem/3315761> 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
+<rdar://problem/3328394> 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
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.218  2003/07/14 16:26:06  cheshire
+<rdar://problem/3324795> 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
+<rdar://problem/3325169> 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
+<rdar://problem/3325169> 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
+<rdar://problem/3325166> 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
+<rdar://problem/3324495> After name conflict, appended number should be higher than previous number
+
+Revision 1.210  2003/07/12 01:43:28  cheshire
+<rdar://problem/3324795> 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
+<rdar://problem/3320079> 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
+<rdar://problem/3161289> No more local.arpa
+
+Revision 1.206  2003/07/11 00:45:02  cheshire
+<rdar://problem/3321909> 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
+<rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
+
+Revision 1.203  2003/07/10 23:53:41  cheshire
+<rdar://problem/3320079> Hostname conflict naming should not use two hyphens
+
+Revision 1.202  2003/07/04 02:23:20  cheshire
+<rdar://problem/3311955> 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
+<rdar://problem/3315775> 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
+<rdar://problem/3315652>:      Lots of "have given xxx answers" syslog warnings
+Added more detailed debugging information
+
+Revision 1.198  2003/07/03 22:19:30  cheshire
+<rdar://problem/3314346> Bug fix in 3274153 breaks TiVo
+Make exception to allow _tivo_servemedia._tcp.
+
+Revision 1.197  2003/07/02 22:33:05  cheshire
+<rdar://problem/2986146> 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
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.195  2003/07/02 19:56:58  cheshire
+<rdar://problem/2986146> 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
+<rdar://problem/2986146> 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
+<rdar://problem/3304625> Merge of build failure fix for gcc 3.3
+
+Revision 1.189  2003/06/11 19:24:03  cheshire
+<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
+Slight refinement to previous checkin
+
+Revision 1.188  2003/06/10 20:33:28  cheshire
+<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
+
+Revision 1.187  2003/06/10 04:30:44  cheshire
+<rdar://problem/3286234> 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
+<rdar://problem/3283637> 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
+<rdar://problem/3283516> 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
+<rdar://problem/3285082> 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
+<rdar://problem/3283666> 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
+<rdar://problem/3283637> 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
+<rdar://problem/3283540> 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
+<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
+
+Revision 1.173  2003/06/07 01:22:13  cheshire
+<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
+
+Revision 1.172  2003/06/07 00:59:42  cheshire
+<rdar://problem/3283454> 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
+<rdar://problem/3282962> 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
+<rdar://problem/3274950> 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
+<rdar://problem/3277665> 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
+<rdar://problem/3277080> 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
+<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
+
+Revision 1.159  2003/06/03 03:31:57  cheshire
+<rdar://problem/3277033> 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
+<rdar://problem/3274862> Add ability to discover what services are on a network
+
+Revision 1.156  2003/05/30 23:56:49  cheshire
+<rdar://problem/3274847> Crash after error in mDNS_RegisterService()
+Need to set "sr->Extras = mDNSNULL" before returning
+
+Revision 1.155  2003/05/30 23:48:00  cheshire
+<rdar://problem/3274832> 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
+<rdar://problem/3274814> 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
+<rdar://problem/3274153> ConstructServiceName needs to be more restrictive
+
+Revision 1.152  2003/05/29 22:39:16  cheshire
+<rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character
+
+Revision 1.151  2003/05/29 06:35:42  cheshire
+<rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
+
+Revision 1.150  2003/05/29 06:25:45  cheshire
+<rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
+
+Revision 1.149  2003/05/29 06:18:39  cheshire
+<rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
+
+Revision 1.148  2003/05/29 06:11:34  cheshire
+<rdar://problem/3272214> 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
+<rdar://problem/3271550> 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
+<rdar://problem/3009899> 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
+<rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
+
+Revision 1.142  2003/05/28 03:13:07  cheshire
+<rdar://problem/3009899> mDNSResponder allows invalid service registrations
+Require that the transport protocol be _udp or _tcp
+
+Revision 1.141  2003/05/28 02:19:12  cheshire
+<rdar://problem/3270634> 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
+<rdar://problem/3270634> Misleading messages generated by iChat
+
+Revision 1.138  2003/05/27 22:35:00  cheshire
+<rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
+
+Revision 1.137  2003/05/27 20:04:33  cheshire
+<rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
+
+Revision 1.136  2003/05/27 18:50:07  cheshire
+<rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes
+
+Revision 1.135  2003/05/26 04:57:28  cheshire
+<rdar://problem/3268953> Delay queries when there are already answers in the cache
+
+Revision 1.134  2003/05/26 04:54:54  cheshire
+<rdar://problem/3268904> 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
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.131  2003/05/26 00:42:05  cheshire
+<rdar://problem/3268876> Temporarily include mDNSResponder version in packets
+
+Revision 1.130  2003/05/24 16:39:48  cheshire
+<rdar://problem/3268631> 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
+<rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness
+
+Revision 1.127  2003/05/23 01:02:15  ksekar
+Bug #: <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
+
+Revision 1.126  2003/05/22 02:29:22  cheshire
+<rdar://problem/2984918> 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
+<rdar://problem/3148431> 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 #: <rdar://problem/3148431> 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
+<rdar://problem/3162914> 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
+<rdar://problem/3159272> 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
+<rdar://problem/3159272> 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
+<rdar://problem/3027144> 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
+<rdar://problem/3250330> Also protect against NULL domainlabels
+
+Revision 1.113  2003/05/07 00:28:18  cheshire
+<rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
+
+Revision 1.112  2003/05/06 00:00:46  cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.111  2003/05/05 23:42:08  cheshire
+<rdar://problem/3245631> 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
+<rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names
+
+Revision 1.109  2003/04/26 02:41:56  cheshire
+<rdar://problem/3241281> Change timenow from a local variable to a structure member
+
+Revision 1.108  2003/04/25 01:45:56  cheshire
+<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
+
+Revision 1.107  2003/04/25 00:41:31  cheshire
+<rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
+
+Revision 1.106  2003/04/22 03:14:45  cheshire
+<rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
+
+Revision 1.105  2003/04/22 01:07:43  cheshire
+<rdar://problem/3176248> 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 #: <rdar://problem/3233804> Incorrect goodbye packet after conflict
+
+Revision 1.102  2003/04/17 03:06:28  cheshire
+Bug #: <rdar://problem/3231321> 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 #: <rdar://problem/3229064> 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
+<rdar://problem/3216837> Off-by-one error in probe rate limiting
+
+Revision 1.97  2003/04/02 01:48:17  cheshire
+<rdar://problem/3212360> 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
+<rdar://problem/3214832> 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
+<rdar://problem/3212360> 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
+<rdar://problem/3210018> 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
+<rdar://problem/3195426> 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[] = "<<NULL>>"; 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[] = "<<NULL>>"; 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), "<<INVALID LABEL LENGTH %u>>", *a); break; }
+                                                                                       if (s + *a >= &mDNS_VACB[254]) { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); 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), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", 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<i; j++) *sbuffer++ = *s++;                  // Write the converted result
+                       nwritten += i;
+                       if (nwritten >= 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; i<DupSuppressInfoSize; i++) if (ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
+       }
+
+mDNSlocal void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time, mDNSInterfaceID InterfaceID)
+       {
+       int i;
+       for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
+       }
+
+mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize], const NetworkInterfaceInfo * const intf)
+       {
+       int i;
+       mDNSBool v4 = !intf->IPv4Available;             // 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; i<DupSuppressInfoSize; i++)
+               if (ds[i].InterfaceID == intf->InterfaceID)
                        {
-                       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++) if (ds[i].InterfaceID == InterfaceID && ds[i].Type == Type) break;
+
+       // If not, find a slot we can re-use
+       if (i >= DupSuppressInfoSize)
+               {
+               i = 0;
+               for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++)
+                       if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0)
+                               i = j;
                }
+       
+       // Record the info about this query we saw
+       ds[i].Time        = Time;
+       ds[i].InterfaceID = InterfaceID;
+       ds[i].Type        = Type;
+       
+       return(i);
        }
 
-#define MaxQuestionInterval         (3600 * mDNSPlatformOneSecond)
-#define GetNextQInterval(X)         (((X)*2) <= MaxQuestionInterval ? ((X)*2) : MaxQuestionInterval)
-#define GetNextSendTime(T,EARLIEST) (((T) - (EARLIEST) >= 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; i<query->h.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; i<query->h.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; i<query->h.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; i<query->h.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; i<DupSuppressInfoSize; i++)
+                       question->DupSuppress[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; i<sr->NumSubTypes; 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; i<NumSubTypes; i++)
+               {
+               domainlabel s = *(domainlabel*)&sr->SubTypes[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; i<NumSubTypes; i++) if (!err) err = mDNS_Register_internal(m, &sr->SubTypes[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; i<sr->NumSubTypes; 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; i<numrecords; i++) storage[i].next = &storage[i+1];
+               storage[numrecords-1].next = m->rrcache_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; i<rrcachesize; i++) rrcachestorage[i].next = &rrcachestorage[i+1];
-               rrcachestorage[rrcachesize-1].next = mDNSNULL;
-               }
-       m->rrcache = 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");
index c6ba060cf18d665ccb97b1f9ace4efa8d2bb2f9b..975d5593c764aa5f53012038af4fa9ec6dfd39f5 100755 (executable)
@@ -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
+<rdar://problem/3400967> 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
+<rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
+
+Revision 1.111  2003/08/21 02:21:50  cheshire
+<rdar://problem/3386473> Efficiency: Reduce repeated queries
+
+Revision 1.110  2003/08/20 23:39:31  cheshire
+<rdar://problem/3344098> 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
+<rdar://problem/3376721> 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
+<rdar://problem/3376552> 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
+<rdar://problem/3368159> 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
+<rdar://problem/3378386> 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
+<rdar://problem/3382423> UpdateRecord not working right
+Added "newrdlength" field to hold new length of updated rdata
+
+Revision 1.102  2003/08/16 03:39:00  cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.101  2003/08/15 20:16:02  cheshire
+<rdar://problem/3366590> 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
+<rdar://problem/3378473> 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
+<rdar://problem/3375491> 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
+<rdar://problem/3374490> 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
+<rdar://problem/3323817> Improve cache performance
+Changed the number of hash table slots from 37 to 499
 
+Revision 1.95  2003/08/09 00:55:02  cheshire
+<rdar://problem/3366553> 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
+<rdar://problem/3370365> Guard against time going backwards
+
+Revision 1.92  2003/08/08 18:36:04  cheshire
+<rdar://problem/3344154> 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<b) and logical operators (a||b)
+
+Revision 1.87  2003/07/22 23:57:20  cheshire
+Move platform-layer function prototypes from mDNSClientAPI.h to mDNSPlatformFunctions.h where they belong
+
+Revision 1.86  2003/07/20 03:52:02  ksekar
+Bug #: <rdar://problem/3320722>: 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
+<rdar://problem/2986147> 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
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.82  2003/07/17 17:35:04  cheshire
+<rdar://problem/3325583> 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
+<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
+
+Revision 1.80  2003/07/15 01:55:12  cheshire
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.79  2003/07/13 02:28:00  cheshire
+<rdar://problem/3325166> 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
+<rdar://problem/3161289> No more local.arpa
+
+Revision 1.75  2003/07/02 21:19:45  cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.74  2003/07/02 02:41:23  cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+
+Revision 1.73  2003/06/10 04:24:39  cheshire
+<rdar://problem/3283637> 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
+<rdar://problem/3283666> No need for multiple machines to all be sending the same queries
+
+Revision 1.70  2003/06/07 04:50:53  cheshire
+<rdar://problem/3283637> 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
+<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
+
+Revision 1.67  2003/06/07 01:22:14  cheshire
+<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
+
+Revision 1.66  2003/06/07 00:59:43  cheshire
+<rdar://problem/3283454> 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
+<rdar://problem/3274950> 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
+<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
+
+Revision 1.60  2003/05/31 00:09:49  cheshire
+<rdar://problem/3274862> Add ability to discover what services are on a network
+
+Revision 1.59  2003/05/29 06:11:35  cheshire
+<rdar://problem/3272214>:      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
+<rdar://problem/3268904> 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
+<rdar://problem/3268631> 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
+<rdar://problem/2984918> 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
+<rdar://problem/3159272> 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
+<rdar://problem/3248914> 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
+<rdar://problem/3241281> Change timenow from a local variable to a structure member
+
+Revision 1.43  2003/04/25 01:45:56  cheshire
+<rdar://problem/3240002> 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
+<rdar://problem/3212360> 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
+<rdar://problem/3210018> 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
+<rdar://problem/3176950> 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>            // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration
 #include "mDNSDebug.h"
 
 #ifdef __cplusplus
 // 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
index f6e6e1e9aeae1c65a9eecdf2601faec53b98d0de..65b6daf479bc29e3a40571fb19fccd953f19bff8 100755 (executable)
-// 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
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.12  2003/05/26 03:01:27  cheshire
+<rdar://problem/3268904> 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 (executable)
index 1a349c7..0000000
+++ /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 <OpenTptInternet.h>
-#include <OpenTptClient.h>
-
-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 <SystemConfiguration/SystemConfiguration.h>
-#include <IOKit/pwr_mgt/IOPMLib.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-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
index e058894acaec311015580ece26e33890bc080e15..beef207bb3bb4216a4ffa42abbdd2bf7fd55e21b 100755 (executable)
@@ -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
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.21  2003/08/15 20:16:57  cheshire
+Update comment for <rdar://problem/3366590> 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
+<rdar://problem/3330324> 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
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.15  2003/05/23 22:39:45  cheshire
+<rdar://problem/3268151> 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 (executable)
index beaab51..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-#include <stdio.h>
-#include <stdarg.h>                                                    // 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<i; j++) *sbuffer++ = *s++;
-               nwritten += i;
-
-                       //  pad on the right
-
-               for (; i < F.fieldWidth; i++)
-                       { *sbuffer++ = ' '; ++nwritten; }
-               }
-
-done: return(nwritten);
-       }
-
-int mDNS_sprintf(char *sbuffer, const char *fmt, ...)
-{
-       int     length;
-       
-    va_list ptr;
-       va_start(ptr,fmt);
-       length = mDNS_vsprintf(sbuffer, fmt, ptr);
-       sbuffer[length] = 0;
-       va_end(ptr);
-       
-       return length;
-}
diff --git a/mDNSCore/mDNSsprintf.h b/mDNSCore/mDNSsprintf.h
deleted file mode 100755 (executable)
index 199048e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifdef __cplusplus
-       extern "C" {
-#endif
-
-extern int mDNS_sprintf(char *sbuffer, const char *fmt, ...);
-
-#ifdef __cplusplus
-       }
-#endif
diff --git a/mDNSCore/mDNSvsprintf.h b/mDNSCore/mDNSvsprintf.h
deleted file mode 100755 (executable)
index d6e9f05..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifdef __cplusplus
-       extern "C" {
-#endif
-
-extern int mDNS_vsprintf(char *sbuffer, const char *fmt, va_list arg);
-
-#ifdef __cplusplus
-       }
-#endif
diff --git a/mDNSMacOS9/CarbonResource.r b/mDNSMacOS9/CarbonResource.r
new file mode 100644 (file)
index 0000000..105a398
--- /dev/null
@@ -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: CarbonResource.r,v $
+Revision 1.5  2003/08/12 19:56:24  cheshire
+Update to APSL 2.0
+
+ */
+
+data 'carb' (0) { };
diff --git a/mDNSMacOS9/Mac OS Test Responder.c b/mDNSMacOS9/Mac OS Test Responder.c
new file mode 100644 (file)
index 0000000..7c55ca3
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * 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\040Responder.c,v $
+Revision 1.17  2003/08/14 02:19:54  cheshire
+<rdar://problem/3375491> 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 <stdio.h>                                             // For printf()
+#include <string.h>                    // For strlen() etc.
+
+#include <Events.h>                                            // For WaitNextEvent()
+#include <SIOUX.h>                                             // 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 (file)
index 0000000..d55dcd5
--- /dev/null
@@ -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
+<rdar://problem/3375491> 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 <stdio.h>                                             // For printf()
+#include <Events.h>                                            // For WaitNextEvent()
+#include <SIOUX.h>                                             // 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 (file)
index 0000000..4d13761
--- /dev/null
@@ -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 (file)
index 0000000..dc4932a
Binary files /dev/null and b/mDNSMacOS9/mDNS.mcp differ
diff --git a/mDNSMacOS9/mDNSMacOS9.c b/mDNSMacOS9/mDNSMacOS9.c
new file mode 100644 (file)
index 0000000..cdb49b3
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * 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.c,v $
+Revision 1.19  2003/08/18 23:09:20  cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.18  2003/08/12 19:56:24  cheshire
+Update to APSL 2.0
+
+ */
+
+#include <LowMem.h>                                            // For LMGetCurApName()
+#include <TextUtils.h>                                 // For smSystemScript
+#include <UnicodeConverter.h>                  // For ConvertFromPStringToUnicode()
+
+#include <stdio.h>
+#include <stdarg.h>                                            // 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 (executable)
index 0000000..d707f9f
--- /dev/null
@@ -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 <OpenTransport.h>
+#include <OpenTptInternet.h>
+#include <OpenTptClient.h>
+
+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 (file)
index 0000000..95f1a48
--- /dev/null
@@ -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 (file)
index 0000000..2c31a97
--- /dev/null
@@ -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 (file)
index 0000000..d275361
--- /dev/null
@@ -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 (executable)
index 0000000..f58c914
--- /dev/null
@@ -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 <Cocoa/Cocoa.h>
+#import <DNSServiceDiscovery/DNSServiceDiscovery.h>
+
+#include <netinet/in.h>
+
+@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 (executable)
index 0000000..1dbe2a4
--- /dev/null
@@ -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 (file)
index 0000000..1cb8edd
--- /dev/null
@@ -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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>DNS Service Browser</string>
+       <key>CFBundleIconFile</key>
+       <string></string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>0.1</string>
+       <key>NSMainNibFile</key>
+       <string>MainMenu</string>
+       <key>NSPrincipalClass</key>
+       <string>NSApplication</string>
+</dict>
+</plist>
+";
+                       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 (file)
index 0000000..08c0f0c
Binary files /dev/null and b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings differ
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 (file)
index 0000000..2668d5d
--- /dev/null
@@ -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 (file)
index 0000000..e31cf4c
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>IBDocumentLocation</key>
+       <string>14 102 356 240 0 0 1152 746 </string>
+       <key>IBEditorPositions</key>
+       <dict>
+               <key>29</key>
+               <string>22 474 271 44 0 0 1152 746 </string>
+       </dict>
+       <key>IBFramework Version</key>
+       <string>273.0</string>
+       <key>IBOpenObjects</key>
+       <array>
+               <integer>220</integer>
+               <integer>201</integer>
+       </array>
+       <key>IBSystem Version</key>
+       <string>6C35</string>
+</dict>
+</plist>
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 (file)
index 0000000..b330188
Binary files /dev/null and b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib differ
diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/main.m b/mDNSMacOSX/Applications/DNSServiceBrowser/main.m
new file mode 100644 (file)
index 0000000..c340509
--- /dev/null
@@ -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 <Cocoa/Cocoa.h>
+
+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 (file)
index 0000000..9487a76
--- /dev/null
@@ -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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
+<plist version=\"1.0\">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>DNS Service Registration</string>
+       <key>CFBundleIconFile</key>
+       <string></string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>0.1</string>
+       <key>NSMainNibFile</key>
+       <string>MainMenu</string>
+       <key>NSPrincipalClass</key>
+       <string>NSApplication</string>
+</dict>
+</plist>
+";
+                       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 (file)
index 0000000..2aadfdc
Binary files /dev/null and b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings differ
diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/classes.nib b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/classes.nib
new file mode 100644 (file)
index 0000000..46f466c
--- /dev/null
@@ -0,0 +1,30 @@
+{
+    IBClasses = (
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {
+                addNewService = id; 
+                registerService = id; 
+                removeSelected = id; 
+                unregisterService = id; 
+            }; 
+            CLASS = RegistrationController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {
+                domainColumn = NSTableColumn; 
+                nameColumn = NSTableColumn; 
+                portColumn = NSTableColumn; 
+                serviceDisplayTable = NSTableView; 
+                serviceDomainField = NSTextField; 
+                serviceNameField = NSTextField; 
+                servicePortField = NSTextField; 
+                serviceTextField = NSTextField; 
+                serviceTypeField = NSTextField; 
+                textColumn = NSTableColumn; 
+                typeColumn = NSTableColumn; 
+            }; 
+            SUPERCLASS = NSObject; 
+        }
+    ); 
+    IBVersion = 1; 
+}
\ No newline at end of file
diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/info.nib b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/info.nib
new file mode 100644 (file)
index 0000000..9d2eb74
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>IBDocumentLocation</key>
+       <string>32 111 356 240 0 0 1152 746 </string>
+       <key>IBEditorPositions</key>
+       <dict>
+               <key>29</key>
+               <string>103 609 252 44 0 0 1152 746 </string>
+       </dict>
+       <key>IBFramework Version</key>
+       <string>273.0</string>
+       <key>IBOpenObjects</key>
+       <array>
+               <integer>243</integer>
+               <integer>21</integer>
+       </array>
+       <key>IBSystem Version</key>
+       <string>6C30</string>
+</dict>
+</plist>
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 (file)
index 0000000..705e77d
Binary files /dev/null and b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib differ
diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.h b/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.h
new file mode 100644 (file)
index 0000000..ca53bf2
--- /dev/null
@@ -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: RegistrationController.h,v $
+Revision 1.5  2003/08/12 19:55:07  cheshire
+Update to APSL 2.0
+
+ */
+
+/* RegistrationController */
+
+#import <Cocoa/Cocoa.h>
+
+@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 (file)
index 0000000..6255179
--- /dev/null
@@ -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 <DNSServiceDiscovery/DNSServiceDiscovery.h>
+
+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 (file)
index 0000000..c340509
--- /dev/null
@@ -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 <Cocoa/Cocoa.h>
+
+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 (file)
index 0000000..7bb3e90
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+
+@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 (file)
index 0000000..4aae5ed
--- /dev/null
@@ -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 <AppKit/AppKit.h>
+
+#include <sys/types.h>
+#include "arpa/inet.h"
+#include <netinet/in.h>
+
+@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 (file)
index 0000000..7d654f8
--- /dev/null
@@ -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 (file)
index 0000000..6ad4471
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+#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 (file)
index 0000000..519a899
--- /dev/null
@@ -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
+<rdar://problem/3412328> Don't log "sendto failed" errors during the first two minutes of startup
+
+Revision 1.114  2003/08/27 02:55:13  cheshire
+<rdar://problem/3387910>:      Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down)
+
+Revision 1.113  2003/08/19 22:20:00  cheshire
+<rdar://problem/3376721> 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
+<rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
+
+Revision 1.111  2003/08/18 22:53:37  cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformTimeNow()
+
+Revision 1.110  2003/08/16 03:39:00  cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.109  2003/08/15 02:19:49  cheshire
+<rdar://problem/3375225> 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
+<rdar://problem/3375225> 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
+<rdar://problem/3370229> 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
+<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
+
+Revision 1.102  2003/08/06 00:14:52  cheshire
+<rdar://problem/3330324> 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
+<rdar://problem/3330324> Need to check IP TTL on responses
+
+Revision 1.100  2003/08/05 21:18:50  cheshire
+<rdar://problem/3363185> 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
+<rdar://problem/3294080> 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
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.95  2003/07/12 03:15:20  cheshire
+<rdar://problem/3324848> 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
+<rdar://problem/3287213> When select() and recvmgs() disagree, get more info from kernel about the socket state
+
+Revision 1.93  2003/07/03 00:09:14  cheshire
+<rdar://problem/3286004> 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
+<rdar://problem/3313413> 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
+<rdar://problem/3303118> 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
+<rdar://problem/3296061> 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
+<rdar://problem/3291162> 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
+<rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
+
+Revision 1.86  2003/05/28 02:41:52  cheshire
+<rdar://problem/3034346> 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
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.81  2003/05/24 02:06:42  cheshire
+<rdar://problem/3268480> 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
+<rdar://problem/3221880> if_indextoname consumes a lot of CPU
+Fix error in myIfIndexToName; was returning prematurely
+
+Revision 1.79  2003/05/23 23:07:44  cheshire
+<rdar://problem/3268199> Must not write to stderr when running as daemon
+
+Revision 1.78  2003/05/23 01:19:04  cheshire
+<rdar://problem/3267085> 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
+<rdar://problem/3264366> 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 #: <rdar://problem/3191277>:       mDNSResponder doesn't watch for IPv6 address changes
+
+Revision 1.72  2003/05/14 18:48:41  cheshire
+<rdar://problem/3159272> 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
+<rdar://problem/3159272> 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
+<rdar://problem/3248914> 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
+<rdar://problem/3241281> 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
+<rdar://problem/3210018> 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 <stdio.h>
+#include <unistd.h>                                    // For select() and close()
+#include <stdarg.h>                                    // For va_list support
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>                                // For IP_RECVTTL
+#ifndef IP_RECVTTL
+#define IP_RECVTTL 24  /* bool; receive reception TTL w/dgram */
+#endif
+
+#include <netinet/in_systm.h>          // For n_long, required by <netinet/ip.h> below
+#include <netinet/ip.h>                                // For IPTOS_LOWDELAY etc.
+#include <netinet6/in6_var.h>          // 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 <ifaddrs.h>
+#endif
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOMessage.h>
+#include <mach/mach_time.h>
+
+// ***************************************************************************
+// 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 <rdar://problem/3409090>)
+               // 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 (file)
index 0000000..e0cf87c
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 <sys/ioctl.h>
+#include <sys/sockio.h>
+#define ifaddrs ifa_info
+#ifndef        ifa_broadaddr
+#define        ifa_broadaddr   ifa_dstaddr     /* broadcast address interface */
+#endif
+#include <sys/cdefs.h>
+
+/* 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 <net/if.h> */
+#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 <net/if.h> */
+  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 (file)
index 0000000..b6d3a9a
--- /dev/null
@@ -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 <mach/mach_types.h>
+
+#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 (file)
index 0000000..942fb6b
--- /dev/null
@@ -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 <mach/std_types.defs>
+#include <mach/mach_types.defs>
+
+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 (file)
index 0000000..eb400b1
--- /dev/null
@@ -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 <mach/std_types.defs>
+#include <mach/mach_types.defs>
+
+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 (executable)
index 0000000..7838fc1
--- /dev/null
@@ -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 <dns_sd.h>
+#include <unistd.h>
+#include <DNSServiceDiscovery/DNSServiceDiscovery.h> // include Mach API to ensure no conflicts exist
+#include <CoreFoundation/CoreFoundation.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define BIND_8_COMPAT 1
+#include <nameser.h>
+// 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/mDNSMacOSX/SamplemDNSClient.c b/mDNSMacOSX/SamplemDNSClient.c
new file mode 100644 (file)
index 0000000..f905bd7
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * 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 <http://www.kafejo.com/komp/1tbs.htm>,
+ * 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: SamplemDNSClient.c,v $
+Revision 1.39  2003/08/18 19:05:45  cheshire
+<rdar://problem/3382423> 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
+<rdar://problem/3362184> 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
+<rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
+
+Revision 1.35  2003/07/11 01:57:18  cheshire
+Add checkin history header
+
+ */
+
+#include <libc.h>
+#define BIND_8_COMPAT
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
+
+//*************************************************************************************************************
+// Globals
+
+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[3] = "\002AA";
+static char bigNULL[4096];
+
+//*************************************************************************************************************
+// Supporting Utility Functions
+//
+// This code takes care of:
+// 1. Extracting the mach_port_t from the dns_service_discovery_ref
+// 2. Making a CFMachPortRef from it
+// 3. Making a CFRunLoopSourceRef from that
+// 4. Adding that source to the current RunLoop
+// 5. and passing the resulting messages back to DNSServiceDiscovery_handleReply() for processing
+//
+// Code that's not based around a CFRunLoop will need its own mechanism to receive Mach messages
+// from the mDNSResponder daemon and pass them to the DNSServiceDiscovery_handleReply() routine.
+// (There is no way to automate this, because it varies depending on the application's existing
+// event handling model.)
+
+static void MyHandleMachMessage(CFMachPortRef port, void *msg, CFIndex size, void *info)
+       {
+    (void)port;        // Unused
+    (void)size;        // Unused
+    (void)info;        // Unused
+       DNSServiceDiscovery_handleReply(msg);
+       }
+
+static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref client)
+    {
+       mach_port_t port = DNSServiceDiscoveryMachPort(client);
+    if (!port)
+        return(-1);
+    else
+        {
+        CFMachPortContext  context    = { 0, 0, NULL, NULL, NULL };
+        Boolean            shouldFreeInfo;
+        CFMachPortRef      cfMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault, port, MyHandleMachMessage, &context, &shouldFreeInfo);
+        CFRunLoopSourceRef rls        = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
+        CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+        CFRelease(rls);
+        return(0);
+        }
+    }
+
+//*************************************************************************************************************
+// 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")
+
+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");
+       }
+
+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");
+       }
+
+static void browse_reply(DNSServiceBrowserReplyResultType resultType,
+    const char *replyName, const char *replyType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context)
+       {
+       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)
+       {
+    (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
+               {
+        const char *src = txtRecord;
+        printtimestamp();
+
+        if (address->sa_family == AF_INET)
+            {
+            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 && 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);
+            }
+               printf("\n");
+               }
+       }
+
+static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info)
+       {
+       (void)timer;    // Parameter not used
+       (void)info;             // Parameter not used
+    
+    switch (operation)
+        {
+        case 'A':
+            {
+            switch (addtest)
+                {
+                case 0: printf("Adding Test HINFO record\n");
+                        record = DNSServiceRegistrationAddRecord(client, T_HINFO, sizeof(myhinfo9), &myhinfo9[0], 120);
+                        addtest = 1;
+                        break;
+                case 1: printf("Updating Test HINFO record\n");
+                        DNSServiceRegistrationUpdateRecord(client, record, sizeof(myhinfoX), &myhinfoX[0], 120);
+                        addtest = 2;
+                        break;
+                case 2: printf("Removing Test HINFO record\n");
+                        DNSServiceRegistrationRemoveRecord(client, record);
+                        addtest = 0;
+                        break;
+                }
+            }
+            break;
+
+        case 'U':
+            {
+            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, 1+updatetest[0], &updatetest[0], 120);
+            }
+            break;
+
+        case 'N':
+            {
+            printf("Adding big NULL record\n");
+            DNSServiceRegistrationAddRecord(client, T_NULL, sizeof(bigNULL), &bigNULL[0], 120);
+            CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
+            }
+            break;
+        }
+    }
+
+static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
+       {
+    (void)context; // Unused
+    printf("Got a reply from the server: ");
+    switch (errorCode)
+        {
+        case kDNSServiceDiscoveryNoError:      printf("Name now registered and active\n"); break;
+        case kDNSServiceDiscoveryNameConflict: printf("Name in use, please choose another\n"); exit(-1);
+        default:                               printf("Error %d\n", errorCode); return;
+        }
+
+    if (operation == 'A' || operation == 'U' || operation == 'N')
+        {
+        CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, 0, NULL, NULL, NULL };
+        CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault,
+            CFAbsoluteTimeGetCurrent() + 5.0, 5.0, 0, 1,       // Next fire time, periodic interval, flags, and order
+                                myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext);
+        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
+        }
+       }
+
+//*************************************************************************************************************
+// The main test function
+
+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");
+       if (operation == -1) goto Fail;
+
+    switch (operation)
+        {
+        case 'E':      printf("Looking for recommended registration domains:\n");
+                    client = DNSServiceDomainEnumerationCreate(1, regdom_reply, nil);
+                    break;
+
+        case 'F':      printf("Looking for recommended browsing domains:\n");
+                    client = DNSServiceDomainEnumerationCreate(0, browsedom_reply, nil);
+                    break;
+
+        case 'B':      if (argc < optind+1) goto Fail;
+                    dom = (argc < optind+2) ? "" : argv[optind+1];
+                    if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;      // We allow '.' on the command line as a synonym for empty string
+                    printf("Browsing for %s%s\n", argv[optind+0], dom);
+                    client = DNSServiceBrowserCreate(argv[optind+0], dom, browse_reply, nil);
+                    break;
+
+        case 'L':      if (argc < optind+2) goto Fail;
+                    dom = (argc < optind+3) ? "" : argv[optind+2];
+                    if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;      // We allow '.' on the command line as a synonym for empty string
+                    printf("Lookup %s.%s%s\n", argv[optind+0], argv[optind+1], dom);
+                    client = DNSServiceResolverResolve(argv[optind+0], argv[optind+1], dom, resolve_reply, nil);
+                    break;
+
+        case 'R':      if (argc < optind+4) goto Fail;
+                    {
+                    char *nam = argv[optind+0];
+                    char *typ = argv[optind+1];
+                    char *dom = argv[optind+2];
+                    uint16_t PortAsNumber = atoi(argv[optind+3]);
+                    Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } };
+                    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;
+                    }
+
+        case 'A':
+        case 'U':
+        case 'N':      {
+                    Opaque16 registerPort = { { 0x12, 0x34 } };
+                    static const char TXT[] = "First String\001Second String\001Third String";
+                    printf("Registering Service Test._testupdate._tcp.local.\n");
+                    client = DNSServiceRegistrationCreate("Test", "_testupdate._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
+                    break;
+                    }
+
+        case 'T':      {
+                    Opaque16 registerPort = { { 0x23, 0x45 } };
+                    char TXT[1000];
+                    unsigned int i;
+                    for (i=0; i<sizeof(TXT)-1; i++)
+                        if ((i & 0x1F) == 0x1F) TXT[i] = 1; else TXT[i] = 'A' + (i >> 5);
+                    TXT[i] = 0;
+                    printf("Registering Service Test._testlargetxt._tcp.local.\n");
+                    client = DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil);
+                    break;
+                    }
+
+        case 'M':      {
+                    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("", "_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;
+                    }
+
+        default: goto Exit;
+        }
+
+    if (!client) { fprintf(stderr, "DNSService call failed\n"); return (-1); }
+    if (AddDNSServiceClientToRunLoop(client) != 0) { fprintf(stderr, "AddDNSServiceClientToRunLoop failed\n"); return (-1); }
+    printf("Talking to DNS SD Daemon at Mach port %d\n", DNSServiceDiscoveryMachPort(client));
+       CFRunLoopRun();
+    
+    // Be sure to deallocate the dns_service_discovery_ref when you're finished
+    // Note: What other cleanup has to be done here?
+    // We should probably invalidate, remove and release our CFRunLoopSourceRef?
+    DNSServiceDiscoveryDeallocate(client);
+    
+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        <Type> <Domain>        (Browse for services instances)\n", argv[0]);
+       fprintf(stderr, "%s -L <Name> <Type> <Domain>           (Look up a service instance)\n", argv[0]);
+       fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (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 (file)
index 0000000..b6aa01e
--- /dev/null
@@ -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 <http://www.kafejo.com/komp/1tbs.htm>,
+ * 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
+<rdar://problem/3387941> Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog
+
+Revision 1.133  2003/08/20 23:39:31  cheshire
+<rdar://problem/3344098> 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
+<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
+
+Revision 1.130  2003/08/16 03:39:01  cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.129  2003/08/15 20:16:03  cheshire
+<rdar://problem/3366590> 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
+<rdar://problem/3378473> Include list of cache records in SIGINFO output
+
+Revision 1.127  2003/08/14 02:18:21  cheshire
+<rdar://problem/3375491> 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
+<rdar://problem/3344154> 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
+<rdar://problem/3339388> 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
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.119  2003/07/17 19:08:58  cheshire
+<rdar://problem/3332153> 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
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.116  2003/07/02 21:19:51  cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.115  2003/07/02 02:41:24  cheshire
+<rdar://problem/2986146> 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
+<rdar://problem/3221246> 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 #: <rdar://problem/3249292>: 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
+<rdar://problem/3287858> mDNSResponder binary compatibility
+Make single binary that can run on both Jaguar and Panther.
+
+Revision 1.110  2003/06/10 01:14:11  cheshire
+<rdar://problem/3286004> 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
+<rdar://problem/3262962> 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
+<rdar://problem/3268876> Temporarily include mDNSResponder version in packets
+
+Revision 1.103  2003/05/23 23:07:44  cheshire
+<rdar://problem/3268199> 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
+<rdar://problem/3239284> 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 #: <rdar://problem/3247035>: 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
+<rdar://problem/3262962> 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
+<rdar://problem/3250330> Forgot to set "err = mStatus_BadParamErr" in a couple of places
+
+Revision 1.96  2003/05/07 22:10:46  cheshire
+<rdar://problem/3250330> Add a few more error logging messages
+
+Revision 1.95  2003/05/07 19:20:17  cheshire
+<rdar://problem/3251391> Add version number to mDNSResponder builds
+
+Revision 1.94  2003/05/07 00:28:18  cheshire
+<rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
+
+Revision 1.93  2003/05/06 00:00:49  cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.92  2003/04/04 20:38:57  cheshire
+Add $Log header
+
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <servers/bootstrap.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <paths.h>
+#include <fcntl.h>
+
+#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 <DNSServiceDiscovery/DNSServiceDiscovery.h>
+
+#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; i<query->info->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<argc; i++)
+               {
+               if (!strcmp(argv[i], "-d")) debug_mode = 1;
+               }
+
+       signal(SIGINT,  HandleSIGTERM);         // SIGINT is what you get for a Ctrl-C
+       signal(SIGTERM, HandleSIGTERM);
+       signal(SIGINFO, HandleSIGINFO);
+
+       // Register the server with mach_init for automatic restart only during debug mode
+    if (!debug_mode)
+               registerBootstrapService();
+
+       if (!debug_mode && !restarting_via_mach_init)
+               exit(0); /* mach_init will restart us immediately as a daemon */
+
+       // Unlike deamon(), mach_init does redirect standard file descriptors to /dev/null
+       if (!debug_mode)
+               {
+               int fd = open(_PATH_DEVNULL, O_RDWR, 0);
+               if (fd != -1)
+                       {
+                       // Avoid to unnecessarily duplicate a file descriptor to itself
+                       if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO);
+                       if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO);
+                       if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO);
+                       if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) 
+                               (void)close (fd);
+                       }
+               }
+
+       fp = fopen(PID_FILE, "w");
+       if (fp != NULL)
+               {
+               fprintf(fp, "%d\n", getpid());
+               fclose(fp);
+               }
+       
+       LogMsg("%s starting", mDNSResponderVersionString);
+       status = mDNSDaemonInitialize();
+
+       if (status == 0)
+               {
+               int numevents = 0;
+               int RunLoopStatus = kCFRunLoopRunTimedOut;
+               
+               // This is the main work loop:
+               // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time
+               // (2) Then we make sure we've delivered all waiting browse messages to our clients
+               // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner
+               // (4) On wakeup we first process *all* events
+               // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again
+               while (RunLoopStatus == kCFRunLoopRunTimedOut)
+                       {
+                       // 1. Before going into a blocking wait call and letting our process to go sleep,
+                       // call mDNSDaemonIdle to allow any deferred work to be completed.
+                       mDNSs32 nextevent = mDNSDaemonIdle();
+#if ENABLE_UDS
+                       nextevent = udsserver_idle(nextevent);
+#endif
+
+                       // 2. Work out how long we expect to sleep before the next scheduled task
+                       mDNSs32 ticks = nextevent - mDNSPlatformTimeNow();
+                       if (ticks < 1) ticks = 1;
+                       CFAbsoluteTime interval = (CFAbsoluteTime)ticks / (CFAbsoluteTime)mDNSPlatformOneSecond;
+                                       
+                       // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until
+                       // (a) our next wakeup time, or (b) an event occurs.
+                       // The 'true' parameter makes it return after handling any event that occurs
+                       // This gives us chance to regain control so we can call mDNS_Execute() before sleeping again
+                       verbosedebugf("main: Handled %d events; now sleeping for %d ticks", numevents, ticks);
+                       numevents = 0;
+                       RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, interval, true);
+
+                       // 4. Time to do some work? Handle all remaining events as quickly as we can, before returning to mDNSDaemonIdle()
+                       while (RunLoopStatus == kCFRunLoopRunHandledSource)
+                               {
+                               numevents++;
+                               RunLoopStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
+                               }
+                       }
+
+               LogMsg("ERROR: CFRunLoopRun Exiting.");
+               mDNS_Close(&mDNSStorage);
+               }
+
+       destroyBootstrapService();
+
+       return(status);
+       }
+
+// For convenience when using the "strings" command, this is the last thing in the file
+#if mDNSResponderVersion > 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 (executable)
index 0000000..eaed335
--- /dev/null
@@ -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 <sys/types.h>
+#include <sys/socket.h>
+#include <stdint.h>
+#include <netinet/in.h>
+
+
+/* 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. <length byte> <data> 
+ *                  <length byte> <data> ...
+ *
+ * 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 <servicename>.<protocol>.<domain>.
+ *                  (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 (executable)
index 0000000..7277eda
--- /dev/null
@@ -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 (file)
index 0000000..186f4d3
--- /dev/null
@@ -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 (file)
index 0000000..2b7b323
--- /dev/null
@@ -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 <sys/types.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+//#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 (file)
index 0000000..b255cd0
--- /dev/null
@@ -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
+<rdar://problem/3376721> 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
+<rdar://problem/3380097> 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
+<rdar://problem/3376721> 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
+<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
+
+Revision 1.15  2003/08/05 00:32:28  cheshire
+<rdar://problem/3326712> 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
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.12  2003/07/12 03:15:20  cheshire
+<rdar://problem/3324848> 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
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.10  2003/06/25 23:42:19  ksekar
+Bug #: <rdar://problem/3249292>: 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
+<rdar://problem/3286004> New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call
+
+Revision 1.8  2003/05/14 07:08:37  cheshire
+<rdar://problem/3159272> 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 <SystemConfiguration/SystemConfiguration.h>
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+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/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj
new file mode 100644 (file)
index 0000000..2bd8d35
--- /dev/null
@@ -0,0 +1,808 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 38;
+       objects = {
+               000753D303367C1C0CCA2C71 = {
+                       isa = PBXFileReference;
+                       path = mDNSMacOSX.h;
+                       refType = 4;
+               };
+               0017390704CC75C30CCA2C71 = {
+                       isa = PBXFileReference;
+                       path = SampleUDSClient.c;
+                       refType = 2;
+               };
+               0017390804CC75C30CCA2C71 = {
+                       fileRef = 0017390704CC75C30CCA2C71;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               0044D34804CC73600CCA2C71 = {
+                       buildPhases = (
+                               0044D34904CC73600CCA2C71,
+                               0044D34A04CC73600CCA2C71,
+                               0044D34C04CC73600CCA2C71,
+                               0044D34E04CC73600CCA2C71,
+                       );
+                       buildSettings = {
+                               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 = "-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";
+                       };
+                       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;
+               };
+               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 = {
+                               MVERS = 1;
+                       };
+                       isa = PBXBuildStyle;
+                       name = Development;
+               };
+               00CA213D02786FC30CCA2C71 = {
+                       isa = PBXFrameworkReference;
+                       name = IOKit.framework;
+                       path = /System/Library/Frameworks/IOKit.framework;
+                       refType = 0;
+               };
+               00DD152B04CC79700CCA2C71 = {
+                       fileRef = 004EFB9604CC78130CCA2C71;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               00DD152C04CC79A50CCA2C71 = {
+                       fileRef = F5E11B5A04A28126019798ED;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+//000
+//001
+//002
+//003
+//004
+//030
+//031
+//032
+//033
+//034
+               034768E2FF38A6DC11DB9C8B = {
+                       isa = PBXExecutableFileReference;
+                       path = mDNSResponder;
+                       refType = 3;
+               };
+//030
+//031
+//032
+//033
+//034
+//080
+//081
+//082
+//083
+//084
+               08FB7793FE84155DC02AAC07 = {
+                       buildStyles = (
+                               00B2AB0C032D7B220CCA2C71,
+                       );
+                       isa = PBXProject;
+                       mainGroup = 08FB7794FE84155DC02AAC07;
+                       projectDirPath = "";
+                       targets = (
+                               00AD62BB032D7A0C0CCA2C71,
+                               08FB779FFE84155DC02AAC07,
+                               00AD62A3032D799A0CCA2C71,
+                               6575FC1C022EB76000000109,
+                               0044D34804CC73600CCA2C71,
+                       );
+               };
+               08FB7794FE84155DC02AAC07 = {
+                       children = (
+                               08FB7795FE84155DC02AAC07,
+                               6575FC1F022EB78C00000109,
+                               6575FBFE022EAFA800000109,
+                               08FB779DFE84155DC02AAC07,
+                               19C28FBDFE9D53C911CA2CBB,
+                       );
+                       isa = PBXGroup;
+                       name = mDNSResponder;
+                       refType = 4;
+               };
+               08FB7795FE84155DC02AAC07 = {
+                       children = (
+                               F525E72804AA167501F1CF4D,
+                               F5E11B5A04A28126019798ED,
+                               F5E11B5B04A28126019798ED,
+                               6575FBEC022EAF7200000109,
+                               6575FBE9022EAF5A00000109,
+                               6575FBEB022EAF7200000109,
+                               654BE64F02B63B93000001D1,
+                               654BE65002B63B93000001D1,
+                               654BE65202B63B93000001D1,
+                               000753D303367C1C0CCA2C71,
+                       );
+                       isa = PBXGroup;
+                       name = "mDNS Server Sources";
+                       path = "";
+                       refType = 4;
+               };
+               08FB779DFE84155DC02AAC07 = {
+                       children = (
+                               09AB6884FE841BABC02AAC07,
+                               65713D46025A293200000109,
+                               00CA213D02786FC30CCA2C71,
+                       );
+                       isa = PBXGroup;
+                       name = "External Frameworks and Libraries";
+                       refType = 4;
+               };
+               08FB779FFE84155DC02AAC07 = {
+                       buildPhases = (
+                               08FB77A0FE84155DC02AAC07,
+                               08FB77A1FE84155DC02AAC07,
+                               08FB77A3FE84155DC02AAC07,
+                               08FB77A5FE84155DC02AAC07,
+                       );
+                       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 = "-no-cpp-precomp -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)";
+                               OTHER_LDFLAGS = "";
+                               OTHER_REZFLAGS = "";
+                               PRODUCT_NAME = mDNSResponder;
+                               REZ_EXECUTABLE = YES;
+                               SECTORDER_FLAGS = "";
+                               STRIPFLAGS = "-S";
+                               WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas";
+                       };
+                       dependencies = (
+                       );
+                       isa = PBXToolTarget;
+                       name = mDNSResponder;
+                       productInstallPath = "$(HOME)/bin";
+                       productName = mDNSResponder;
+                       productReference = 034768E2FF38A6DC11DB9C8B;
+                       shouldUseHeadermap = 1;
+               };
+               08FB77A0FE84155DC02AAC07 = {
+                       buildActionMask = 2147483647;
+                       files = (
+                               6575FC02022EAFBA00000109,
+                               F5E11B5D04A28126019798ED,
+                       );
+                       isa = PBXHeadersBuildPhase;
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               08FB77A1FE84155DC02AAC07 = {
+                       buildActionMask = 2147483647;
+                       files = (
+                               6575FC0D022EB18700000109,
+                               6575FC0E022EB18700000109,
+                               6575FBEA022EAF5A00000109,
+                               6575FBED022EAF7200000109,
+                               6575FBEE022EAF7200000109,
+                               F5E11B5C04A28126019798ED,
+                               F525E72904AA167501F1CF4D,
+                       );
+                       isa = PBXSourcesBuildPhase;
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               08FB77A3FE84155DC02AAC07 = {
+                       buildActionMask = 2147483647;
+                       files = (
+                               09AB6885FE841BABC02AAC07,
+                               65713D66025A293200000109,
+                               6585DD640279A3B7000001D1,
+                       );
+                       isa = PBXFrameworksBuildPhase;
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               08FB77A5FE84155DC02AAC07 = {
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       isa = PBXRezBuildPhase;
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+//080
+//081
+//082
+//083
+//084
+//090
+//091
+//092
+//093
+//094
+               09AB6884FE841BABC02AAC07 = {
+                       isa = PBXFrameworkReference;
+                       name = CoreFoundation.framework;
+                       path = /System/Library/Frameworks/CoreFoundation.framework;
+                       refType = 0;
+               };
+               09AB6885FE841BABC02AAC07 = {
+                       fileRef = 09AB6884FE841BABC02AAC07;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+//090
+//091
+//092
+//093
+//094
+//190
+//191
+//192
+//193
+//194
+               19C28FBDFE9D53C911CA2CBB = {
+                       children = (
+                               034768E2FF38A6DC11DB9C8B,
+                               6575FC1D022EB76000000109,
+                               00AD62B8032D799A0CCA2C71,
+                               0044D34F04CC73600CCA2C71,
+                       );
+                       isa = PBXGroup;
+                       name = Products;
+                       refType = 4;
+               };
+//190
+//191
+//192
+//193
+//194
+//650
+//651
+//652
+//653
+//654
+               654BE64F02B63B93000001D1 = {
+                       isa = PBXFileReference;
+                       name = mDNSClientAPI.h;
+                       path = ../mDNSCore/mDNSClientAPI.h;
+                       refType = 4;
+               };
+               654BE65002B63B93000001D1 = {
+                       isa = PBXFileReference;
+                       name = mDNSDebug.h;
+                       path = ../mDNSCore/mDNSDebug.h;
+                       refType = 4;
+               };
+               654BE65202B63B93000001D1 = {
+                       isa = PBXFileReference;
+                       name = mDNSPlatformFunctions.h;
+                       path = ../mDNSCore/mDNSPlatformFunctions.h;
+                       refType = 4;
+               };
+               65713D46025A293200000109 = {
+                       isa = PBXFrameworkReference;
+                       name = SystemConfiguration.framework;
+                       path = /System/Library/Frameworks/SystemConfiguration.framework;
+                       refType = 0;
+               };
+               65713D66025A293200000109 = {
+                       fileRef = 65713D46025A293200000109;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               6575FBE9022EAF5A00000109 = {
+                       indentWidth = 4;
+                       isa = PBXFileReference;
+                       name = mDNS.c;
+                       path = ../mDNSCore/mDNS.c;
+                       refType = 4;
+                       tabWidth = 4;
+                       usesTabs = 1;
+               };
+               6575FBEA022EAF5A00000109 = {
+                       fileRef = 6575FBE9022EAF5A00000109;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               6575FBEB022EAF7200000109 = {
+                       indentWidth = 4;
+                       isa = PBXFileReference;
+                       path = CFSocket.c;
+                       refType = 4;
+                       tabWidth = 4;
+                       usesTabs = 1;
+               };
+               6575FBEC022EAF7200000109 = {
+                       indentWidth = 4;
+                       isa = PBXFileReference;
+                       path = daemon.c;
+                       refType = 4;
+                       tabWidth = 4;
+                       usesTabs = 1;
+               };
+               6575FBED022EAF7200000109 = {
+                       fileRef = 6575FBEB022EAF7200000109;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               6575FBEE022EAF7200000109 = {
+                       fileRef = 6575FBEC022EAF7200000109;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               6575FBFE022EAFA800000109 = {
+                       children = (
+                               6575FBFF022EAFBA00000109,
+                               6575FC00022EAFBA00000109,
+                               6575FC01022EAFBA00000109,
+                       );
+                       isa = PBXGroup;
+                       name = "DNS Service Discovery MIG files";
+                       refType = 4;
+               };
+               6575FBFF022EAFBA00000109 = {
+                       isa = PBXFileReference;
+                       path = DNSServiceDiscoveryDefines.h;
+                       refType = 4;
+               };
+               6575FC00022EAFBA00000109 = {
+                       isa = PBXFileReference;
+                       path = DNSServiceDiscoveryReply.defs;
+                       refType = 4;
+               };
+               6575FC01022EAFBA00000109 = {
+                       isa = PBXFileReference;
+                       path = DNSServiceDiscoveryRequest.defs;
+                       refType = 4;
+               };
+               6575FC02022EAFBA00000109 = {
+                       fileRef = 6575FBFF022EAFBA00000109;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               6575FC0D022EB18700000109 = {
+                       fileRef = 6575FC00022EAFBA00000109;
+                       isa = PBXBuildFile;
+                       settings = {
+                               ATTRIBUTES = (
+                                       Client,
+                               );
+                       };
+               };
+               6575FC0E022EB18700000109 = {
+                       fileRef = 6575FC01022EAFBA00000109;
+                       isa = PBXBuildFile;
+                       settings = {
+                               ATTRIBUTES = (
+                                       Server,
+                                       Client,
+                               );
+                       };
+               };
+               6575FC18022EB76000000109 = {
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       isa = PBXHeadersBuildPhase;
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6575FC19022EB76000000109 = {
+                       buildActionMask = 2147483647;
+                       files = (
+                               6575FC21022EB7AA00000109,
+                       );
+                       isa = PBXSourcesBuildPhase;
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6575FC1A022EB76000000109 = {
+                       buildActionMask = 2147483647;
+                       files = (
+                               6575FC24022EBA5D00000109,
+                       );
+                       isa = PBXFrameworksBuildPhase;
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6575FC1B022EB76000000109 = {
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       isa = PBXRezBuildPhase;
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               6575FC1C022EB76000000109 = {
+                       buildPhases = (
+                               6575FC18022EB76000000109,
+                               6575FC19022EB76000000109,
+                               6575FC1A022EB76000000109,
+                               6575FC1B022EB76000000109,
+                       );
+                       buildSettings = {
+                               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 = "";
+                               STRIPFLAGS = "-S";
+                               WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas";
+                       };
+                       dependencies = (
+                       );
+                       isa = PBXToolTarget;
+                       name = "mDNS Command-Line tool";
+                       productInstallPath = /usr/bin;
+                       productName = "Sample mDNS Client";
+                       productReference = 6575FC1D022EB76000000109;
+                       shouldUseHeadermap = 0;
+               };
+               6575FC1D022EB76000000109 = {
+                       isa = PBXExecutableFileReference;
+                       path = mDNS;
+                       refType = 3;
+               };
+               6575FC1F022EB78C00000109 = {
+                       children = (
+                               6575FC20022EB7AA00000109,
+                               0017390704CC75C30CCA2C71,
+                               004EFB9604CC78130CCA2C71,
+                       );
+                       isa = PBXGroup;
+                       name = SampleMulticastDNSClient;
+                       refType = 4;
+               };
+               6575FC20022EB7AA00000109 = {
+                       indentWidth = 4;
+                       isa = PBXFileReference;
+                       path = SamplemDNSClient.c;
+                       refType = 4;
+                       tabWidth = 4;
+                       usesTabs = 0;
+               };
+               6575FC21022EB7AA00000109 = {
+                       fileRef = 6575FC20022EB7AA00000109;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               6575FC24022EBA5D00000109 = {
+                       fileRef = 09AB6884FE841BABC02AAC07;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               6585DD640279A3B7000001D1 = {
+                       fileRef = 00CA213D02786FC30CCA2C71;
+                       isa = PBXBuildFile;
+                       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 (file)
index 0000000..c7df012
--- /dev/null
@@ -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 #: <rdar://problem/3380097>: 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
+<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
+
+Revision 1.20  2003/08/16 03:39:01  cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.19  2003/08/15 20:16:03  cheshire
+<rdar://problem/3366590> 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 #: <rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client
+
+Revision 1.17  2003/08/14 02:18:21  cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.16  2003/08/13 23:58:52  ksekar
+Bug #: <rdar://problem/3374911>: 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 #: <rdar://problem/3374671>: 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 <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+// 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 (executable)
index 0000000..f3f9801
--- /dev/null
@@ -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
+<rdar://problem/3375491> 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
+<rdar://problem/3313413> 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
+<rdar://problem/3248914> 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 <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#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 (file)
index 0000000..a0531bc
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 <stdio.h>                     // For printf()
+#include <stdlib.h>                    // For exit() etc.
+#include <string.h>                    // For strlen() etc.
+#include <unistd.h>                    // For select()
+#include <errno.h>                     // For errno, EINTR
+#include <arpa/inet.h>         // For inet_addr()
+#include <netinet/in.h>                // For INADDR_NONE
+#include <netdb.h>                     // For gethostbyname()
+#include <signal.h>                    // 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 (file)
index 0000000..4274a46
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 (file)
index 0000000..228f0a6
--- /dev/null
@@ -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 <http://www.kafejo.com/komp/1tbs.htm>,
+ * 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 <signal.h> 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
+<rdar://problem/3375491> 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 <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>          // For n_long, required by <netinet/ip.h> below
+#include <netinet/ip.h>                                // For IPTOS_LOWDELAY etc.
+#include <arpa/inet.h>
+#include <signal.h>
+
+#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 <dot-local hostname> or <IPv4 address> or <IPv6 address>\n", argv[0]);
+       return(-1);
+       }
diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile
new file mode 100755 (executable)
index 0000000..1801a93
--- /dev/null
@@ -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 #: <rdar://problem/3218120>: 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 #: <rdar://problem/3218120>: 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
+# <http://www.gnu.org/manual/make-3.80/html_chapter/make_10.html#SEC111>
+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 (file)
index 0000000..b495d9a
--- /dev/null
@@ -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 <http://www.kafejo.com/komp/1tbs.htm>,
+ * 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
+<rdar://problem/3375491> 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
+<rdar://problem/3315761> 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
+<rdar://problem/3315761> 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
+<rdar://problem/3313413> 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 <time.h> 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
+<rdar://problem/3268904> 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
+<rdar://problem/3241281> 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 <stdio.h>                     // For printf()
+#include <stdlib.h>                    // For malloc()
+#include <string.h>                    // For bcopy()
+#include <time.h>                      // For "struct tm" etc.
+#include <netdb.h>                     // For gethostbyname()
+#include <sys/socket.h>                // For AF_INET, AF_INET6, etc.
+#include <arpa/inet.h>         // For inet_addr()
+#include <netinet/in.h>                // 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; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
+                       entry->totalops = 0;
+                       for (i=0; i<OP_NumTypes;      i++) entry->stat[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; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
+               }
+
+       (*s)->totalops++;
+       (*s)->stat[op]++;
+       if (entry)
+               {
+               entry->totalops++;
+               entry->stat[op]++;
+               }
+       }
+
+mDNSlocal void printstats(int max)
+       {
+       int i;
+       for (i=0; i<max; i++)
+               {
+               int max = 0;
+               ActivityStat *s, *m = NULL;
+               for (s = stats; s; s=s->next)
+                       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; i<msg->h.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; i<msg->h.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; i<msg->h.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; i<msg->h.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; i<msg->h.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; i<msg->h.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; i<msg->h.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; i<argc; i++)
+               {
+               struct in_addr s4;
+               struct in6_addr s6;
+               mDNSAddr a;
+               a.type = mDNSAddrType_IPv4;
+
+               if (inet_pton(AF_INET, argv[i], &s4) == 1)
+                       a.ip.v4.NotAnInteger = s4.s_addr;
+               else if (inet_pton(AF_INET6, argv[i], &s6) == 1)
+                       {
+                       a.type = mDNSAddrType_IPv6;
+                       bcopy(&s6, &a.ip.v6, sizeof(a.ip.v6));
+                       }
+               else
+                       {
+                       struct hostent *h = gethostbyname(argv[i]);
+                       if (h) a.ip.v4.NotAnInteger = *(long*)h->h_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 (<host>)\n", argv[0]);
+       fprintf(stderr, "Optional <host> 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 (file)
index 0000000..3bf4f11
--- /dev/null
@@ -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
+<rdar://problem/3375491> 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
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.18  2003/07/02 21:19:58  cheshire
+<rdar://problem/3313413> 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
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.15  2003/05/06 00:00:50  cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.14  2003/04/25 01:45:57  cheshire
+<rdar://problem/3240002> 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 <stdio.h>                     // For printf()
+#include <stdlib.h>                    // For exit() etc.
+#include <string.h>                    // For strlen() etc.
+#include <unistd.h>                    // For select()
+#include <errno.h>                     // For errno, EINTR
+#include <arpa/inet.h>         // For inet_addr()
+#include <netinet/in.h>                // For INADDR_NONE
+#include <netdb.h>                     // 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 (executable)
index 0000000..fc00dbd
--- /dev/null
@@ -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.
+
+  <http://www.multicastdns.org/>
+
+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.
+
+  <http://www.zeroconf.org/>
+
+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.
+
+  <http://www.kohala.com/start/unpv12e.html>
+
+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 <dts@apple.com> 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 (executable)
index 0000000..6130e0c
--- /dev/null
@@ -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
+<rdar://problem/3375491> 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
+<rdar://problem/3315777> 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
+<rdar://problem/3318717> mDNSResponder Posix version is missing a 'b' in the getopt option string
+
+Revision 1.9  2003/07/02 21:19:59  cheshire
+<rdar://problem/3313413> 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
+<rdar://problem/3248914> 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 <assert.h>
+#include <stdio.h>                     // For printf()
+#include <stdlib.h>                    // For exit() etc.
+#include <string.h>                    // For strlen() etc.
+#include <unistd.h>                    // For select()
+#include <errno.h>                     // For errno, EINTR
+#include <signal.h>
+#include <fcntl.h>
+
+#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 <pid>
+    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 <pid>
+
+       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 (executable)
index 0000000..a7b73da
--- /dev/null
@@ -0,0 +1,14 @@
+Tweedlebug
+_afpovertcp._tcp.
+name=val1
+548
+
+Tweedlebug2
+_afpovertcp._tcp. local.
+name=val2\ 1name2=anotherattribute
+548
+
+Tweedlebug3
+_afpovertcp._tcp. apple.com.
+name=val3
+548
diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
new file mode 100755 (executable)
index 0000000..345b2f4
--- /dev/null
@@ -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 <http://www.kafejo.com/komp/1tbs.htm>,
+ * 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
+<rdar://problem/3382647> 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
+<rdar://problem/3313413> 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
+<rdar://problem/3268904> 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
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.10  2003/04/25 01:45:57  cheshire
+<rdar://problem/3240002> 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+
+#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 (executable)
index 0000000..53f7fe9
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 <sys/time.h>
+
+#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 (executable)
index 0000000..fa9bda0
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* Solaris defined SIOCGIFCONF etc in <sys/sockio.h> but 
+   other platforms don't even have that include file.  So, 
+   if we haven't yet got a definition, let's try to find 
+   <sys/sockio.h>.
+*/
+
+#ifndef SIOCGIFCONF
+    #include <sys/sockio.h>
+#endif
+
+/* sockaddr_dl is only referenced if we're using IP_RECVIF, 
+   so only include the header in that case.
+*/
+
+#ifdef  IP_RECVIF
+    #include <net/if_dl.h>
+#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 (executable)
index 0000000..21d13f1
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#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 <net/if.h> */
+#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 <net/if.h> */
+  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/mDNSResponder.pbproj/project.pbxproj b/mDNSResponder.pbproj/project.pbxproj
deleted file mode 100644 (file)
index 7fc8cc5..0000000
+++ /dev/null
@@ -1,531 +0,0 @@
-// !$*UTF8*$!
-{
-       archiveVersion = 1;
-       classes = {
-       };
-       objectVersion = 38;
-       objects = {
-               00CA213D02786FC30CCA2C71 = {
-                       isa = PBXFrameworkReference;
-                       name = IOKit.framework;
-                       path = /System/Library/Frameworks/IOKit.framework;
-                       refType = 0;
-               };
-//000
-//001
-//002
-//003
-//004
-//010
-//011
-//012
-//013
-//014
-               014CEA490018CE3211CA2923 = {
-                       buildRules = (
-                       );
-                       buildSettings = {
-                               COPY_PHASE_STRIP = NO;
-                               OPTIMIZATION_CFLAGS = "-O0";
-                               OTHER_CFLAGS = "-D__MACOSX__ -DDEBUGBREAKS=1";
-                       };
-                       isa = PBXBuildStyle;
-                       name = Development;
-               };
-               014CEA4A0018CE3211CA2923 = {
-                       buildRules = (
-                       );
-                       buildSettings = {
-                               COPY_PHASE_STRIP = YES;
-                               OTHER_CFLAGS = "-D__MACOSX__";
-                       };
-                       isa = PBXBuildStyle;
-                       name = Deployment;
-               };
-//010
-//011
-//012
-//013
-//014
-//030
-//031
-//032
-//033
-//034
-               034768E2FF38A6DC11DB9C8B = {
-                       isa = PBXExecutableFileReference;
-                       path = mDNSResponder;
-                       refType = 3;
-               };
-//030
-//031
-//032
-//033
-//034
-//080
-//081
-//082
-//083
-//084
-               08FB7793FE84155DC02AAC07 = {
-                       buildStyles = (
-                               014CEA490018CE3211CA2923,
-                               014CEA4A0018CE3211CA2923,
-                       );
-                       isa = PBXProject;
-                       mainGroup = 08FB7794FE84155DC02AAC07;
-                       projectDirPath = "";
-                       targets = (
-                               08FB779FFE84155DC02AAC07,
-                               6575FC1C022EB76000000109,
-                       );
-               };
-               08FB7794FE84155DC02AAC07 = {
-                       children = (
-                               08FB7795FE84155DC02AAC07,
-                               6575FC1F022EB78C00000109,
-                               6575FBFE022EAFA800000109,
-                               08FB779DFE84155DC02AAC07,
-                               19C28FBDFE9D53C911CA2CBB,
-                       );
-                       isa = PBXGroup;
-                       name = mDNSResponder;
-                       refType = 4;
-               };
-               08FB7795FE84155DC02AAC07 = {
-                       children = (
-                               6575FBEC022EAF7200000109,
-                               6575FBE9022EAF5A00000109,
-                               6575FC12022EB27800000109,
-                               6575FBEB022EAF7200000109,
-                               654BE64F02B63B93000001D1,
-                               654BE65002B63B93000001D1,
-                               654BE65102B63B93000001D1,
-                               654BE65202B63B93000001D1,
-                               654BE65302B63B93000001D1,
-                               654BE65402B63B93000001D1,
-                       );
-                       isa = PBXGroup;
-                       name = "mDNS Server Sources";
-                       refType = 4;
-               };
-               08FB779DFE84155DC02AAC07 = {
-                       children = (
-                               09AB6884FE841BABC02AAC07,
-                               65713D46025A293200000109,
-                               00CA213D02786FC30CCA2C71,
-                       );
-                       isa = PBXGroup;
-                       name = "External Frameworks and Libraries";
-                       refType = 4;
-               };
-               08FB779FFE84155DC02AAC07 = {
-                       buildPhases = (
-                               08FB77A0FE84155DC02AAC07,
-                               08FB77A1FE84155DC02AAC07,
-                               08FB77A3FE84155DC02AAC07,
-                               08FB77A5FE84155DC02AAC07,
-                       );
-                       buildSettings = {
-                               FRAMEWORK_SEARCH_PATHS = "";
-                               HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\"";
-                               INSTALL_PATH = /usr/sbin;
-                               LIBRARY_SEARCH_PATHS = "";
-                               OTHER_CFLAGS = "-D__MACOSX__";
-                               OTHER_LDFLAGS = "";
-                               OTHER_REZFLAGS = "";
-                               PRODUCT_NAME = mDNSResponder;
-                               REZ_EXECUTABLE = YES;
-                               SECTORDER_FLAGS = "";
-                               WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
-                       };
-                       dependencies = (
-                       );
-                       isa = PBXToolTarget;
-                       name = mDNSResponder;
-                       productInstallPath = "$(HOME)/bin";
-                       productName = mDNSResponder;
-                       productReference = 034768E2FF38A6DC11DB9C8B;
-                       shouldUseHeadermap = 1;
-               };
-               08FB77A0FE84155DC02AAC07 = {
-                       buildActionMask = 2147483647;
-                       files = (
-                               6575FC02022EAFBA00000109,
-                               654BE65502B63B93000001D1,
-                               654BE65602B63B93000001D1,
-                               654BE65702B63B93000001D1,
-                               654BE65802B63B93000001D1,
-                               654BE65902B63B93000001D1,
-                               654BE65A02B63B93000001D1,
-                       );
-                       isa = PBXHeadersBuildPhase;
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
-               08FB77A1FE84155DC02AAC07 = {
-                       buildActionMask = 2147483647;
-                       files = (
-                               6575FC0D022EB18700000109,
-                               6575FC0E022EB18700000109,
-                               6575FBEA022EAF5A00000109,
-                               6575FBED022EAF7200000109,
-                               6575FBEE022EAF7200000109,
-                               6575FC15022EB27800000109,
-                       );
-                       isa = PBXSourcesBuildPhase;
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
-               08FB77A3FE84155DC02AAC07 = {
-                       buildActionMask = 2147483647;
-                       files = (
-                               09AB6885FE841BABC02AAC07,
-                               65713D66025A293200000109,
-                               6585DD640279A3B7000001D1,
-                       );
-                       isa = PBXFrameworksBuildPhase;
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
-               08FB77A5FE84155DC02AAC07 = {
-                       buildActionMask = 2147483647;
-                       files = (
-                       );
-                       isa = PBXRezBuildPhase;
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
-//080
-//081
-//082
-//083
-//084
-//090
-//091
-//092
-//093
-//094
-               09AB6884FE841BABC02AAC07 = {
-                       isa = PBXFrameworkReference;
-                       name = CoreFoundation.framework;
-                       path = /System/Library/Frameworks/CoreFoundation.framework;
-                       refType = 0;
-               };
-               09AB6885FE841BABC02AAC07 = {
-                       fileRef = 09AB6884FE841BABC02AAC07;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-//090
-//091
-//092
-//093
-//094
-//190
-//191
-//192
-//193
-//194
-               19C28FBDFE9D53C911CA2CBB = {
-                       children = (
-                               034768E2FF38A6DC11DB9C8B,
-                               6575FC1D022EB76000000109,
-                       );
-                       isa = PBXGroup;
-                       name = Products;
-                       refType = 4;
-               };
-//190
-//191
-//192
-//193
-//194
-//650
-//651
-//652
-//653
-//654
-               654BE64F02B63B93000001D1 = {
-                       isa = PBXFileReference;
-                       name = 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;
-                       refType = 4;
-               };
-               654BE65202B63B93000001D1 = {
-                       isa = PBXFileReference;
-                       name = mDNSPlatformFunctions.h;
-                       path = mDNSCore/mDNSPlatformFunctions.h;
-                       refType = 4;
-               };
-               654BE65302B63B93000001D1 = {
-                       isa = PBXFileReference;
-                       name = mDNSsprintf.h;
-                       path = mDNSCore/mDNSsprintf.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;
-                       path = /System/Library/Frameworks/SystemConfiguration.framework;
-                       refType = 0;
-               };
-               65713D66025A293200000109 = {
-                       fileRef = 65713D46025A293200000109;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-               6575FBE9022EAF5A00000109 = {
-                       indentWidth = 4;
-                       isa = PBXFileReference;
-                       name = mDNS.c;
-                       path = mDNSCore/mDNS.c;
-                       refType = 4;
-                       tabWidth = 4;
-                       usesTabs = 1;
-               };
-               6575FBEA022EAF5A00000109 = {
-                       fileRef = 6575FBE9022EAF5A00000109;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-               6575FBEB022EAF7200000109 = {
-                       indentWidth = 4;
-                       isa = PBXFileReference;
-                       path = CFSocket.c;
-                       refType = 4;
-                       tabWidth = 4;
-                       usesTabs = 1;
-               };
-               6575FBEC022EAF7200000109 = {
-                       indentWidth = 4;
-                       isa = PBXFileReference;
-                       path = daemon.c;
-                       refType = 4;
-                       tabWidth = 4;
-                       usesTabs = 1;
-               };
-               6575FBED022EAF7200000109 = {
-                       fileRef = 6575FBEB022EAF7200000109;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-               6575FBEE022EAF7200000109 = {
-                       fileRef = 6575FBEC022EAF7200000109;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-               6575FBFE022EAFA800000109 = {
-                       children = (
-                               6575FBFF022EAFBA00000109,
-                               6575FC00022EAFBA00000109,
-                               6575FC01022EAFBA00000109,
-                       );
-                       isa = PBXGroup;
-                       name = "DNS Service Discovery MIG files";
-                       refType = 4;
-               };
-               6575FBFF022EAFBA00000109 = {
-                       isa = PBXFileReference;
-                       path = DNSServiceDiscoveryDefines.h;
-                       refType = 4;
-               };
-               6575FC00022EAFBA00000109 = {
-                       isa = PBXFileReference;
-                       path = DNSServiceDiscoveryReply.defs;
-                       refType = 4;
-               };
-               6575FC01022EAFBA00000109 = {
-                       isa = PBXFileReference;
-                       path = DNSServiceDiscoveryRequest.defs;
-                       refType = 4;
-               };
-               6575FC02022EAFBA00000109 = {
-                       fileRef = 6575FBFF022EAFBA00000109;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-               6575FC0D022EB18700000109 = {
-                       fileRef = 6575FC00022EAFBA00000109;
-                       isa = PBXBuildFile;
-                       settings = {
-                               ATTRIBUTES = (
-                                       Client,
-                               );
-                       };
-               };
-               6575FC0E022EB18700000109 = {
-                       fileRef = 6575FC01022EAFBA00000109;
-                       isa = PBXBuildFile;
-                       settings = {
-                               ATTRIBUTES = (
-                                       Server,
-                                       Client,
-                               );
-                       };
-               };
-               6575FC12022EB27800000109 = {
-                       isa = PBXFileReference;
-                       name = mDNSsprintf.c;
-                       path = mDNSCore/mDNSsprintf.c;
-                       refType = 4;
-               };
-               6575FC15022EB27800000109 = {
-                       fileRef = 6575FC12022EB27800000109;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-               6575FC18022EB76000000109 = {
-                       buildActionMask = 2147483647;
-                       files = (
-                       );
-                       isa = PBXHeadersBuildPhase;
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
-               6575FC19022EB76000000109 = {
-                       buildActionMask = 2147483647;
-                       files = (
-                               6575FC21022EB7AA00000109,
-                       );
-                       isa = PBXSourcesBuildPhase;
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
-               6575FC1A022EB76000000109 = {
-                       buildActionMask = 2147483647;
-                       files = (
-                               6575FC24022EBA5D00000109,
-                       );
-                       isa = PBXFrameworksBuildPhase;
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
-               6575FC1B022EB76000000109 = {
-                       buildActionMask = 2147483647;
-                       files = (
-                       );
-                       isa = PBXRezBuildPhase;
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
-               6575FC1C022EB76000000109 = {
-                       buildPhases = (
-                               6575FC18022EB76000000109,
-                               6575FC19022EB76000000109,
-                               6575FC1A022EB76000000109,
-                               6575FC1B022EB76000000109,
-                       );
-                       buildSettings = {
-                               OTHER_CFLAGS = "";
-                               OTHER_LDFLAGS = "";
-                               OTHER_REZFLAGS = "";
-                               PRODUCT_NAME = mDNS;
-                               REZ_EXECUTABLE = YES;
-                               SECTORDER_FLAGS = "";
-                               WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
-                       };
-                       dependencies = (
-                       );
-                       isa = PBXToolTarget;
-                       name = mDNS;
-                       productInstallPath = /usr/local/bin;
-                       productName = "Sample mDNS Client";
-                       productReference = 6575FC1D022EB76000000109;
-                       shouldUseHeadermap = 0;
-               };
-               6575FC1D022EB76000000109 = {
-                       isa = PBXExecutableFileReference;
-                       path = mDNS;
-                       refType = 3;
-               };
-               6575FC1F022EB78C00000109 = {
-                       children = (
-                               6575FC20022EB7AA00000109,
-                       );
-                       isa = PBXGroup;
-                       name = SampleMulticastDNSClient;
-                       refType = 4;
-               };
-               6575FC20022EB7AA00000109 = {
-                       indentWidth = 4;
-                       isa = PBXFileReference;
-                       path = SamplemDNSClient.c;
-                       refType = 4;
-                       tabWidth = 4;
-                       usesTabs = 0;
-               };
-               6575FC21022EB7AA00000109 = {
-                       fileRef = 6575FC20022EB7AA00000109;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-               6575FC24022EBA5D00000109 = {
-                       fileRef = 09AB6884FE841BABC02AAC07;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-               6585DD640279A3B7000001D1 = {
-                       fileRef = 00CA213D02786FC30CCA2C71;
-                       isa = PBXBuildFile;
-                       settings = {
-                       };
-               };
-       };
-       rootObject = 08FB7793FE84155DC02AAC07;
-}
diff --git a/mDNSVxWorks/README.txt b/mDNSVxWorks/README.txt
new file mode 100644 (file)
index 0000000..f0ea01e
--- /dev/null
@@ -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 (file)
index 0000000..b482fb8
--- /dev/null
@@ -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
+<rdar://problem/3382647> 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
+<rdar://problem/3375491> 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       <stdarg.h>
+#include       <stddef.h>
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       <string.h>
+
+#include       <sys/types.h>
+#include       <arpa/inet.h>
+#include       <fcntl.h>
+#include       <netinet/if_ether.h>
+#include       <netinet/in.h>
+#include       <netinet/ip.h>
+#include       <sys/ioctl.h>
+#include       <sys/socket.h>
+#include       <unistd.h>
+
+#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 : "<any>" );
+               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 (file)
index 0000000..de026a4
--- /dev/null
@@ -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 (file)
index 0000000..f588af6
--- /dev/null
@@ -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 (file)
index 0000000..3f64b2a
--- /dev/null
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="7.00"
+       Name="Application"
+       ProjectGUID="{EDE4B529-4CF5-4a49-9B6F-C10F0EA24278}"
+       Keyword="MFCProj">
+       <Platforms>
+               <Platform
+                       Name="Win32"/>
+       </Platforms>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory="Debug"
+                       IntermediateDirectory="Debug"
+                       ConfigurationType="1"
+                       UseOfMFC="1"
+                       CharacterSet="2">
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="0"
+                               AdditionalIncludeDirectories=".\Resources;..\;..\..\..\..\mDNSCore;..\..\..\mDNSWindows;..\..\..\DNSServices"
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;DEBUG"
+                               StringPooling="TRUE"
+                               MinimalRebuild="FALSE"
+                               BasicRuntimeChecks="3"
+                               SmallerTypeCheck="FALSE"
+                               RuntimeLibrary="1"
+                               BufferSecurityCheck="TRUE"
+                               EnableFunctionLevelLinking="FALSE"
+                               ForceConformanceInForLoopScope="TRUE"
+                               RuntimeTypeInfo="TRUE"
+                               UsePrecompiledHeader="2"
+                               PrecompiledHeaderThrough="StdAfx.h"
+                               PrecompiledHeaderFile=".\Debug/Application.pch"
+                               AssemblerListingLocation=".\Debug/"
+                               ObjectFile=".\Debug/"
+                               ProgramDataBaseFileName=".\Debug/"
+                               BrowseInformation="1"
+                               WarningLevel="4"
+                               WarnAsError="TRUE"
+                               SuppressStartupBanner="TRUE"
+                               Detect64BitPortabilityProblems="TRUE"
+                               DebugInformationFormat="3"
+                               CompileAs="0"/>
+                       <Tool
+                               Name="VCCustomBuildTool"/>
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalOptions="/MACHINE:I386"
+                               AdditionalDependencies="ws2_32.lib nafxcwd.lib LIBCMTD.lib"
+                               OutputFile="Rendezvous Browser Debug.exe"
+                               LinkIncremental="1"
+                               SuppressStartupBanner="TRUE"
+                               IgnoreDefaultLibraryNames="wsock32.lib;nafxcwd.lib;LIBCMTD.lib"
+                               GenerateDebugInformation="TRUE"
+                               ProgramDatabaseFile=".\Debug/Application.pdb"
+                               SubSystem="2"/>
+                       <Tool
+                               Name="VCMIDLTool"
+                               PreprocessorDefinitions="_DEBUG"
+                               MkTypLibCompatible="FALSE"
+                               SuppressStartupBanner="TRUE"
+                               TargetEnvironment="1"
+                               TypeLibraryName=".\Debug/Application.tlb"/>
+                       <Tool
+                               Name="VCPostBuildEventTool"/>
+                       <Tool
+                               Name="VCPreBuildEventTool"/>
+                       <Tool
+                               Name="VCPreLinkEventTool"/>
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                               PreprocessorDefinitions="_AFXDLL;_DEBUG"
+                               Culture="1033"/>
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"/>
+                       <Tool
+                               Name="VCWebDeploymentTool"/>
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory=".\Release"
+                       IntermediateDirectory=".\Release"
+                       ConfigurationType="1"
+                       UseOfMFC="1"
+                       CharacterSet="2"
+                       WholeProgramOptimization="FALSE">
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="1"
+                               GlobalOptimizations="TRUE"
+                               InlineFunctionExpansion="0"
+                               FavorSizeOrSpeed="2"
+                               OmitFramePointers="FALSE"
+                               OptimizeForWindowsApplication="FALSE"
+                               AdditionalIncludeDirectories=".\Resources;..\;..\..\..\..\mDNSCore;..\..\..\mDNSWindows;..\..\..\DNSServices"
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+                               StringPooling="TRUE"
+                               MinimalRebuild="FALSE"
+                               RuntimeLibrary="0"
+                               BufferSecurityCheck="FALSE"
+                               EnableFunctionLevelLinking="FALSE"
+                               ForceConformanceInForLoopScope="TRUE"
+                               RuntimeTypeInfo="TRUE"
+                               UsePrecompiledHeader="2"
+                               PrecompiledHeaderThrough="stdafx.h"
+                               PrecompiledHeaderFile=".\Release/Application.pch"
+                               AssemblerListingLocation=".\Release/"
+                               ObjectFile=".\Release/"
+                               ProgramDataBaseFileName=".\Release/"
+                               BrowseInformation="1"
+                               WarningLevel="4"
+                               WarnAsError="TRUE"
+                               SuppressStartupBanner="TRUE"
+                               Detect64BitPortabilityProblems="TRUE"
+                               CompileAs="0"/>
+                       <Tool
+                               Name="VCCustomBuildTool"/>
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalOptions="/MACHINE:I386"
+                               AdditionalDependencies="ws2_32.lib nafxcw.lib LIBCMT.lib"
+                               OutputFile="Rendezvous Browser.exe"
+                               LinkIncremental="1"
+                               SuppressStartupBanner="TRUE"
+                               IgnoreDefaultLibraryNames="wsock32.lib;nafxcw.lib;LIBCMT.lib"
+                               ProgramDatabaseFile=".\Release/Application.pdb"
+                               SubSystem="2"
+                               OptimizeReferences="2"
+                               EnableCOMDATFolding="2"/>
+                       <Tool
+                               Name="VCMIDLTool"
+                               PreprocessorDefinitions="NDEBUG"
+                               MkTypLibCompatible="TRUE"
+                               SuppressStartupBanner="TRUE"
+                               TargetEnvironment="1"
+                               TypeLibraryName=".\Release/Application.tlb"/>
+                       <Tool
+                               Name="VCPostBuildEventTool"/>
+                       <Tool
+                               Name="VCPreBuildEventTool"/>
+                       <Tool
+                               Name="VCPreLinkEventTool"/>
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                               PreprocessorDefinitions="_AFXDLL;NDEBUG"
+                               Culture="1033"/>
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"/>
+                       <Tool
+                               Name="VCWebDeploymentTool"/>
+               </Configuration>
+               <Configuration
+                       Name="All|Win32"
+                       OutputDirectory="All"
+                       IntermediateDirectory="All"
+                       ConfigurationType="1">
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalIncludeDirectories=""/>
+                       <Tool
+                               Name="VCCustomBuildTool"/>
+                       <Tool
+                               Name="VCLinkerTool"/>
+                       <Tool
+                               Name="VCMIDLTool"/>
+                       <Tool
+                               Name="VCPostBuildEventTool"/>
+                       <Tool
+                               Name="VCPreBuildEventTool"/>
+                       <Tool
+                               Name="VCPreLinkEventTool"/>
+                       <Tool
+                               Name="VCResourceCompilerTool"/>
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"/>
+                       <Tool
+                               Name="VCWebDeploymentTool"/>
+               </Configuration>
+       </Configurations>
+       <Files>
+               <Filter
+                       Name="Source Files"
+                       Filter="">
+                       <File
+                               RelativePath="Sources\AboutDialog.cpp">
+                       </File>
+                       <File
+                               RelativePath="Sources\AboutDialog.h">
+                       </File>
+                       <File
+                               RelativePath="Sources\Application.cpp">
+                       </File>
+                       <File
+                               RelativePath="Sources\Application.h">
+                       </File>
+                       <File
+                               RelativePath="Sources\ChooserDialog.cpp">
+                       </File>
+                       <File
+                               RelativePath="Sources\ChooserDialog.h">
+                       </File>
+                       <File
+                               RelativePath="Sources\StdAfx.cpp">
+                       </File>
+                       <File
+                               RelativePath="Sources\StdAfx.h">
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Resource Files"
+                       Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;rc">
+                       <File
+                               RelativePath="Resources\Application.ico">
+                       </File>
+                       <File
+                               RelativePath="Resources\Application.rc">
+                       </File>
+                       <File
+                               RelativePath="Resources\Application.rc2">
+                       </File>
+                       <File
+                               RelativePath=".\Resources\Resource.h">
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Rendezvous"
+                       Filter="">
+                       <File
+                               RelativePath="..\..\..\DNSServices\DNSServices.c">
+                       </File>
+                       <File
+                               RelativePath="..\..\..\..\mDNSCore\mDNS.c">
+                       </File>
+                       <File
+                               RelativePath="..\..\..\mDNSWin32.c">
+                       </File>
+               </Filter>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico
new file mode 100644 (file)
index 0000000..a992327
Binary files /dev/null and b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.ico differ
diff --git a/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc b/mDNSWindows/Applications/RendezvousBrowser/Windows/Resources/Application.rc
new file mode 100644 (file)
index 0000000..1d9c2eb
--- /dev/null
@@ -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 (file)
index 0000000..bf1983f
--- /dev/null
@@ -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 (file)
index 0000000..5329fc1
--- /dev/null
@@ -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 (file)
index 0000000..cd8cafb
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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       <stdlib.h>
+
+#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 (file)
index 0000000..11c9149
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 (file)
index 0000000..396b7e9
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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       <assert.h>
+
+#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 (file)
index 0000000..e174e4f
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 (file)
index 0000000..ddd81f9
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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       <assert.h>
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       <string.h>
+#include       <time.h>
+
+#include       <algorithm>
+#include       <memory>
+
+#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 <DomainEventInfo *> ( 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 <DomainEventInfo *> ( 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 <ServiceEventInfo *> ( 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 <ServiceEventInfo *> ( 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 <ServiceInstanceInfo *> ( 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 <ChooserDialog *> ( 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 (file)
index 0000000..7acb0b5
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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       <string>
+#include       <vector>
+
+#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 (file)
index 0000000..a26a1e7
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 (file)
index 0000000..3c16ff7
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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 <afxwin.h>         // MFC core and standard components
+#include <afxext.h>         // MFC extensions
+#include <afxdtctl.h>          // MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h>                    // MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include <afxsock.h>           // MFC socket extensions
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#include       <stdlib.h>
+
+#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 (file)
index 0000000..22f443d
--- /dev/null
@@ -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 (file)
index 0000000..7f62479
--- /dev/null
@@ -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 (file)
index 0000000..11ef513
--- /dev/null
@@ -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 (file)
index 0000000..51a182e
Binary files /dev/null and b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.ico differ
diff --git a/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc b/mDNSWindows/Applications/RendezvousBrowser/WindowsCE/Resources/Application.rc
new file mode 100644 (file)
index 0000000..c453f27
--- /dev/null
@@ -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 (file)
index 0000000..29c9fe7
--- /dev/null
@@ -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 (file)
index 0000000..31c3a43
--- /dev/null
@@ -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 <commctrl.h>
+       // 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 (file)
index 0000000..0337c56
--- /dev/null
@@ -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 (file)
index 0000000..ba28f41
--- /dev/null
@@ -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 (file)
index 0000000..1f4fe13
--- /dev/null
@@ -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 (file)
index 0000000..991fc08
--- /dev/null
@@ -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 (file)
index 0000000..7777510
--- /dev/null
@@ -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 (file)
index 0000000..6622a5d
--- /dev/null
@@ -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 (file)
index 0000000..3672152
--- /dev/null
@@ -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 <afxwin.h>         // MFC core and standard components
+#include <afxext.h>         // MFC extensions
+
+#if defined(_AFXDLL)
+#include <afxdtctl.h>          // MFC support for Internet Explorer 4 Common Controls
+#endif
+
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h>                    // MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include <afxsock.h>           // 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 (file)
index 0000000..7e49566
--- /dev/null
@@ -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        <stdint.h>
+#endif
+
+#include       <stdio.h>
+#include       <stdlib.h>
+
+#if( __MACH__ )
+       #include        <sys/types.h>
+       #include        <sys/socket.h>
+       #include        <netinet/in.h>
+       
+       #include        <signal.h>
+       #include        <unistd.h>
+       
+       #include        <CoreServices/CoreServices.h>
+#else
+       #define WIN32_LEAN_AND_MEAN
+
+       #include        <winsock2.h>
+       #include        <windows.h>
+#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 <type> <domain>                                     'b'rowse for 's'ervices\n" );
+       fprintf( stderr, "  -lsi <name> <type> <domain>                             'l'ookup 's'ervice 'i'nstance\n" );
+       fprintf( stderr, "  -rdb[d] <domain>                                        'r'egister 'd'omain for 'b'rowsing ['d'efault]\n" );
+       fprintf( stderr, "  -rdr[d] <domain>                                        'r'egister 'd'omain for 'r'egistration ['d'efault]\n" );
+       fprintf( stderr, "  -rs <name> <type> <domain> <port> <txt>                 'r'egister 's'ervice\n" );
+       fprintf( stderr, "  -rps <host> <ip> <name> <type> <domain> <port> <txt>    'r'egister 'p'roxy 's'ervice\n" );
+       fprintf( stderr, "  -rnss <name> <type> <domain>                            'r'egister 'n'o 's'uch 's'ervice\n" );
+       
+       fprintf( stderr, "  -ebs <type> <domain>                                    'e'mulated 'b'rowse for 's'ervices\n" );
+       fprintf( stderr, "  -ebd <registration/browse>                              'e'mulated 'b'rowse for 'd'omains\n" );
+       fprintf( stderr, "  -elsi <name> <type> <domain>                            'e'mulated 'l'ookup 's'ervice 'i'nstance\n" );
+       fprintf( stderr, "  -ers <name> <type> <domain> <port> <txt>                '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 <type> <domain>
+                                               
+                       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 <name> <type> <domain>
+                       
+                       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] <domain>
+                                               
+                       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] <domain>
+                       
+                       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 <name> <type> <domain> <port> <txt>
+                                               
+                       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 <name> <type> <domain> <port> <txt>
+                                               
+                       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 <name> <type> <domain>
+                                               
+                       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 <type> <domain>
+                                               
+                       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 <registration/browse>
+                       
+                       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 <name> <type> <domain>
+                       
+                       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 <name> <type> <domain> <port> <txt>
+                                               
+                       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 (file)
index 0000000..eaa6a70
--- /dev/null
@@ -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 (file)
index 0000000..1f76fc2
--- /dev/null
@@ -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 (file)
index 0000000..064aba0
Binary files /dev/null and b/mDNSWindows/Applications/RendezvousTest/ToolWin32.mcp differ
diff --git a/mDNSWindows/Applications/RendezvousTest/ToolWin32.sln b/mDNSWindows/Applications/RendezvousTest/ToolWin32.sln
new file mode 100644 (file)
index 0000000..495d5df
--- /dev/null
@@ -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 (file)
index 0000000..d5e1868
--- /dev/null
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="7.00"
+       Name="Tool"
+       ProjectGUID="{F66EFE7E-50A6-44D4-87C7-742B303BA852}"
+       Keyword="Win32Proj">
+       <Platforms>
+               <Platform
+                       Name="Win32"/>
+       </Platforms>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory="Debug"
+                       IntermediateDirectory="Debug"
+                       ConfigurationType="1"
+                       CharacterSet="1">
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="..\..\..\mDNSCore;..\..\..\mDNSWindows;..\..\DNSServices"
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+                               StringPooling="TRUE"
+                               MinimalRebuild="TRUE"
+                               BasicRuntimeChecks="3"
+                               SmallerTypeCheck="FALSE"
+                               RuntimeLibrary="1"
+                               BufferSecurityCheck="TRUE"
+                               ForceConformanceInForLoopScope="TRUE"
+                               UsePrecompiledHeader="2"
+                               BrowseInformation="1"
+                               WarningLevel="4"
+                               WarnAsError="TRUE"
+                               Detect64BitPortabilityProblems="TRUE"
+                               DebugInformationFormat="4"/>
+                       <Tool
+                               Name="VCCustomBuildTool"/>
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="ws2_32.lib"
+                               OutputFile="$(OutDir)/Tool.exe"
+                               LinkIncremental="2"
+                               GenerateDebugInformation="TRUE"
+                               ProgramDatabaseFile="$(OutDir)/Tool.pdb"
+                               SubSystem="1"
+                               TargetMachine="1"/>
+                       <Tool
+                               Name="VCMIDLTool"/>
+                       <Tool
+                               Name="VCPostBuildEventTool"/>
+                       <Tool
+                               Name="VCPreBuildEventTool"/>
+                       <Tool
+                               Name="VCPreLinkEventTool"/>
+                       <Tool
+                               Name="VCResourceCompilerTool"/>
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"/>
+                       <Tool
+                               Name="VCWebDeploymentTool"/>
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory="Release"
+                       IntermediateDirectory="Release"
+                       ConfigurationType="1"
+                       CharacterSet="1"
+                       WholeProgramOptimization="FALSE">
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="2"
+                               InlineFunctionExpansion="2"
+                               FavorSizeOrSpeed="2"
+                               OmitFramePointers="TRUE"
+                               AdditionalIncludeDirectories="..\..\..\mDNSCore;..\..\..\mDNSWindows;..\..\DNSServices"
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+                               StringPooling="TRUE"
+                               ExceptionHandling="FALSE"
+                               RuntimeLibrary="0"
+                               BufferSecurityCheck="FALSE"
+                               EnableFunctionLevelLinking="FALSE"
+                               DisableLanguageExtensions="FALSE"
+                               ForceConformanceInForLoopScope="TRUE"
+                               UsePrecompiledHeader="2"
+                               WarningLevel="4"
+                               WarnAsError="TRUE"
+                               Detect64BitPortabilityProblems="TRUE"
+                               DebugInformationFormat="3"/>
+                       <Tool
+                               Name="VCCustomBuildTool"/>
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="ws2_32.lib"
+                               OutputFile="$(OutDir)/Tool.exe"
+                               LinkIncremental="1"
+                               GenerateDebugInformation="TRUE"
+                               SubSystem="1"
+                               OptimizeReferences="2"
+                               EnableCOMDATFolding="2"
+                               TargetMachine="1"/>
+                       <Tool
+                               Name="VCMIDLTool"/>
+                       <Tool
+                               Name="VCPostBuildEventTool"/>
+                       <Tool
+                               Name="VCPreBuildEventTool"/>
+                       <Tool
+                               Name="VCPreLinkEventTool"/>
+                       <Tool
+                               Name="VCResourceCompilerTool"/>
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"/>
+                       <Tool
+                               Name="VCWebDeploymentTool"/>
+               </Configuration>
+               <Configuration
+                       Name="all|Win32"
+                       OutputDirectory="all"
+                       IntermediateDirectory="all"
+                       ConfigurationType="1">
+                       <Tool
+                               Name="VCCLCompilerTool"/>
+                       <Tool
+                               Name="VCCustomBuildTool"/>
+                       <Tool
+                               Name="VCLinkerTool"/>
+                       <Tool
+                               Name="VCMIDLTool"/>
+                       <Tool
+                               Name="VCPostBuildEventTool"/>
+                       <Tool
+                               Name="VCPreBuildEventTool"/>
+                       <Tool
+                               Name="VCPreLinkEventTool"/>
+                       <Tool
+                               Name="VCResourceCompilerTool"/>
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"/>
+                       <Tool
+                               Name="VCWebDeploymentTool"/>
+               </Configuration>
+       </Configurations>
+       <Files>
+               <Filter
+                       Name="Rendezvous"
+                       Filter="">
+                       <File
+                               RelativePath="..\..\DNSServices\DNSServiceDiscovery.c">
+                       </File>
+                       <File
+                               RelativePath="..\..\DNSServices\DNSServiceDiscovery.h">
+                       </File>
+                       <File
+                               RelativePath="..\..\DNSServices\DNSServices.c">
+                       </File>
+                       <File
+                               RelativePath="..\..\DNSServices\DNSServices.h">
+                       </File>
+                       <File
+                               RelativePath="..\..\..\mDNSCore\mDNS.c">
+                       </File>
+                       <File
+                               RelativePath="..\..\..\mDNSCore\mDNSClientAPI.h">
+                       </File>
+                       <File
+                               RelativePath="..\..\..\mDNSCore\mDNSDebug.h">
+                       </File>
+                       <File
+                               RelativePath="..\..\..\mDNSCore\mDNSPlatformFunctions.h">
+                       </File>
+                       <File
+                               RelativePath="..\..\mDNSWin32.c">
+                       </File>
+                       <File
+                               RelativePath="..\..\mDNSWin32.h">
+                       </File>
+               </Filter>
+               <File
+                       RelativePath="Tool.c">
+               </File>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/mDNSWindows/DNSServices/DNSServiceDiscovery.c b/mDNSWindows/DNSServices/DNSServiceDiscovery.c
new file mode 100644 (file)
index 0000000..77e3dcb
--- /dev/null
@@ -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       <stddef.h>
+#include       <stdlib.h>
+#include       <string.h>
+
+#if( macintosh || __MACH__ )
+
+       #include        <sys/types.h>
+       #include        <sys/socket.h>
+       #include        <netinet/in.h>
+       
+#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        <winsock2.h>
+       
+#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, &registration );
+       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 (file)
index 0000000..618bbb3
--- /dev/null
@@ -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       <stddef.h>
+
+#if( __MACH__ )
+
+       #include        <mach/mach_types.h>
+
+       #include        <sys/types.h>
+       #include        <sys/socket.h>
+       #include        <sys/cdefs.h>
+       
+       #include        <netinet/in.h>
+
+#elif( defined( __MWERKS__ ) )
+
+       #include        <stdint.h>
+       
+#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 <http://www.iana.org/assignments/port-numbers>)
+    @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 (executable)
index 0000000..ca84436
--- /dev/null
@@ -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
+<rdar://problem/3375491> 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
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.10  2003/07/02 21:20:10  cheshire
+<rdar://problem/3313413> 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
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.7  2003/03/27 03:30:57  cheshire
+<rdar://problem/3210018> 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       <stddef.h>
+#include       <stdlib.h>
+#include       <string.h>
+
+#if( __MACH__ )
+       #include        <CoreServices/CoreServices.h>
+#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 (executable)
index 0000000..f5be4aa
--- /dev/null
@@ -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
+<rdar://problem/3313413> 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       <stddef.h>
+
+#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:
+       
+       <http://www.jaggersoft.com/pubs/CVu11_3.html>
+       <http://www.jaggersoft.com/pubs/CVu11_5.html>
+       
+       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:
+       
+       <http://www.jaggersoft.com/pubs/CVu11_3.html>
+       <http://www.jaggersoft.com/pubs/CVu11_5.html>
+*/
+
+#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 <http://www.multicastdns.org/>).
+*/
+
+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 (file)
index 0000000..52c48b1
--- /dev/null
@@ -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 (executable)
index 0000000..161e635
--- /dev/null
@@ -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
+<rdar://problem/3382647> 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 <rdar://problem/3160248>
+"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
+<rdar://problem/3313413> 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
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.12  2003/05/06 21:06:05  cheshire
+<rdar://problem/3242673> mDNSWindows needs a wakeupEvent object to signal the main thread
+
+Revision 1.11  2003/05/06 00:00:51  cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.10  2003/04/29 00:06:09  cheshire
+<rdar://problem/3242673> 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       <stdarg.h>
+#include       <stddef.h>
+#include       <stdio.h>
+#include       <stdlib.h>
+#include       <string.h>
+
+#include       <windows.h>
+#include       <winsock2.h>
+#include       <Ws2tcpip.h>
+
+#if( !defined( _WIN32_WCE ) )                  // Windows CE does not have process.h.
+       #include        <process.h>
+#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, &param );
+       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 <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp>.
+       
+       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 <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp>.
+       
+       _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 (executable)
index 0000000..2bf5ddd
--- /dev/null
@@ -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 <rdar://problem/3160248>
+"ScheduleNextTask needs to be smarter" has refined the way m->NextScheduledEvent is set
+
+Revision 1.6  2003/07/02 21:20:04  cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.5  2003/04/29 00:06:09  cheshire
+<rdar://problem/3242673> 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       <windows.h>
+#include       <winsock2.h>
+#include       <Ws2tcpip.h>
+
+#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__