From 8e92c31c9a45a66732f5bc7afbc9f5596c17e91d Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 29 Jun 2004 05:26:23 +0000 Subject: [PATCH] mDNSResponder-66.3.tar.gz --- Clients/DNS-SD.xcode/project.pbxproj | 581 +++ Clients/DNSServiceBrowser-Info.plist | 30 + Clients/DNSServiceBrowser.m | 709 ++-- Clients/DNSServiceBrowser.nib/classes.nib | 5 +- Clients/DNSServiceBrowser.nib/info.nib | 6 +- Clients/DNSServiceBrowser.nib/objects.nib | Bin 9876 -> 10423 bytes Clients/DNSServiceReg.m | 247 -- Clients/DNSServiceReg.nib/classes.nib | 30 - Clients/DNSServiceReg.nib/info.nib | 22 - Clients/DNSServiceReg.nib/objects.nib | Bin 9113 -> 0 bytes Clients/DNSServiceRegistration-Info.plist | 30 + Clients/DNSServiceRegistration.m | 138 +- Clients/Java/BrowserApp.java | 394 +++ Clients/Java/DNSSDUnitTest.java | 311 ++ Clients/Java/SimpleChat.java | 338 ++ Clients/Java/SwingBrowseListener.java | 119 + Clients/Java/SwingDomainListener.java | 111 + Clients/Java/SwingQueryListener.java | 103 + Clients/Java/SwingResolveListener.java | 101 + Clients/Makefile | 62 + Clients/ReadMe.txt | 25 + Clients/dns-sd.c | 455 +++ Makefile | 6 +- README.txt | 34 +- mDNSCore/DNSCommon.c | 1684 +++++++++ mDNSCore/DNSCommon.h | 287 ++ mDNSCore/DNSDigest.c | 1440 ++++++++ mDNSCore/mDNS.c | 2781 +++++---------- mDNSCore/mDNSClientAPI.h | 795 ++++- mDNSCore/mDNSDebug.h | 74 +- mDNSCore/mDNSEmbeddedAPI.h | 1470 -------- mDNSCore/mDNSPlatformFunctions.h | 83 - mDNSCore/uDNS.c | 3001 ++++++++++++++++ mDNSCore/uDNS.h | 116 + mDNSMacOS9/CarbonResource.r | 2 + mDNSMacOS9/Mac OS Test Responder.c | 49 +- mDNSMacOS9/Mac OS Test Searcher.c | 48 +- mDNSMacOS9/README.txt | 40 +- mDNSMacOS9/Responder.c | 202 ++ mDNSMacOS9/Searcher.c | 259 ++ mDNSMacOS9/ShowInitIcon.c | 160 + mDNSMacOS9/ShowInitIcon.h | 20 + mDNSMacOS9/mDNS.mcp | Bin 413450 -> 967336 bytes mDNSMacOS9/mDNSLibrary.c | 80 + mDNSMacOS9/mDNSLibraryLoader.c | 84 + mDNSMacOS9/mDNSLibraryResources.r | 222 ++ mDNSMacOS9/mDNSMacOS9.c | 368 +- mDNSMacOS9/mDNSMacOS9.h | 32 +- mDNSMacOS9/mDNSPrefix.h | 77 + .../DNSServiceBrowser/BrowserController.h | 83 - .../DNSServiceBrowser/BrowserController.m | 541 --- .../project.pbxproj | 377 -- .../English.lproj/InfoPlist.strings | Bin 612 -> 0 bytes .../English.lproj/MainMenu.nib/classes.nib | 37 - .../English.lproj/MainMenu.nib/info.nib | 22 - .../English.lproj/MainMenu.nib/objects.nib | Bin 9876 -> 0 bytes .../project.pbxproj | 377 -- .../English.lproj/InfoPlist.strings | Bin 642 -> 0 bytes .../English.lproj/MainMenu.nib/classes.nib | 30 - .../English.lproj/MainMenu.nib/info.nib | 22 - .../English.lproj/MainMenu.nib/objects.nib | Bin 9113 -> 0 bytes .../RegistrationController.h | 66 - .../RegistrationController.m | 247 -- .../HAAutomounter/HAAutomounter.m | 102 - .../HAAutomounter.pbproj/project.pbxproj | 296 -- mDNSMacOSX/CFSocket.c | 1554 +++++++-- mDNSMacOSX/CFSocketPuma.c | 2 + mDNSMacOSX/DNSServiceDiscoveryDefines.h | 2 + mDNSMacOSX/DNSServiceDiscoveryReply.defs | 2 + mDNSMacOSX/DNSServiceDiscoveryRequest.defs | 2 + mDNSMacOSX/SampleUDSClient.c | 411 --- mDNSMacOSX/SamplemDNSClient.c | 42 +- mDNSMacOSX/daemon.c | 981 ++++-- mDNSMacOSX/dnssd_clientstub.c | 998 ------ mDNSMacOSX/dnssd_ipc.c | 137 - mDNSMacOSX/dnssd_ipc.h | 162 - mDNSMacOSX/mDNSMacOSX.c | 1494 -------- mDNSMacOSX/mDNSMacOSX.h | 136 +- mDNSMacOSX/mDNSMacOSXPuma.c | 238 -- .../mDNSResponder.pbproj/project.pbxproj | 824 +++-- mDNSMacOSX/uds_daemon.c | 2282 ------------ mDNSPosix/Client.c | 2 + mDNSPosix/ExampleClientApp.c | 2 + mDNSPosix/ExampleClientApp.h | 2 + mDNSPosix/Identify.c | 225 +- mDNSPosix/Makefile | 390 ++- mDNSPosix/NetMonitor.c | 296 +- mDNSPosix/PosixDaemon.c | 323 ++ mDNSPosix/ProxyResponder.c | 40 +- mDNSPosix/ReadMe.txt | 289 +- mDNSPosix/Responder.c | 51 +- mDNSPosix/mDNSPosix.c | 894 ++++- mDNSPosix/mDNSPosix.h | 61 +- mDNSPosix/mDNSUNP.c | 60 +- mDNSPosix/mDNSUNP.h | 38 +- mDNSPosix/mdnsd.sh | 89 + mDNSShared/GenLinkedList.c | 337 ++ mDNSShared/GenLinkedList.h | 112 + mDNSShared/Java/BaseListener.java | 2 + mDNSShared/Java/BrowseListener.java | 2 + mDNSShared/Java/DNSRecord.java | 27 +- mDNSShared/Java/DNSSD.java | 65 +- mDNSShared/Java/DNSSDException.java | 2 + mDNSShared/Java/DNSSDRegistration.java | 51 +- mDNSShared/Java/DNSSDService.java | 2 + mDNSShared/Java/DomainListener.java | 2 + mDNSShared/Java/JNISupport.c | 278 +- mDNSShared/Java/QueryListener.java | 2 + mDNSShared/Java/RegisterListener.java | 2 + mDNSShared/Java/ResolveListener.java | 2 + mDNSShared/Java/TXTRecord.java | 102 +- mDNSShared/dns_sd.h | 561 ++- mDNSShared/dnssd_clientlib.c | 342 ++ mDNSShared/dnssd_clientshim.c | 699 ++++ mDNSShared/dnssd_clientstub.c | 311 +- mDNSShared/dnssd_ipc.c | 10 +- mDNSShared/dnssd_ipc.h | 2 + mDNSShared/mDNS.1 | 184 + mDNSShared/mDNSDebug.c | 132 + mDNSShared/mDNSResponder.8 | 108 + mDNSShared/uds_daemon.c | 1235 ++++--- mDNSShared/uds_daemon.h | 69 + mDNSVxWorks/mDNSVxWorks.c | 85 +- mDNSVxWorks/mDNSVxWorks.h | 2 + mDNSWindows/Applications/DLL/dllmain.c | 49 + mDNSWindows/Applications/DLL/dnssd.def | 57 + mDNSWindows/Applications/DLL/dnssd.vcproj | 156 + .../Windows/ApplicationVS2002.sln | 7 +- .../Windows/ApplicationVS2002.vcproj | 95 +- .../Windows/ApplicationVS2003.sln | 3 - .../Windows/ApplicationVS2003.vcproj | 97 +- .../Windows/Resources/Application.ico | Bin 2238 -> 3638 bytes .../Windows/Resources/Application.rc | 104 +- .../Windows/Resources/Application.rc2 | 5 +- .../Windows/Resources/Resource.h | 10 +- .../Windows/Sources/AboutDialog.cpp | 7 +- .../Windows/Sources/AboutDialog.h | 7 +- .../Windows/Sources/Application.cpp | 17 +- .../Windows/Sources/Application.h | 7 +- .../Windows/Sources/ChooserDialog.cpp | 501 ++- .../Windows/Sources/ChooserDialog.h | 11 +- .../Windows/Sources/LoginDialog.cpp | 127 + .../Windows/Sources/LoginDialog.h | 71 + .../Windows/Sources/StdAfx.cpp | 7 +- .../Windows/Sources/StdAfx.h | 27 +- .../WindowsCE/Application.vcp | 419 ++- .../WindowsCE/Resources/Application.ico | Bin 1078 -> 1406 bytes .../WindowsCE/Resources/Application.rc | 2 +- .../WindowsCE/Sources/Application.cpp | 13 +- .../WindowsCE/Sources/Application.h | 7 +- .../WindowsCE/Sources/BrowserDialog.cpp | 241 +- .../WindowsCE/Sources/BrowserDialog.h | 25 +- .../WindowsCE/Sources/StdAfx.cpp | 7 +- .../WindowsCE/Sources/StdAfx.h | 10 +- .../Applications/DNSServiceTest/Tool.c | 48 +- .../DNSServiceTest/ToolPrefixWindows.h | 7 +- .../DNSServiceTest/ToolPrefixWindowsDebug.h | 7 +- .../Applications/DNSServiceTest/ToolWin32.mcp | Bin 284642 -> 284642 bytes .../DNSServiceTest/ToolWin32VS2002.sln | 7 +- .../DNSServiceTest/ToolWin32VS2002.vcproj | 44 +- .../DNSServiceTest/ToolWin32VS2003.sln | 3 - .../DNSServiceTest/ToolWin32VS2003.vcproj | 50 +- .../ExplorerPlugin/ClassFactory.cpp | 192 + .../ExplorerPlugin/ClassFactory.h | 66 + .../ExplorerPlugin/ExplorerBar.cpp | 582 ++++ .../Applications/ExplorerPlugin/ExplorerBar.h | 112 + .../ExplorerPlugin/ExplorerBarWindow.cpp | 751 ++++ .../ExplorerPlugin/ExplorerBarWindow.h | 281 ++ .../ExplorerPlugin/ExplorerPlugin.cpp | 477 +++ .../ExplorerPlugin/ExplorerPlugin.def | 37 + .../ExplorerPlugin/ExplorerPlugin.h | 33 +- .../ExplorerPlugin/ExplorerPlugin.rc | 173 + .../ExplorerPlugin/ExplorerPlugin.sln | 21 + .../ExplorerPlugin/ExplorerPlugin.vcproj | 242 ++ .../ExplorerPlugin/LoginDialog.cpp | 126 + .../Applications/ExplorerPlugin/LoginDialog.h | 68 + .../Applications/ExplorerPlugin/ReadMe.txt | 9 + .../Applications/ExplorerPlugin/Resource.h | 23 + .../Applications/ExplorerPlugin/StdAfx.cpp | 25 +- .../Applications/ExplorerPlugin/StdAfx.h | 39 +- mDNSWindows/Applications/Java/makefile | 134 + .../Applications/NSPTool/Prefix.h | 35 +- mDNSWindows/Applications/NSPTool/Tool.c | 580 +++ mDNSWindows/Applications/NSPTool/Tool.mcp | Bin 0 -> 306146 bytes .../SystemService/EventLogMessages.bin | Bin 0 -> 28 bytes .../Applications/SystemService/Prefix.h | 34 +- .../Applications/SystemService/Resource.h | 21 +- .../Applications/SystemService/Service.c | 829 +++++ .../Applications/SystemService/Service.mcp | Bin 0 -> 299890 bytes .../Applications/SystemService/Service.rc | 94 + .../SystemService/Service2002.sln | 21 + .../SystemService/Service2002.vcproj | 196 ++ .../Applications/SystemServiceTest/Prefix.h | 30 +- .../Applications/SystemServiceTest/Tool.c | 847 +++++ .../Applications/SystemServiceTest/Tool.mcp | Bin 0 -> 398728 bytes .../SystemServiceTest/Tool2002.sln | 21 + .../SystemServiceTest/Tool2002.vcproj | 191 + mDNSWindows/Applications/mdnsNSP/NSP.c | 1367 ++++++++ mDNSWindows/Applications/mdnsNSP/NSP.def | 36 + mDNSWindows/Applications/mdnsNSP/NSP.mcp | Bin 0 -> 198118 bytes mDNSWindows/Applications/mdnsNSP/Prefix.h | 45 + mDNSWindows/Applications/mdnsNSP/ReadMe.txt | 15 + mDNSWindows/Applications/mdnsNSP/mdnsNSP.c | 1367 ++++++++ mDNSWindows/Applications/mdnsNSP/mdnsNSP.def | 36 + mDNSWindows/Applications/mdnsNSP/mdnsNSP.mcp | Bin 0 -> 198118 bytes mDNSWindows/CommonServices.h | 1509 ++++++++ mDNSWindows/DNSSD.c | 1720 +++++++++ mDNSWindows/DNSSD.h | 1689 +++++++++ mDNSWindows/DNSSDDirect.c | 1863 ++++++++++ mDNSWindows/DNSSDDirect.h | 285 ++ mDNSWindows/DNSServices/DNSServiceDiscovery.c | 40 +- mDNSWindows/DNSServices/DNSServiceDiscovery.h | 13 +- mDNSWindows/DNSServices/DNSServices.c | 110 +- mDNSWindows/DNSServices/DNSServices.h | 16 +- mDNSWindows/DebugServices.c | 3101 +++++++++++++++++ mDNSWindows/DebugServices.h | 1633 +++++++++ mDNSWindows/Installer.vct | Bin 0 -> 40028 bytes mDNSWindows/README.txt | 57 +- mDNSWindows/RMxClient.c | 1302 +++++++ mDNSWindows/RMxClient.h | 307 ++ mDNSWindows/RMxCommon.c | 1501 ++++++++ mDNSWindows/RMxCommon.h | 662 ++++ mDNSWindows/RMxServer.c | 1656 +++++++++ mDNSWindows/RMxServer.h | 119 + mDNSWindows/mDNSWin32.c | 2129 +++++++---- mDNSWindows/mDNSWin32.h | 77 +- 226 files changed, 50996 insertions(+), 16392 deletions(-) create mode 100644 Clients/DNS-SD.xcode/project.pbxproj create mode 100644 Clients/DNSServiceBrowser-Info.plist delete mode 100644 Clients/DNSServiceReg.m delete mode 100644 Clients/DNSServiceReg.nib/classes.nib delete mode 100644 Clients/DNSServiceReg.nib/info.nib delete mode 100644 Clients/DNSServiceReg.nib/objects.nib create mode 100644 Clients/DNSServiceRegistration-Info.plist create mode 100644 Clients/Java/BrowserApp.java create mode 100644 Clients/Java/DNSSDUnitTest.java create mode 100644 Clients/Java/SimpleChat.java create mode 100644 Clients/Java/SwingBrowseListener.java create mode 100644 Clients/Java/SwingDomainListener.java create mode 100644 Clients/Java/SwingQueryListener.java create mode 100644 Clients/Java/SwingResolveListener.java create mode 100755 Clients/Makefile create mode 100644 Clients/ReadMe.txt create mode 100644 Clients/dns-sd.c create mode 100644 mDNSCore/DNSCommon.c create mode 100644 mDNSCore/DNSCommon.h create mode 100644 mDNSCore/DNSDigest.c delete mode 100755 mDNSCore/mDNSEmbeddedAPI.h delete mode 100755 mDNSCore/mDNSPlatformFunctions.h create mode 100755 mDNSCore/uDNS.c create mode 100755 mDNSCore/uDNS.h create mode 100644 mDNSMacOS9/Responder.c create mode 100644 mDNSMacOS9/Searcher.c create mode 100755 mDNSMacOS9/ShowInitIcon.c create mode 100755 mDNSMacOS9/ShowInitIcon.h create mode 100644 mDNSMacOS9/mDNSLibrary.c create mode 100644 mDNSMacOS9/mDNSLibraryLoader.c create mode 100644 mDNSMacOS9/mDNSLibraryResources.r create mode 100644 mDNSMacOS9/mDNSPrefix.h delete mode 100755 mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h delete mode 100755 mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m delete mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj delete mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings delete mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib delete mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib delete mode 100644 mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib delete mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj delete mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings delete mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/classes.nib delete mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/info.nib delete mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib delete mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.h delete mode 100644 mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m delete mode 100644 mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m delete mode 100644 mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj delete mode 100755 mDNSMacOSX/SampleUDSClient.c delete mode 100755 mDNSMacOSX/dnssd_clientstub.c delete mode 100644 mDNSMacOSX/dnssd_ipc.c delete mode 100644 mDNSMacOSX/dnssd_ipc.h delete mode 100644 mDNSMacOSX/mDNSMacOSX.c delete mode 100644 mDNSMacOSX/mDNSMacOSXPuma.c delete mode 100644 mDNSMacOSX/uds_daemon.c create mode 100644 mDNSPosix/PosixDaemon.c create mode 100644 mDNSPosix/mdnsd.sh create mode 100755 mDNSShared/GenLinkedList.c create mode 100755 mDNSShared/GenLinkedList.h create mode 100755 mDNSShared/dnssd_clientlib.c create mode 100644 mDNSShared/dnssd_clientshim.c create mode 100644 mDNSShared/mDNS.1 create mode 100644 mDNSShared/mDNSDebug.c create mode 100644 mDNSShared/mDNSResponder.8 create mode 100644 mDNSShared/uds_daemon.h create mode 100644 mDNSWindows/Applications/DLL/dllmain.c create mode 100644 mDNSWindows/Applications/DLL/dnssd.def create mode 100644 mDNSWindows/Applications/DLL/dnssd.vcproj create mode 100644 mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp create mode 100644 mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.h create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ClassFactory.cpp create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ClassFactory.h create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.cpp create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.h create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.cpp create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.h create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.cpp create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.def rename mDNSMacOS9/mDNSPrefixCarbon.h => mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.h (54%) create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.rc create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.sln create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.vcproj create mode 100644 mDNSWindows/Applications/ExplorerPlugin/LoginDialog.cpp create mode 100644 mDNSWindows/Applications/ExplorerPlugin/LoginDialog.h create mode 100644 mDNSWindows/Applications/ExplorerPlugin/ReadMe.txt create mode 100644 mDNSWindows/Applications/ExplorerPlugin/Resource.h rename mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h => mDNSWindows/Applications/ExplorerPlugin/StdAfx.cpp (74%) rename mDNSMacOS9/mDNSPrefixCarbonDebug.h => mDNSWindows/Applications/ExplorerPlugin/StdAfx.h (53%) create mode 100755 mDNSWindows/Applications/Java/makefile rename mDNSMacOSX/Applications/HAAutomounter/main.m => mDNSWindows/Applications/NSPTool/Prefix.h (67%) create mode 100644 mDNSWindows/Applications/NSPTool/Tool.c create mode 100644 mDNSWindows/Applications/NSPTool/Tool.mcp create mode 100644 mDNSWindows/Applications/SystemService/EventLogMessages.bin rename mDNSMacOS9/mDNSPrefixClassic.h => mDNSWindows/Applications/SystemService/Prefix.h (60%) rename mDNSMacOSX/Applications/DNSServiceRegistration/main.m => mDNSWindows/Applications/SystemService/Resource.h (74%) create mode 100644 mDNSWindows/Applications/SystemService/Service.c create mode 100644 mDNSWindows/Applications/SystemService/Service.mcp create mode 100644 mDNSWindows/Applications/SystemService/Service.rc create mode 100644 mDNSWindows/Applications/SystemService/Service2002.sln create mode 100644 mDNSWindows/Applications/SystemService/Service2002.vcproj rename mDNSMacOSX/Applications/DNSServiceBrowser/main.m => mDNSWindows/Applications/SystemServiceTest/Prefix.h (66%) create mode 100644 mDNSWindows/Applications/SystemServiceTest/Tool.c create mode 100644 mDNSWindows/Applications/SystemServiceTest/Tool.mcp create mode 100644 mDNSWindows/Applications/SystemServiceTest/Tool2002.sln create mode 100644 mDNSWindows/Applications/SystemServiceTest/Tool2002.vcproj create mode 100644 mDNSWindows/Applications/mdnsNSP/NSP.c create mode 100644 mDNSWindows/Applications/mdnsNSP/NSP.def create mode 100644 mDNSWindows/Applications/mdnsNSP/NSP.mcp create mode 100644 mDNSWindows/Applications/mdnsNSP/Prefix.h create mode 100644 mDNSWindows/Applications/mdnsNSP/ReadMe.txt create mode 100644 mDNSWindows/Applications/mdnsNSP/mdnsNSP.c create mode 100644 mDNSWindows/Applications/mdnsNSP/mdnsNSP.def create mode 100644 mDNSWindows/Applications/mdnsNSP/mdnsNSP.mcp create mode 100644 mDNSWindows/CommonServices.h create mode 100644 mDNSWindows/DNSSD.c create mode 100644 mDNSWindows/DNSSD.h create mode 100644 mDNSWindows/DNSSDDirect.c create mode 100644 mDNSWindows/DNSSDDirect.h create mode 100644 mDNSWindows/DebugServices.c create mode 100644 mDNSWindows/DebugServices.h create mode 100644 mDNSWindows/Installer.vct create mode 100644 mDNSWindows/RMxClient.c create mode 100644 mDNSWindows/RMxClient.h create mode 100644 mDNSWindows/RMxCommon.c create mode 100644 mDNSWindows/RMxCommon.h create mode 100644 mDNSWindows/RMxServer.c create mode 100644 mDNSWindows/RMxServer.h diff --git a/Clients/DNS-SD.xcode/project.pbxproj b/Clients/DNS-SD.xcode/project.pbxproj new file mode 100644 index 0000000..46805b9 --- /dev/null +++ b/Clients/DNS-SD.xcode/project.pbxproj @@ -0,0 +1,581 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 39; + objects = { + 014CEA490018CE3211CA2923 = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + OPTIMIZATION_CFLAGS = "-O0"; + ZERO_LINK = NO; + }; + isa = PBXBuildStyle; + name = Development; + }; + 014CEA4A0018CE3211CA2923 = { + buildRules = ( + ); + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + ZERO_LINK = NO; + }; + isa = PBXBuildStyle; + name = Deployment; + }; +//010 +//011 +//012 +//013 +//014 +//080 +//081 +//082 +//083 +//084 + 08FB7793FE84155DC02AAC07 = { + buildSettings = { + }; + buildStyles = ( + 014CEA490018CE3211CA2923, + 014CEA4A0018CE3211CA2923, + ); + hasScannedForEncodings = 1; + isa = PBXProject; + mainGroup = 08FB7794FE84155DC02AAC07; + projectDirPath = ""; + targets = ( + FFF520490671177900DA3D49, + 8DD76F740486A8DE00D96B5E, + FF1B691006711383002304DD, + FF1E351206711B5C003DD5BC, + ); + }; + 08FB7794FE84155DC02AAC07 = { + children = ( + 08FB7795FE84155DC02AAC07, + 08FB779DFE84155DC02AAC07, + 19C28FBDFE9D53C911CA2CBB, + ); + isa = PBXGroup; + name = mDNS; + refType = 4; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 = { + children = ( + 08FB7796FE84155DC02AAC07, + FF1B6914067114AF002304DD, + FF964DAB067115710099215A, + FF1E351B06711BCF003DD5BC, + FF1E352506711BD6003DD5BC, + ); + isa = PBXGroup; + name = Source; + refType = 4; + sourceTree = ""; + }; + 08FB7796FE84155DC02AAC07 = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + path = "dns-sd.c"; + refType = 4; + sourceTree = ""; + }; + 08FB779DFE84155DC02AAC07 = { + children = ( + FF964CA90671155C0099215A, + FF964AA00671153B0099215A, + ); + isa = PBXGroup; + name = Frameworks; + refType = 4; + sourceTree = ""; + }; +//080 +//081 +//082 +//083 +//084 +//190 +//191 +//192 +//193 +//194 + 19C28FBDFE9D53C911CA2CBB = { + children = ( + 8DD76F7E0486A8DE00D96B5E, + FF1B691106711383002304DD, + FF1E351306711B5C003DD5BC, + ); + isa = PBXGroup; + name = Products; + refType = 4; + sourceTree = ""; + }; +//190 +//191 +//192 +//193 +//194 +//8D0 +//8D1 +//8D2 +//8D3 +//8D4 + 8DD76F740486A8DE00D96B5E = { + buildPhases = ( + 8DD76F750486A8DE00D96B5E, + 8DD76F760486A8DE00D96B5E, + 8DD76F780486A8DE00D96B5E, + 8DD76F7A0486A8DE00D96B5E, + 8DD76F7B0486A8DE00D96B5E, + ); + buildRules = ( + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; + GCC_ENABLE_TRIGRAPHS = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = "$(HOME)/bin"; + LIBRARY_SEARCH_PATHS = ""; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "dns-sd"; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + ZERO_LINK = NO; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = "dns-sd"; + productInstallPath = "$(HOME)/bin"; + productName = mDNS; + productReference = 8DD76F7E0486A8DE00D96B5E; + productType = "com.apple.product-type.tool"; + }; + 8DD76F750486A8DE00D96B5E = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 8DD76F760486A8DE00D96B5E = { + buildActionMask = 2147483647; + files = ( + 8DD76F770486A8DE00D96B5E, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 8DD76F770486A8DE00D96B5E = { + fileRef = 08FB7796FE84155DC02AAC07; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + ); + }; + }; + 8DD76F780486A8DE00D96B5E = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 8DD76F7A0486A8DE00D96B5E = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 8DD76F7B0486A8DE00D96B5E = { + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + 8DD76F7E0486A8DE00D96B5E = { + explicitFileType = "compiled.mach-o.executable"; + includeInIndex = 0; + isa = PBXFileReference; + path = "dns-sd"; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; +//8D0 +//8D1 +//8D2 +//8D3 +//8D4 +//FF0 +//FF1 +//FF2 +//FF3 +//FF4 + FF1B690C06711383002304DD = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1B690D06711383002304DD = { + buildActionMask = 2147483647; + files = ( + FF964DAC067115710099215A, + ); + isa = PBXResourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1B690E06711383002304DD = { + buildActionMask = 2147483647; + files = ( + FF1B6915067114AF002304DD, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1B690F06711383002304DD = { + buildActionMask = 2147483647; + files = ( + FF964AA10671153B0099215A, + FF964CAA0671155C0099215A, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1B691006711383002304DD = { + buildPhases = ( + FF1B690C06711383002304DD, + FF1B690D06711383002304DD, + FF1B690E06711383002304DD, + FF1B690F06711383002304DD, + ); + buildRules = ( + ); + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + INFOPLIST_FILE = "DNSServiceBrowser-Info.plist"; + INSTALL_PATH = "$(USER_APPS_DIR)"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = "-framework Foundation -framework AppKit"; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "DNS Service Browser"; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = "DNS Service Browser"; + productName = "DNS Service Browser"; + productReference = FF1B691106711383002304DD; + productSettingsXML = " + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + DNS Service Browser + CFBundleGetInfoString + + CFBundleIconFile + + CFBundleIdentifier + com.apple.DNS_Service_Browser + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + + CFBundleSignature + ???? + CFBundleVersion + 1.0.0d1 + NSMainNibFile + DNSServiceBrowser + NSPrincipalClass + NSApplication + + +"; + productType = "com.apple.product-type.application"; + }; + FF1B691106711383002304DD = { + explicitFileType = wrapper.application; + includeInIndex = 0; + isa = PBXFileReference; + path = "DNS Service Browser.app"; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + FF1B6914067114AF002304DD = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.objc; + path = DNSServiceBrowser.m; + refType = 4; + sourceTree = ""; + }; + FF1B6915067114AF002304DD = { + fileRef = FF1B6914067114AF002304DD; + isa = PBXBuildFile; + settings = { + }; + }; + FF1E350E06711B5C003DD5BC = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1E350F06711B5C003DD5BC = { + buildActionMask = 2147483647; + files = ( + FF1E352606711BD6003DD5BC, + ); + isa = PBXResourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1E351006711B5C003DD5BC = { + buildActionMask = 2147483647; + files = ( + FF1E351C06711BCF003DD5BC, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1E351106711B5C003DD5BC = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1E351206711B5C003DD5BC = { + buildPhases = ( + FF1E350E06711B5C003DD5BC, + FF1E350F06711B5C003DD5BC, + FF1E351006711B5C003DD5BC, + FF1E351106711B5C003DD5BC, + ); + buildRules = ( + ); + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = NO; + INFOPLIST_FILE = "DNSServiceRegistration-Info.plist"; + INSTALL_PATH = "$(USER_APPS_DIR)"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = "-framework Foundation -framework AppKit"; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "DNS Service Registration"; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost"; + }; + dependencies = ( + ); + isa = PBXNativeTarget; + name = "DNS Service Registration"; + productName = "DNS Service Registration"; + productReference = FF1E351306711B5C003DD5BC; + productSettingsXML = " + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + DNS Service Registration + CFBundleGetInfoString + + CFBundleIconFile + + CFBundleIdentifier + com.apple.DNS_Service_Registration + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + + CFBundleSignature + ???? + CFBundleVersion + 1.0.0d1 + NSMainNibFile + DNSServiceRegistration + NSPrincipalClass + NSApplication + + +"; + productType = "com.apple.product-type.application"; + }; + FF1E351306711B5C003DD5BC = { + explicitFileType = wrapper.application; + includeInIndex = 0; + isa = PBXFileReference; + path = "DNS Service Registration.app"; + refType = 3; + sourceTree = BUILT_PRODUCTS_DIR; + }; + FF1E351706711B6A003DD5BC = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = FF1E351206711B5C003DD5BC; + remoteInfo = DNSServiceRegistration; + }; + FF1E351806711B6A003DD5BC = { + isa = PBXTargetDependency; + target = FF1E351206711B5C003DD5BC; + targetProxy = FF1E351706711B6A003DD5BC; + }; + FF1E351B06711BCF003DD5BC = { + fileEncoding = 4; + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.objc; + path = DNSServiceRegistration.m; + refType = 4; + sourceTree = ""; + }; + FF1E351C06711BCF003DD5BC = { + fileRef = FF1E351B06711BCF003DD5BC; + isa = PBXBuildFile; + settings = { + }; + }; + FF1E352506711BD6003DD5BC = { + isa = PBXFileReference; + lastKnownFileType = wrapper.nib; + path = DNSServiceRegistration.nib; + refType = 4; + sourceTree = ""; + }; + FF1E352606711BD6003DD5BC = { + fileRef = FF1E352506711BD6003DD5BC; + isa = PBXBuildFile; + settings = { + }; + }; + FF383825067117F300FEF615 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = 8DD76F740486A8DE00D96B5E; + remoteInfo = "dns-sd"; + }; + FF383826067117F300FEF615 = { + isa = PBXTargetDependency; + target = 8DD76F740486A8DE00D96B5E; + targetProxy = FF383825067117F300FEF615; + }; + FF383827067117F600FEF615 = { + containerPortal = 08FB7793FE84155DC02AAC07; + isa = PBXContainerItemProxy; + proxyType = 1; + remoteGlobalIDString = FF1B691006711383002304DD; + remoteInfo = "DNS Service Browser"; + }; + FF383828067117F600FEF615 = { + isa = PBXTargetDependency; + target = FF1B691006711383002304DD; + targetProxy = FF383827067117F600FEF615; + }; + FF964AA00671153B0099215A = { + isa = PBXFileReference; + lastKnownFileType = wrapper.framework; + name = Foundation.framework; + path = /System/Library/Frameworks/Foundation.framework; + refType = 0; + sourceTree = ""; + }; + FF964AA10671153B0099215A = { + fileRef = FF964AA00671153B0099215A; + isa = PBXBuildFile; + settings = { + }; + }; + FF964CA90671155C0099215A = { + isa = PBXFileReference; + lastKnownFileType = wrapper.framework; + name = AppKit.framework; + path = /System/Library/Frameworks/AppKit.framework; + refType = 0; + sourceTree = ""; + }; + FF964CAA0671155C0099215A = { + fileRef = FF964CA90671155C0099215A; + isa = PBXBuildFile; + settings = { + }; + }; + FF964DAB067115710099215A = { + isa = PBXFileReference; + lastKnownFileType = wrapper.nib; + path = DNSServiceBrowser.nib; + refType = 4; + sourceTree = ""; + }; + FF964DAC067115710099215A = { + fileRef = FF964DAB067115710099215A; + isa = PBXBuildFile; + settings = { + }; + }; + FFF520490671177900DA3D49 = { + buildPhases = ( + ); + buildSettings = { + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "Build All"; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + FF383826067117F300FEF615, + FF383828067117F600FEF615, + FF1E351806711B6A003DD5BC, + ); + isa = PBXAggregateTarget; + name = "Build All"; + productName = "Build All"; + }; + }; + rootObject = 08FB7793FE84155DC02AAC07; +} diff --git a/Clients/DNSServiceBrowser-Info.plist b/Clients/DNSServiceBrowser-Info.plist new file mode 100644 index 0000000..9ccdde6 --- /dev/null +++ b/Clients/DNSServiceBrowser-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + DNS Service Browser + CFBundleGetInfoString + + CFBundleIconFile + + CFBundleIdentifier + com.apple.DNS_Service_Browser + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + + CFBundleSignature + ???? + CFBundleVersion + 1.0.0d1 + NSMainNibFile + DNSServiceBrowser + NSPrincipalClass + NSApplication + + diff --git a/Clients/DNSServiceBrowser.m b/Clients/DNSServiceBrowser.m index c11cb68..f2a38c8 100755 --- a/Clients/DNSServiceBrowser.m +++ b/Clients/DNSServiceBrowser.m @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,71 +25,205 @@ Change History (most recent first): $Log: DNSServiceBrowser.m,v $ +Revision 1.29 2004/06/04 20:58:36 cheshire +Move DNSServiceBrowser from mDNSMacOSX directory to Clients directory + +Revision 1.28 2004/05/18 23:51:26 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.27 2003/11/19 18:49:48 rpantos +: couple of little tweaks to previous checkin + +Revision 1.26 2003/11/07 19:35:20 rpantos +/6: Display multiple IP addresses. Connect using host rather than IP addr. + +Revision 1.25 2003/10/29 05:16:54 rpantos +Checkpoint: transition from DNSServiceDiscovery.h to dns_sd.h + +Revision 1.24 2003/10/28 02:25:45 rpantos +/9,10: Cancel pending resolve when focus changes or service disappears. + +Revision 1.23 2003/10/28 01:29:15 rpantos +/4,5: Restructure a bit to make arrow keys work & views behave better. + +Revision 1.22 2003/10/28 01:23:27 rpantos +/11: Bail if mDNS cannot be initialized at startup. + +Revision 1.21 2003/10/28 01:19:45 rpantos +/3,11: Do not put a trailing '.' on service names. Handle PATH for HTTP txtRecords. + +Revision 1.20 2003/10/28 01:13:49 rpantos +/2: Remove filter when displaying browse results. + +Revision 1.19 2003/10/28 01:10:14 rpantos +/1: Change 'compare' to 'caseInsensitiveCompare' to fix sort order. + Revision 1.18 2003/08/12 19:55:07 cheshire Update to APSL 2.0 */ -#import "BrowserController.h" +#include +#include +#include +#include -#include "arpa/inet.h" +#import +#import -void -MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info ) +#include +#include "dns_sd.h" + +@class ServiceController; // holds state corresponding to outstanding DNSServiceRef + +@interface BrowserController : NSObject { - DNSServiceDiscovery_handleReply(msg); + 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 hostField; + IBOutlet id ipAddressField; + IBOutlet id ip6AddressField; + IBOutlet id portField; + IBOutlet id textField; + + NSMutableArray *srvtypeKeys; + NSMutableArray *srvnameKeys; + NSMutableArray *domainKeys; + NSMutableArray *nameKeys; + NSString *Domain; + NSString *SrvType; + NSString *SrvName; + NSString *Name; + + ServiceController *fDomainBrowser; + ServiceController *fServiceBrowser; + ServiceController *fServiceResolver; + ServiceController *fAddressResolver; + } -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 - ) +- (IBAction)handleDomainClick:(id)sender; +- (IBAction)handleNameClick:(id)sender; +- (IBAction)handleTypeClick:(id)sender; +- (void)notifyTypeSelectionChange:(NSNotification*)note; +- (void)notifyNameSelectionChange:(NSNotification*)note; + +- (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:(DNSServiceFlags)flags name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain; +- (void)updateEnumWithResult:(DNSServiceFlags)flags domain:(NSString *)domain; +- (void)resolveClientWitHost:(NSString *)host port:(uint16_t)port interfaceIndex:(uint32_t)interface txtRecord:(const char*)txtRecord txtLen:(uint16_t)txtLen; +- (void)updateAddress:(uint16_t)rrtype addr:(const void *)buff addrLen:(uint16_t)addrLen + host:(const char*) host interfaceIndex:(uint32_t)interface more:(boolean_t)moreToCome; + +- (void)_cancelPendingResolve; +- (void)_clearResolvedInfo; + +@end + +// The ServiceController manages cleanup of DNSServiceRef & runloop info for an outstanding request +@interface ServiceController : NSObject { - [[NSApp delegate] updateBrowseWithResult:resultType name:[NSString stringWithUTF8String:replyName] type:[NSString stringWithUTF8String:replyType] domain:[NSString stringWithUTF8String:replyDomain] flags:flags]; - return; + DNSServiceRef fServiceRef; + CFSocketRef fSocketRef; + CFRunLoopSourceRef fRunloopSrc; } -void enum_reply ( - DNSServiceDomainEnumerationReplyResultType resultType, - const char *replyDomain, - DNSServiceDiscoveryReplyFlags flags, - void *context - ) +- (id) initWithServiceRef:(DNSServiceRef) ref; +- (boolean_t) addToCurrentRunLoop; +- (DNSServiceRef) serviceRef; +- (void) dealloc; + +@end // interface ServiceController + + +static void ProcessSockData( CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) +// CFRunloop callback that notifies dns_sd when new data appears on a DNSServiceRef's socket. { - [[NSApp delegate] updateEnumWithResult:resultType domain:[NSString stringWithUTF8String:replyDomain] flags:flags]; + DNSServiceRef serviceRef = (DNSServiceRef) info; + DNSServiceErrorType err = DNSServiceProcessResult( serviceRef); + if ( err != kDNSServiceErr_NoError) + printf( "DNSServiceProcessResult() returned an error! %d\n", err); +} - return; +static void DomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context ) +// Report newly-discovered domains to the BrowserController. +{ + if ( errorCode == kDNSServiceErr_NoError) { + BrowserController *pSelf = (BrowserController*) context; + [pSelf updateEnumWithResult:flags domain:[NSString stringWithUTF8String:replyDomain]]; + } else { + printf( "DomainEnumReply got an error! %d\n", errorCode); + } } -void resolve_reply ( - struct sockaddr *interface, - struct sockaddr *address, - const char *txtRecord, - DNSServiceDiscoveryReplyFlags flags, - void *context - ) +static void ServiceBrowseReply( DNSServiceRef sdRef, DNSServiceFlags servFlags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, + const char *serviceName, const char *regtype, const char *replyDomain, void *context ) +// Report newly-discovered services to the BrowserController. { - [[NSApp delegate] resolveClientWithInterface:interface address:address txtRecord:[NSString stringWithUTF8String:txtRecord]]; + if ( errorCode == kDNSServiceErr_NoError) { + BrowserController *pSelf = (BrowserController*) context; + [pSelf updateBrowseWithResult:servFlags name:[NSString stringWithUTF8String:serviceName] + type:[NSString stringWithUTF8String:regtype] domain:[NSString stringWithUTF8String:replyDomain]]; + } else { + printf( "ServiceBrowseReply got an error! %d\n", errorCode); + } +} - return; +static void ServiceResolveReply( 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 ) +// Pass along resolved service info to the BrowserController. +{ + if ( errorCode == kDNSServiceErr_NoError) { + BrowserController *pSelf = (BrowserController*) context; + [pSelf resolveClientWitHost:[NSString stringWithUTF8String:hosttarget] port:port interfaceIndex:interfaceIndex txtRecord:txtRecord txtLen:txtLen]; + } else { + printf( "ServiceResolveReply got an error! %d\n", errorCode); + } +} + +static void QueryRecordReply( 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 callback used to look up IP addresses. +{ + BrowserController *pBrowser = (BrowserController*) context; + + [pBrowser updateAddress:rrtype addr:rdata addrLen:rdlen host:fullname interfaceIndex:interfaceIndex + more:((flags & kDNSServiceFlagsMoreComing) != 0)]; } + @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 *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)", @@ -106,7 +242,10 @@ void resolve_reply ( { [self registerDefaults]; - browse_client = nil; + fDomainBrowser = nil; + fServiceBrowser = nil; + fServiceResolver = nil; + fAddressResolver = nil; return [super init]; } @@ -126,27 +265,29 @@ void resolve_reply ( [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]; + [typeField sizeLastColumnToFit]; //Set column sizes to use their whole table's width. [nameField sizeLastColumnToFit]; - [domainField setDataSource:self]; [domainField sizeLastColumnToFit]; +// (self is specified as the NSTableViews' data source in the nib) [nameField setDoubleAction:@selector(connect:)]; - //[srvtypeKeys addObject:@"_ftp._tcp."]; //Add supported protocols and domains to their + // Listen for table selection changes + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyTypeSelectionChange:) + name:NSTableViewSelectionDidChangeNotification object:typeField]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyNameSelectionChange:) + name:NSTableViewSelectionDidChangeNotification object:nameField]; + + //[srvtypeKeys addObject:@"_ftp._tcp"]; //Add supported protocols and domains to their //[srvnameKeys addObject:@"File Transfer (ftp)"]; - //[srvtypeKeys addObject:@"_printer._tcp."]; //respective arrays + //[srvtypeKeys addObject:@"_printer._tcp"]; //respective arrays //[srvnameKeys addObject:@"Printer (lpr)"]; - //[srvtypeKeys addObject:@"_http._tcp."]; //respective arrays + //[srvtypeKeys addObject:@"_http._tcp"]; //respective arrays //[srvnameKeys addObject:@"Web Server (http)"]; - //[srvtypeKeys addObject:@"_afp._tcp."]; //respective arrays + //[srvtypeKeys addObject:@"_afp._tcp"]; //respective arrays //[srvnameKeys addObject:@"AppleShare Server (afp)"]; - [ipAddressField setStringValue:@""]; - [portField setStringValue:@""]; - [textField setStringValue:@""]; + [self _clearResolvedInfo]; [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]]; [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]]; @@ -205,7 +346,7 @@ void resolve_reply ( } if (theTableView == nameField) { - return [[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:rowIndex]; + return [[nameKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:rowIndex]; } if (theTableView == serviceDisplayTable) { @@ -223,115 +364,81 @@ void resolve_reply ( - (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 + // 3282283: No longer used - update happens in notifyTypeSelectionChange } + - (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:@""]; + [self _cancelPendingResolve]; 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 + // 3282283: No longer used - update happens in notifyNameSelectionChange +} + +- (void)notifyTypeSelectionChange:(NSNotification*)note +/* Called when the selection of the Type table changes */ +{ + int index=[[note object] selectedRow]; //Find index of selected row if (index==-1) return; //Error checking - Name=[[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:index]; //Save desired name + SrvType = [srvtypeKeys objectAtIndex:index]; //Save desired Type + SrvName = [srvnameKeys objectAtIndex:index]; //Save desired Type - { - 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; - } - } + [self _cancelPendingResolve]; + + [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records +} + +- (void)notifyNameSelectionChange:(NSNotification*)note +/* Called when the selection of the Name table changes */ +{ + int index=[[note object] selectedRow]; //Find index of selected row + + [self _cancelPendingResolve]; // Cancel any pending Resolve for any table selection change + + if (index==-1) { + Name = nil; // Name may no longer point to a list member + return; + } + Name=[[nameKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:index]; //Save desired name + + [self _clearResolvedInfo]; + + DNSServiceRef serviceRef; + DNSServiceErrorType err; + err = DNSServiceResolve ( &serviceRef, (DNSServiceFlags) 0, 0, (char *)[Name UTF8String], (char *)[SrvType UTF8String], + (char *)(Domain?[Domain UTF8String]:""), ServiceResolveReply, self); + if ( kDNSServiceErr_NoError == err) { + fServiceResolver = [[ServiceController alloc] initWithServiceRef:serviceRef]; + [fServiceResolver addToCurrentRunLoop]; + } } - (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; + DNSServiceErrorType err; + DNSServiceRef serviceRef; + + err = DNSServiceEnumerateDomains( &serviceRef, kDNSServiceFlagsBrowseDomains, 0, DomainEnumReply, self); + if ( kDNSServiceErr_NoError == err) { + fDomainBrowser = [[ServiceController alloc] initWithServiceRef:serviceRef]; + [fDomainBrowser addToCurrentRunLoop]; + } + else { + NSAlert *alert = [NSAlert alertWithMessageText:@"Could not connect to mDNSResponder!" + defaultButton:@"Quit" alternateButton:nil otherButton:nil informativeTextWithFormat: + @"Check to see if mDNSResponder is still running."]; + if ( alert != NULL) + [alert runModal]; + exit( err); } } @@ -340,6 +447,8 @@ void resolve_reply ( const char * DomainC; const char * TypeC=[theType UTF8String]; //Type in C string format + DNSServiceErrorType err = kDNSServiceErr_NoError; + if (theDomain) { DomainC = [theDomain UTF8String]; //Domain in C string format } else { @@ -350,49 +459,18 @@ void resolve_reply ( [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; + if ( fServiceBrowser != nil) { + [fServiceBrowser release]; + fServiceBrowser = 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; - } - } - + DNSServiceRef serviceRef; + err = DNSServiceBrowse( &serviceRef, (DNSServiceFlags) 0, 0, TypeC, DomainC, ServiceBrowseReply, self); + if ( kDNSServiceErr_NoError == err) { + fServiceBrowser = [[ServiceController alloc] initWithServiceRef:serviceRef]; + [fServiceBrowser addToCurrentRunLoop]; + } } @@ -407,13 +485,12 @@ void resolve_reply ( return YES; } -- (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags +- (void)updateEnumWithResult:(DNSServiceFlags)flags domain:(NSString *)domain { - // new domain received - if (DNSServiceDomainEnumerationReplyAddDomain == resultType || DNSServiceDomainEnumerationReplyAddDomainDefault == resultType) { + if ( ( flags & kDNSServiceFlagsAdd) != 0) { // new domain received // add the domain to the list [domainKeys addObject:domain]; - } else { + } else if (!(flags & kDNSServiceFlagsAdd)) { // remove the domain from the list NSEnumerator *dmnEnum = [domainKeys objectEnumerator]; NSString *aDomain = nil; @@ -427,70 +504,165 @@ void resolve_reply ( } // update the domain table [domainField reloadData]; + + // Terminate the enumeration once the last domain is delivered. + if ( ( flags & kDNSServiceFlagsMoreComing) == 0) { + [fDomainBrowser release]; + fDomainBrowser = nil; + } + + // At some point, we may want to support a TableView for domain browsing. For now, just pick first domain that comes up. + if ( Domain == nil) + Domain = [domain retain]; + return; } -- (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags +- (void)updateBrowseWithResult:(DNSServiceFlags)flags name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain { //NSLog(@"Received result %@ %@ %@ %d", name, resulttype, domain, type); - if (([domain isEqualToString:Domain] || [domain isEqualToString:@"local."]) && [resulttype isEqualToString:SrvType]) { + if (!(flags & kDNSServiceFlagsAdd)) { + if ([nameKeys containsObject:name]) { + [nameKeys removeObject:name]; - if (type == DNSServiceBrowserReplyRemoveInstance) { - if ([nameKeys containsObject:name]) { - [nameKeys removeObject:name]; - } + // 3282283: Cancel pending browse if object goes away. + if ( [name isEqualToString:Name]) + [nameField deselectAll:self]; } - if (type == DNSServiceBrowserReplyAddInstance) { - if (![nameKeys containsObject:name]) { - [nameKeys addObject:name]; - } + } + else if ( ( flags & kDNSServiceFlagsAdd) != 0) { + 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]; } + + // If not expecting any more data, then reload (redraw) Name TableView with newly found data + if ((flags & kDNSServiceFlagsMoreComing) == 0) + [nameField reloadData]; return; } -- (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord +- (void)resolveClientWitHost:(NSString *)host port:(uint16_t)port interfaceIndex:(uint32_t)interface + txtRecord:(const char*)txtRecord txtLen:(uint16_t)txtLen +/* Display resolved information about the selected service. */ { - 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]; + DNSServiceErrorType err; + DNSServiceRef serviceRef; + + // Start an async lookup for IPv4 & IPv6 addresses + if ( fAddressResolver != nil) { + [fAddressResolver release]; + fAddressResolver = nil; + } + err = DNSServiceQueryRecord( &serviceRef, (DNSServiceFlags) 0, interface, [host UTF8String], + ns_t_a, ns_c_in, QueryRecordReply, self); + if ( err == kDNSServiceErr_NoError) { + fAddressResolver = [[ServiceController alloc] initWithServiceRef:serviceRef]; + [fAddressResolver addToCurrentRunLoop]; + } + + [hostField setStringValue:host]; [portField setIntValue:port]; - [textField setStringValue:txtRecord]; - return; + // kind of a hack: munge txtRecord so it's human-readable + if ( txtLen > 0) { + char *readableText = (char*) malloc( txtLen); + if ( readableText != nil) { + ByteCount index, subStrLen; + memcpy( readableText, txtRecord, txtLen); + for ( index=0; index < txtLen - 1; index += subStrLen + 1) { + subStrLen = readableText[ index]; + readableText[ index] = '\n'; + } + [textField setStringValue:[NSString stringWithCString:&readableText[1] length:txtLen - 1]]; + free( readableText); + } + } +} + +- (void)updateAddress:(uint16_t)rrtype addr:(const void *)buff addrLen:(uint16_t)addrLen + host:(const char*) host interfaceIndex:(uint32_t)interface more:(boolean_t)moreToCome +/* Update address field(s) with info obtained by fAddressResolver. */ +{ + if ( rrtype == ns_t_a) { // IPv4 + char addrBuff[256]; + inet_ntop( AF_INET, buff, addrBuff, sizeof addrBuff); + strcat( addrBuff, " "); + [ipAddressField setStringValue:[NSString stringWithFormat:@"%@%s", [ipAddressField stringValue], addrBuff]]; + + if ( !moreToCome) { + [fAddressResolver release]; + fAddressResolver = nil; + + // After we find v4 we look for v6 + DNSServiceRef serviceRef; + DNSServiceErrorType err; + err = DNSServiceQueryRecord( &serviceRef, (DNSServiceFlags) 0, interface, host, + ns_t_aaaa, ns_c_in, QueryRecordReply, self); + if ( err == kDNSServiceErr_NoError) { + fAddressResolver = [[ServiceController alloc] initWithServiceRef:serviceRef]; + [fAddressResolver addToCurrentRunLoop]; + } + } + } + else if ( rrtype == ns_t_aaaa) // IPv6 + { + char addrBuff[256]; + inet_ntop( AF_INET6, buff, addrBuff, sizeof addrBuff); + strcat( addrBuff, " "); + [ip6AddressField setStringValue:[NSString stringWithFormat:@"%@%s", [ip6AddressField stringValue], addrBuff]]; + + if ( !moreToCome) { + [fAddressResolver release]; + fAddressResolver = nil; + } + } } + - (void)connect:(id)sender { - NSString *ipAddr = [ipAddressField stringValue]; + NSString *host = [hostField stringValue]; int port = [portField intValue]; NSString *txtRecord = [textField stringValue]; if (!txtRecord) txtRecord = @""; - if (!ipAddr || !port) return; + if (!host || !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]]]; + if ([SrvType isEqualToString:@"_http._tcp"]) + { + NSString *pathDelim = @"path="; + NSRange where; + + // If the TXT record specifies a path, extract it. + where = [txtRecord rangeOfString:pathDelim options:NSCaseInsensitiveSearch]; + if ( where.length) + { + NSRange targetRange = { where.location + where.length, [txtRecord length] - where.location - where.length }; + NSRange endDelim = [txtRecord rangeOfString:@"\n" options:kNilOptions range:targetRange]; + + if ( endDelim.length) // if a delimiter was found, truncate the target range + targetRange.length = endDelim.location - targetRange.location; + + NSString *path = [txtRecord substringWithRange:targetRange]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d%@", host, port, path]]]; + } + else + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d", host, port]]]; + } + else if ([SrvType isEqualToString:@"_ftp._tcp"]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@:%d/", host, port]]]; + else if ([SrvType isEqualToString:@"_tftp._tcp"]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tftp://%@:%d/", host, port]]]; + else if ([SrvType isEqualToString:@"_ssh._tcp"]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ssh://%@:%d/", host, port]]]; + else if ([SrvType isEqualToString:@"_telnet._tcp"]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"telnet://%@:%d/", host, port]]]; + else if ([SrvType isEqualToString:@"_printer._tcp"]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"lpr://%@:%d/", host, port]]]; + else if ([SrvType isEqualToString:@"_ipp._tcp"]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ipp://%@:%d/", host, port]]]; + else if ([SrvType isEqualToString:@"_afpovertcp._tcp"]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%d/", host, port]]]; + else if ([SrvType isEqualToString:@"_smb._tcp"]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"smb://%@:%d/", host, port]]]; return; } @@ -522,10 +694,16 @@ void resolve_reply ( - (IBAction)addNewService:(id)sender { // add new entries from the edit fields to the arrays for the defaults + NSString *newType = [serviceTypeField stringValue]; + NSString *newName = [serviceNameField stringValue]; + + // 3282283: trim trailing '.' from service type field + if ([newType length] && [newType hasSuffix:@"."]) + newType = [newType substringToIndex:[newType length] - 1]; - if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length]) { - [srvtypeKeys addObject:[serviceTypeField stringValue]]; - [srvnameKeys addObject:[serviceNameField stringValue]]; + if ([newType length] && [newName length]) { + [srvtypeKeys addObject:newType]; + [srvnameKeys addObject:newName]; [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; @@ -533,9 +711,94 @@ void resolve_reply ( [typeField reloadData]; [serviceDisplayTable reloadData]; } +} + +- (void)_cancelPendingResolve +// If there a a Resolve outstanding, cancel it. +{ + if ( fAddressResolver != nil) { + [fAddressResolver release]; + fAddressResolver = nil; + } + + if ( fServiceResolver != nil) { + [fServiceResolver release]; + fServiceResolver = nil; + } + + [self _clearResolvedInfo]; +} + +- (void)_clearResolvedInfo +// Erase the display of resolved info. +{ + [hostField setStringValue:@""]; + [ipAddressField setStringValue:@""]; + [ip6AddressField setStringValue:@""]; + [portField setStringValue:@""]; + [textField setStringValue:@""]; +} + +@end // implementation BrowserController + + +@implementation ServiceController : NSObject +{ + DNSServiceRef fServiceRef; + CFSocketRef fSocketRef; + CFRunLoopSourceRef fRunloopSrc; +} +- (id) initWithServiceRef:(DNSServiceRef) ref +{ + [super init]; + fServiceRef = ref; + return self; +} + +- (boolean_t) addToCurrentRunLoop +/* Add the service to the current runloop. Returns non-zero on success. */ +{ + CFSocketContext ctx = { 1, (void*) fServiceRef, nil, nil, nil }; + + fSocketRef = CFSocketCreateWithNative( kCFAllocatorDefault, DNSServiceRefSockFD( fServiceRef), + kCFSocketReadCallBack, ProcessSockData, &ctx); + if ( fSocketRef != nil) + fRunloopSrc = CFSocketCreateRunLoopSource( kCFAllocatorDefault, fSocketRef, 1); + if ( fRunloopSrc != nil) + CFRunLoopAddSource( CFRunLoopGetCurrent(), fRunloopSrc, kCFRunLoopDefaultMode); + else + printf("Could not listen to runloop socket\n"); + + return fRunloopSrc != nil; +} + +- (DNSServiceRef) serviceRef +{ + return fServiceRef; } +- (void) dealloc +/* Remove service from runloop, deallocate service and associated resources */ +{ + if ( fSocketRef != nil) { + CFSocketInvalidate( fSocketRef); + CFRelease( fSocketRef); + } + + if ( fRunloopSrc != nil) { + CFRunLoopRemoveSource( CFRunLoopGetCurrent(), fRunloopSrc, kCFRunLoopDefaultMode); + CFRelease( fRunloopSrc); + } + DNSServiceRefDeallocate( fServiceRef); + + [super dealloc]; +} -@end \ No newline at end of file +@end // implementation ServiceController + +int main(int argc, const char *argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/Clients/DNSServiceBrowser.nib/classes.nib b/Clients/DNSServiceBrowser.nib/classes.nib index 2668d5d..f5d5e98 100644 --- a/Clients/DNSServiceBrowser.nib/classes.nib +++ b/Clients/DNSServiceBrowser.nib/classes.nib @@ -4,7 +4,6 @@ ACTIONS = { addNewService = id; connect = id; - editDomains = id; handleDomainClick = id; handleNameClick = id; handleTableClick = id; @@ -15,9 +14,9 @@ CLASS = BrowserController; LANGUAGE = ObjC; OUTLETS = { - domainEditField = id; domainField = id; - domainWindow = id; + hostField = id; + ip6AddressField = id; ipAddressField = id; nameColumn = id; nameField = id; diff --git a/Clients/DNSServiceBrowser.nib/info.nib b/Clients/DNSServiceBrowser.nib/info.nib index e31cf4c..ffdf970 100644 --- a/Clients/DNSServiceBrowser.nib/info.nib +++ b/Clients/DNSServiceBrowser.nib/info.nib @@ -3,20 +3,20 @@ IBDocumentLocation - 14 102 356 240 0 0 1152 746 + 257 25 522 680 0 0 1280 1002 IBEditorPositions 29 22 474 271 44 0 0 1152 746 IBFramework Version - 273.0 + 349.0 IBOpenObjects 220 201 IBSystem Version - 6C35 + 7B85 diff --git a/Clients/DNSServiceBrowser.nib/objects.nib b/Clients/DNSServiceBrowser.nib/objects.nib index b330188906e31390fc577041682f1d3332a25082..3cebce38cee6e21bf38eb5ad455335fa409a594c 100644 GIT binary patch literal 10423 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 diff --git a/Clients/DNSServiceReg.m b/Clients/DNSServiceReg.m deleted file mode 100644 index 7efd864..0000000 --- a/Clients/DNSServiceReg.m +++ /dev/null @@ -1,247 +0,0 @@ -/* - * 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: DNSServiceReg.m,v $ -Revision 1.13 2003/08/12 19:55:07 cheshire -Update to APSL 2.0 - - */ - -#import "RegistrationController.h" - -#include - -void reg_reply ( - int errorCode, - void *context - ) -{ - // registration reply - printf("Got a reply from the server with error %d\n", errorCode); - return; -} - -void -MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info ) -{ - DNSServiceDiscovery_handleReply(msg); -} - -@implementation RegistrationController - -- (void)registerDefaults -{ - NSMutableDictionary *regDict = [NSMutableDictionary dictionary]; - - NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_ssh._tcp.", @"_tftp._tcp.", @"_http._tcp.", @"_printer._tcp.", @"_afpovertcp._tcp.", nil]; - NSArray *nameArray = [NSArray arrayWithObjects:@"My ftp Server", @"My Computer", @"Testing Boot Image", @"A Web Server", @"SteveÕs Printer", @"Company AppleShare Server", nil]; - NSArray *portArray = [NSArray arrayWithObjects:@"21", @"22", @"69", @"80", @"515", @"548", nil]; - NSArray *domainArray = [NSArray arrayWithObjects:@"", @"", @"", @"", @"", @"", nil]; - NSArray *textArray = [NSArray arrayWithObjects:@"", @"", @"image=mybootimage", @"path=/index.html", @"rn=lpt1", @"Vol=Public", nil]; - - [regDict setObject:typeArray forKey:@"SrvTypeKeys"]; - [regDict setObject:nameArray forKey:@"SrvNameKeys"]; - [regDict setObject:portArray forKey:@"SrvPortKeys"]; - [regDict setObject:domainArray forKey:@"SrvDomainKeys"]; - [regDict setObject:textArray forKey:@"SrvTextKeys"]; - - [[NSUserDefaults standardUserDefaults] registerDefaults:regDict]; -} - -- (id)init -{ - srvtypeKeys = [[NSMutableArray array] retain]; //Define arrays for Type, Domain, and Name - srvnameKeys = [[NSMutableArray array] retain]; - srvportKeys = [[NSMutableArray array] retain]; - srvdomainKeys = [[NSMutableArray array] retain]; - srvtextKeys = [[NSMutableArray array] retain]; - - registeredDict = [[NSMutableDictionary alloc] init]; - - [self registerDefaults]; - return [super init]; -} - -- (void)awakeFromNib //BrowserController startup procedure -{ - [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]]; - [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]]; - [srvportKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvPortKeys"]]; - [srvdomainKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvDomainKeys"]]; - [srvtextKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTextKeys"]]; - - [serviceDisplayTable reloadData]; //Reload (redraw) data in fields - -} - - - - - (IBAction)registerService:(id)sender -{ - int selectedRow = [serviceDisplayTable selectedRow]; - CFRunLoopSourceRef rls; - uint16_t registerPort; - CFMachPortRef cfMachPort; - CFMachPortContext context; - Boolean shouldFreeInfo; - dns_service_discovery_ref dns_client; - mach_port_t port; - - if (selectedRow < 0) { - return; - } - - context.version = 1; - context.info = 0; - context.retain = NULL; - context.release = NULL; - context.copyDescription = NULL; - - registerPort = [[srvportKeys objectAtIndex:selectedRow] intValue]; - - dns_client = DNSServiceRegistrationCreate - ( - [[srvnameKeys objectAtIndex:selectedRow] UTF8String], - [[srvtypeKeys objectAtIndex:selectedRow] UTF8String], - [[srvdomainKeys objectAtIndex:selectedRow] UTF8String], - registerPort, - [[srvtextKeys objectAtIndex:selectedRow] UTF8String], - reg_reply, - nil - ); - - port = DNSServiceDiscoveryMachPort(dns_client); - - if (port) { - - //printf("port is %d\n", port); - - cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo ); - - rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFRelease(rls); - [registeredDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)dns_client] forKey:[srvtypeKeys objectAtIndex:selectedRow]]; - } else { - printf("Could not obtain client port\n"); - } - -} - -- (IBAction)unregisterService:(id)sender -{ - int selectedRow = [serviceDisplayTable selectedRow]; - NSString *key = [srvtypeKeys objectAtIndex:selectedRow]; - - NSNumber *refPtr = [registeredDict objectForKey:key]; - dns_service_discovery_ref ref = (dns_service_discovery_ref)[refPtr unsignedIntValue]; - - if (ref) { - DNSServiceDiscoveryDeallocate(ref); - [registeredDict removeObjectForKey:key]; - } -} - --(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row -{ - if (row<0) return; -} - -- (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods -{ - return [srvtypeKeys count]; -} - -- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex -{ - if (theColumn == typeColumn) { - return [srvtypeKeys objectAtIndex:rowIndex]; - } - if (theColumn == nameColumn) { - return [srvnameKeys objectAtIndex:rowIndex]; - } - if (theColumn == portColumn) { - return [srvportKeys objectAtIndex:rowIndex]; - } - if (theColumn == domainColumn) { - return [srvdomainKeys objectAtIndex:rowIndex]; - } - if (theColumn == textColumn) { - return [srvtextKeys objectAtIndex:rowIndex]; - } - - return(0); -} //End of mandatory TableView methods - -- (IBAction)removeSelected:(id)sender -{ - // remove the selected row and force a refresh - - int selectedRow = [serviceDisplayTable selectedRow]; - - if (selectedRow) { - - [srvtypeKeys removeObjectAtIndex:selectedRow]; - [srvnameKeys removeObjectAtIndex:selectedRow]; - [srvportKeys removeObjectAtIndex:selectedRow]; - [srvdomainKeys removeObjectAtIndex:selectedRow]; - [srvtextKeys removeObjectAtIndex:selectedRow]; - - [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; - - [serviceDisplayTable reloadData]; - } -} - -- (IBAction)addNewService:(id)sender -{ - // add new entries from the edit fields to the arrays for the defaults - - if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length] && [[serviceDomainField stringValue] length]&& [[servicePortField stringValue] length]) { - [srvtypeKeys addObject:[serviceTypeField stringValue]]; - [srvnameKeys addObject:[serviceNameField stringValue]]; - [srvportKeys addObject:[servicePortField stringValue]]; - [srvdomainKeys addObject:[serviceDomainField stringValue]]; - [srvtextKeys addObject:[serviceTextField stringValue]]; - - [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; - - [serviceDisplayTable reloadData]; - } else { - NSBeep(); - } - -} - - - -@end diff --git a/Clients/DNSServiceReg.nib/classes.nib b/Clients/DNSServiceReg.nib/classes.nib deleted file mode 100644 index 46f466c..0000000 --- a/Clients/DNSServiceReg.nib/classes.nib +++ /dev/null @@ -1,30 +0,0 @@ -{ - 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/Clients/DNSServiceReg.nib/info.nib b/Clients/DNSServiceReg.nib/info.nib deleted file mode 100644 index 9d2eb74..0000000 --- a/Clients/DNSServiceReg.nib/info.nib +++ /dev/null @@ -1,22 +0,0 @@ - - - - - IBDocumentLocation - 32 111 356 240 0 0 1152 746 - IBEditorPositions - - 29 - 103 609 252 44 0 0 1152 746 - - IBFramework Version - 273.0 - IBOpenObjects - - 243 - 21 - - IBSystem Version - 6C30 - - diff --git a/Clients/DNSServiceReg.nib/objects.nib b/Clients/DNSServiceReg.nib/objects.nib deleted file mode 100644 index 705e77d75023c87108c7ca36a155b098c343ca3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9113 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 + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + DNS Service Registration + CFBundleGetInfoString + + CFBundleIconFile + + CFBundleIdentifier + com.apple.DNS_Service_Registration + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + + CFBundleSignature + ???? + CFBundleVersion + 1.0.0d1 + NSMainNibFile + DNSServiceRegistration + NSPrincipalClass + NSApplication + + diff --git a/Clients/DNSServiceRegistration.m b/Clients/DNSServiceRegistration.m index d58de3c..3b50a07 100644 --- a/Clients/DNSServiceRegistration.m +++ b/Clients/DNSServiceRegistration.m @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,30 +25,83 @@ Change History (most recent first): $Log: DNSServiceRegistration.m,v $ +Revision 1.15 2004/06/05 02:01:08 cheshire +Move DNSServiceRegistration from mDNSMacOSX directory to Clients directory + +Revision 1.14 2004/03/04 19:20:23 cheshire +Remove invalid UTF-8 character + Revision 1.13 2003/08/12 19:55:07 cheshire Update to APSL 2.0 */ -#import "RegistrationController.h" +#include "dns_sd.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; +} -#include +- (IBAction)registerService:(id)sender; +- (IBAction)unregisterService:(id)sender; -void reg_reply ( - int errorCode, - void *context - ) +- (IBAction)addNewService:(id)sender; +- (IBAction)removeSelected:(id)sender; + +@end + +void reg_reply + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + 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); -} +static void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context) + { + DNSServiceProcessResult((DNSServiceRef)context); + } + +static void addDNSServiceRefToRunLoop(DNSServiceRef ref) + { + int s = DNSServiceRefSockFD(ref); + CFSocketContext myCFSocketContext = { 0, ref, NULL, NULL, NULL }; + CFSocketRef c = CFSocketCreateWithNative(kCFAllocatorDefault, s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext); + CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, c, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + } + @implementation RegistrationController @@ -55,7 +110,7 @@ MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info 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 *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]; @@ -100,53 +155,43 @@ MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info - (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; + DNSServiceRef dns_client; if (selectedRow < 0) { return; } + NSString *key = [srvtypeKeys objectAtIndex:selectedRow]; + if ([registeredDict objectForKey:key]) { printf("Already registered\n"); return; } + context.version = 1; context.info = 0; context.retain = NULL; context.release = NULL; context.copyDescription = NULL; + unsigned char txtbuffer[300]; + strncpy(txtbuffer+1, [[srvtextKeys objectAtIndex:selectedRow] UTF8String], sizeof(txtbuffer)-1); + txtbuffer[0] = strlen(txtbuffer+1); - registerPort = [[srvportKeys objectAtIndex:selectedRow] intValue]; - - dns_client = DNSServiceRegistrationCreate + DNSServiceErrorType err = DNSServiceRegister ( + &dns_client, 0, 0, [[srvnameKeys objectAtIndex:selectedRow] UTF8String], - [[srvtypeKeys objectAtIndex:selectedRow] UTF8String], + [key UTF8String], [[srvdomainKeys objectAtIndex:selectedRow] UTF8String], - registerPort, - [[srvtextKeys objectAtIndex:selectedRow] UTF8String], + NULL, htons([[srvportKeys objectAtIndex:selectedRow] intValue]), + txtbuffer[0]+1, txtbuffer, 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"); - } - + if (err) + printf("DNSServiceRegister failed %d\n", err); + else + { + addDNSServiceRefToRunLoop(dns_client); + [registeredDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)dns_client] forKey:key]; + } } - (IBAction)unregisterService:(id)sender @@ -155,10 +200,10 @@ MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info NSString *key = [srvtypeKeys objectAtIndex:selectedRow]; NSNumber *refPtr = [registeredDict objectForKey:key]; - dns_service_discovery_ref ref = (dns_service_discovery_ref)[refPtr unsignedIntValue]; + DNSServiceRef ref = (DNSServiceRef)[refPtr unsignedIntValue]; if (ref) { - DNSServiceDiscoveryDeallocate(ref); + DNSServiceRefDeallocate(ref); [registeredDict removeObjectForKey:key]; } } @@ -242,6 +287,9 @@ MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info } - - @end + +int main(int argc, const char *argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/Clients/Java/BrowserApp.java b/Clients/Java/BrowserApp.java new file mode 100644 index 0000000..d3805ae --- /dev/null +++ b/Clients/Java/BrowserApp.java @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: BrowserApp.java,v $ +Revision 1.3 2004/05/26 01:41:58 cheshire +Pass proper flags to DNSSD.enumerateDomains + +Revision 1.2 2004/04/30 21:53:34 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + + BrowserApp demonstrates how to use DNSSD to browse for and resolve services. + + To do: + - display resolved TXTRecord + */ + + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.text.*; +import javax.swing.*; +import javax.swing.event.*; + +import com.apple.dnssd.*; + + +class BrowserApp implements ListSelectionListener, ResolveListener +{ + static BrowserApp app; + JFrame frame; + DomainListModel domainList; + BrowserListModel servicesList, serviceList; + JList domainPane, servicesPane, servicePane; + DNSSDService servicesBrowser, serviceBrowser, domainBrowser; + JLabel hostLabel, portLabel; + + public BrowserApp() + { + frame = new JFrame("DNS-SD Service Browser"); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + }); + + domainList = new DomainListModel(); + servicesList = new ServicesBrowserListModel(); + serviceList = new BrowserListModel(); + + try { + domainBrowser = DNSSD.enumerateDomains( DNSSD.BROWSE_DOMAINS, 0, domainList); + + servicesBrowser = DNSSD.browse( 0, 0, "_services._mdns._udp.", "", servicesList); + serviceBrowser = null; + } + catch ( Exception ex) { terminateWithException( ex); } + + this.setupSubPanes( frame.getContentPane()); + frame.pack(); + frame.setVisible(true); + } + + protected void setupSubPanes( Container parent) + { + parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS)); + + JPanel browserRow = new JPanel(); + browserRow.setLayout( new BoxLayout( browserRow, BoxLayout.X_AXIS)); + domainPane = new JList( domainList); + domainPane.addListSelectionListener( this); + JScrollPane domainScroller = new JScrollPane( domainPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + browserRow.add( domainScroller); + servicesPane = new JList( servicesList); + servicesPane.addListSelectionListener( this); + JScrollPane servicesScroller = new JScrollPane( servicesPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + browserRow.add( servicesScroller); + servicePane = new JList( serviceList); + servicePane.addListSelectionListener( this); + JScrollPane serviceScroller = new JScrollPane( servicePane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + browserRow.add( serviceScroller); + +/* + JPanel buttonRow = new JPanel(); + buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS)); + buttonRow.add( Box.createHorizontalGlue()); + JButton connectButton = new JButton( "Don't Connect"); + buttonRow.add( connectButton); + buttonRow.add( Box.createRigidArea( new Dimension( 16, 0))); +*/ + + JPanel labelRow = new JPanel(); + labelRow.setLayout( new BoxLayout( labelRow, BoxLayout.X_AXIS)); + labelRow.add( new JLabel( " Host: ")); + hostLabel = new JLabel(); + labelRow.add( hostLabel); + labelRow.add( Box.createRigidArea( new Dimension( 32, 0))); + labelRow.add( new JLabel( "Port: ")); + portLabel = new JLabel(); + labelRow.add( portLabel); + labelRow.add( Box.createHorizontalGlue()); + + parent.add( browserRow); + parent.add( Box.createRigidArea( new Dimension( 0, 8))); + parent.add( labelRow); +// parent.add( buttonRow); + parent.add( Box.createRigidArea( new Dimension( 0, 16))); + } + + public void valueChanged( ListSelectionEvent e) + { + try { + if ( e.getSource() == domainPane && !e.getValueIsAdjusting()) + { + int newSel = domainPane.getSelectedIndex(); + if ( -1 != newSel) + { + if ( serviceBrowser != null) + serviceBrowser.stop(); + serviceList.removeAllElements(); + servicesBrowser = DNSSD.browse( 0, 0, "_services._mdns._udp.", "", servicesList); + } + } + else if ( e.getSource() == servicesPane && !e.getValueIsAdjusting()) + { + int newSel = servicesPane.getSelectedIndex(); + if ( serviceBrowser != null) + serviceBrowser.stop(); + serviceList.removeAllElements(); + if ( -1 != newSel) + serviceBrowser = DNSSD.browse( 0, 0, servicesList.getNthRegType( newSel), "", serviceList); + } + else if ( e.getSource() == servicePane && !e.getValueIsAdjusting()) + { + int newSel = servicePane.getSelectedIndex(); + + hostLabel.setText( ""); + portLabel.setText( ""); + + if ( -1 != newSel) + { + DNSSD.resolve( 0, serviceList.getNthInterface( newSel), + serviceList.getNthServiceName( newSel), + serviceList.getNthRegType( newSel), + serviceList.getNthDomain( newSel), + new SwingResolveListener( this)); + } + } + } + catch ( Exception ex) { terminateWithException( ex); } + } + + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { + hostLabel.setText( hostName); + portLabel.setText( String.valueOf( port)); + } + + public void operationFailed( DNSSDService service, int errorCode) + { + // handle failure here + } + + protected static void terminateWithException( Exception e) + { + e.printStackTrace(); + System.exit( -1); + } + + public static void main(String s[]) + { + app = new BrowserApp(); + } +} + + +class BrowserListModel extends DefaultListModel implements BrowseListener, Runnable +{ + public BrowserListModel() + { + addCache = new Vector(); + removeCache = new Vector(); + } + + /* The Browser invokes this callback when a service is discovered. */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + addCache.add( new BrowserListElem( serviceName, domain, regType, ifIndex)); + if ( ( flags & DNSSD.MORE_COMING) == 0) + this.scheduleOnEventThread(); + } + + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + removeCache.add( serviceName); + if ( ( flags & DNSSD.MORE_COMING) == 0) + this.scheduleOnEventThread(); + } + + public void run() + { + while ( removeCache.size() > 0) + { + String serviceName = (String) removeCache.remove( removeCache.size() - 1); + int matchInd = this.findMatching( serviceName); // probably doesn't handle near-duplicates well. + if ( matchInd != -1) + this.removeElementAt( matchInd); + } + while ( addCache.size() > 0) + { + BrowserListElem elem = (BrowserListElem) addCache.remove( addCache.size() - 1); + if ( -1 == this.findMatching( elem.fServiceName)) // probably doesn't handle near-duplicates well. + this.addInSortOrder( elem); + } + } + + public void operationFailed( DNSSDService service, int errorCode) + { + // handle failure here + } + + /* The list contains BrowserListElem's */ + class BrowserListElem + { + public BrowserListElem( String serviceName, String domain, String type, int ifIndex) + { fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; } + + public String toString() { return fServiceName; } + + public String fServiceName, fDomain, fType; + public int fInt; + } + + public String getNthServiceName( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fServiceName; + } + + public String getNthRegType( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fType; + } + + public String getNthDomain( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fDomain; + } + + public int getNthInterface( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fInt; + } + + protected void addInSortOrder( Object obj) + { + int i; + for ( i = 0; i < this.size(); i++) + if ( sCollator.compare( obj.toString(), this.getElementAt( i).toString()) < 0) + break; + this.add( i, obj); + } + + protected int findMatching( String match) + { + for ( int i = 0; i < this.size(); i++) + if ( match.equals( this.getElementAt( i).toString())) + return i; + return -1; + } + + protected void scheduleOnEventThread() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected Vector removeCache; // list of serviceNames to remove + protected Vector addCache; // list of BrowserListElem's to add + + protected static Collator sCollator; + + static // Initialize our static variables + { + sCollator = Collator.getInstance(); + sCollator.setStrength( Collator.PRIMARY); + } +} + + +class ServicesBrowserListModel extends BrowserListModel +{ + /* The Browser invokes this callback when a service is discovered. */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + // Overridden to stuff serviceName into regType and make serviceName human-readable. + { + regType = serviceName + ( regType.startsWith( "_udp.") ? "._udp." : "._tcp."); + super.serviceFound( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain); + } + + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + // Overridden to make serviceName human-readable. + { + super.serviceLost( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain); + } + + protected String mapTypeToName( String type) + // Convert a registration type into a human-readable string. Returns original string on no-match. + { + final String[] namedServices = { + "_afpovertcp", "Apple File Sharing", + "_http", "World Wide Web servers", + "_daap", "Digital Audio Access", + "_apple-sasl", "Apple Password Servers", + "_distcc", "Distributed Compiler nodes", + "_finger", "Finger servers", + "_ichat", "iChat clients", + "_presence", "iChat AV clients", + "_ssh", "SSH servers", + "_telnet", "Telnet servers", + "_workstation", "Macintosh Manager clients", + "_bootps", "BootP servers", + "_xserveraid", "XServe RAID devices", + "_eppc", "Remote AppleEvents", + "_ftp", "FTP services", + "_tftp", "TFTP services" + }; + + for ( int i = 0; i < namedServices.length; i+=2) + if ( namedServices[i].equals( type)) + return namedServices[i + 1]; + return type; + } +} + + +class DomainListModel extends DefaultListModel implements DomainListener +{ + /* Called when a domain is discovered. */ + public void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain) + { + if ( !this.contains( domain)) + this.addElement( domain); + } + + public void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain) + { + if ( this.contains( domain)) + this.removeElement( domain); + } + + public void operationFailed( DNSSDService service, int errorCode) + { + // handle failure here + } +} + diff --git a/Clients/Java/DNSSDUnitTest.java b/Clients/Java/DNSSDUnitTest.java new file mode 100644 index 0000000..e4523e1 --- /dev/null +++ b/Clients/Java/DNSSDUnitTest.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSSDUnitTest.java,v $ +Revision 1.3 2004/05/26 01:41:58 cheshire +Pass proper flags to DNSSD.enumerateDomains + +Revision 1.2 2004/04/30 21:53:34 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + + DNSSDUnitTest is a simple program that exercises parts of the DNSSD API. + */ + +import com.apple.dnssd.*; + +import java.net.*; +import java.util.*; + + +class DNSSDUnitTest +{ + public static final String TEST_TYPE = "_unittest._udp"; + public static final String WIRE_CHAR_SET = "ISO-8859-1"; + + public DNSSDUnitTest fInstance = null; + + public DNSSDUnitTest() throws Exception + { + fStage = 0; + fInstance = this; + + Enumeration en = NetworkInterface.getNetworkInterfaces(); + while ( en.hasMoreElements()) + System.out.println( ((NetworkInterface) en.nextElement()).getName()); + } + + public void testTxtRecord() + { + byte[] src = { 6, 'a', 't', '=', 'X', 'Y', 'Z' }; + TXTRecord txtRecord = new TXTRecord( src); + String a; + + txtRecord.set( "path", "~/names"); + txtRecord.set( "ttl", "4"); + + byte[] rawBytes = txtRecord.getRawBytes(); + System.out.println( ( new String( rawBytes, 0, rawBytes.length)) + " has count " + + String.valueOf( txtRecord.size())); + + boolean ttlPresent = txtRecord.contains( "ttl"); + System.out.println( "ttl is present: " + ( ttlPresent ? "true" : "false")); + boolean timeoutPresent = txtRecord.contains( "timeout"); + System.out.println( "timeout is present: " + ( timeoutPresent ? "true" : "false")); + + for ( int i=0; null != ( a = txtRecord.getKey( i)); i++) + System.out.println( "attr/val " + String.valueOf( i) + ": " + a + "," + txtRecord.getValueAsString( i)); + } + + public void run() throws DNSSDException + { + System.out.println( "Running DNSSD unit test for " + System.getProperty( "user.name")); + + this.testTxtRecord(); + + fRegTest = new RegTest(); + new BrowseTest(); + new DomainTest(); + + this.waitForEnd(); + } + + protected int fStage; + protected RegTest fRegTest; + + public synchronized void bumpStage() + { + fStage++; + this.notifyAll(); + } + + protected synchronized void waitForEnd() + { + int stage = fStage; + while ( stage == fStage) + { + try { + wait(); + } catch (InterruptedException e) {} + } + } + + public static void main(String s[]) + { + try { + new DNSSDUnitTest().run(); + } + catch ( Exception e) { terminateWithException( e); } + } + + protected static void terminateWithException( Exception e) + { + e.printStackTrace(); + System.exit( -1); + } +} + +class TermReporter implements BaseListener +{ + public void operationFailed( DNSSDService service, int errorCode) + { + System.out.println( this.getClass().getName() + " encountered error " + String.valueOf( errorCode)); + } + + protected void finalize() throws Throwable + { + System.out.println( "Instance of " + this.getClass().getName() + " has been destroyed"); + } +} + +class RegTest extends TermReporter implements RegisterListener +{ + public static final int TEST_PORT = 5678; + + public RegTest() throws DNSSDException + { + fReg = DNSSD.register( 0, 0, "Test service", DNSSDUnitTest.TEST_TYPE, "", "", TEST_PORT, null, this); + } + + public void serviceRegistered( DNSSDRegistration registration, int flags, String serviceName, + String regType, String domain) + { + String s = "RegTest result flags:" + String.valueOf( flags) + + " serviceName:" + serviceName + " regType:" + regType + " domain:" + domain; + System.out.println( s); + + try { + new DupRegTest(); + + byte[] kResponsiblePerson = { 'c','o','o','k','i','e',' ','m','o','n','s','t','e','r' }; + fReg.addRecord( 0, 17 /*ns_t_rp*/, kResponsiblePerson, 3600); + new QueryTest( 0, 0, "Test service", 17 /*ns_t_rp*/, 1); + } catch( Exception e) { e.printStackTrace(); } + } + + protected DNSSDRegistration fReg; +} + +class DupRegTest extends TermReporter implements RegisterListener +{ + public static final int TEST_PORT = 5678; + + public DupRegTest() throws DNSSDException + { + DNSSD.register( DNSSD.NO_AUTO_RENAME | DNSSD.UNIQUE, 0, "Test service", DNSSDUnitTest.TEST_TYPE, "", "", TEST_PORT + 1, null, this); + } + + public void serviceRegistered( DNSSDRegistration registration, int flags, String serviceName, + String regType, String domain) + { + System.out.println( "Oik - registered a duplicate!"); + String s = "DupRegTest result flags:" + String.valueOf( flags) + + " serviceName:" + serviceName + " regType:" + regType + " domain:" + domain; + System.out.println( s); + } +} + +class BrowseTest extends TermReporter implements BrowseListener +{ + public BrowseTest() + { + try { + DNSSD.browse( 0, 0, DNSSDUnitTest.TEST_TYPE, "", this); + } catch( Exception e) { e.printStackTrace(); } + } + + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + String s = "BrowseTest found flags:" + String.valueOf( flags) + + " ifIndex:" + String.valueOf( ifIndex) + + " serviceName:" + serviceName + " regType:" + regType + " domain:" + domain; + System.out.println( s); + + System.out.println( "Resolving " + serviceName); + new ResolveTest( 0, ifIndex, serviceName, regType, domain); + } + + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + String s = "BrowseTest lost flags:" + String.valueOf( flags) + + " ifIndex:" + String.valueOf( ifIndex) + + " serviceName:" + serviceName + " regType:" + regType + " domain:" + domain; + System.out.println( s); + } + + public void operationFailed( DNSSDService service, int errorCode) + { + System.out.println( "Browse failed " + String.valueOf( errorCode)); + } +} + +class DomainTest extends TermReporter implements DomainListener +{ + public DomainTest() + { + try { + DNSSD.enumerateDomains( DNSSD.BROWSE_DOMAINS, 0, this); + } catch( Exception e) { e.printStackTrace(); } + } + + public void domainFound( DNSSDService enumerator, int flags, int ifIndex, String domain) + { + String s = "Domain found flags:" + String.valueOf( flags) + + " ifIndex:" + String.valueOf( ifIndex) + + " domain:" + domain; + System.out.println( s); + } + + public void domainLost( DNSSDService enumerator, int flags, int ifIndex, String domain) + { + String s = "Domain lost flags:" + String.valueOf( flags) + + " ifIndex:" + String.valueOf( ifIndex) + + " domain:" + domain; + System.out.println( s); + } + + public void operationFailed( DNSSDService service, int errorCode) + { + System.out.println( "Domain enum op failed " + String.valueOf( errorCode)); + } +} + +class ResolveTest extends TermReporter implements ResolveListener +{ + public ResolveTest( int flags, int ifIndex, String serviceName, String regType, + String domain) + { + try { + DNSSD.resolve( flags, ifIndex, serviceName, regType, domain, this); + } catch( Exception e) { e.printStackTrace(); } + } + + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { + String a; + String s = "ResolveTest result flags:" + String.valueOf( flags) + + " ifIndex:" + String.valueOf( ifIndex) + + " fullName:" + fullName + " hostName:" + hostName + " port:" + String.valueOf( port); + for ( int i=0; null != ( a = txtRecord.getKey( i)); i++) + s += " attr/val " + String.valueOf( i) + ": " + a + "," + txtRecord.getValueAsString( i); + + System.out.println( s); + + System.out.println( "Querying " + hostName); + new QueryTest( 0, ifIndex, hostName, 1 /* ns_t_a */, 1 /* ns_c_in */); + } +} + +class QueryTest extends TermReporter implements QueryListener +{ + public QueryTest( int flags, int ifIndex, String serviceName, int rrtype, int rrclass) + { + try { + DNSSD.queryRecord( flags, ifIndex, serviceName, rrtype, rrclass, this); + } catch( Exception e) { e.printStackTrace(); } + } + + public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName, + int rrtype, int rrclass, byte[] rdata, int ttl) + { + String s = "QueryTest result flags:" + String.valueOf( flags) + + " ifIndex:" + String.valueOf( ifIndex) + + " fullName:" + fullName + " rrtype:" + String.valueOf( rrtype) + + " rrclass:" + String.valueOf( rrclass) + " ttl:" + String.valueOf( ttl); + System.out.println( s); + + try { + String dataTxt = new String( rdata, 0, rdata.length, DNSSDUnitTest.WIRE_CHAR_SET); + System.out.println( "Query data is:" + dataTxt); + } catch( Exception e) { e.printStackTrace(); } + } +} + diff --git a/Clients/Java/SimpleChat.java b/Clients/Java/SimpleChat.java new file mode 100644 index 0000000..07f58d2 --- /dev/null +++ b/Clients/Java/SimpleChat.java @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SimpleChat.java,v $ +Revision 1.2 2004/04/30 21:53:35 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + + SimpleChat is a simple peer-to-peer chat program that demonstrates + DNSSD registration, browsing, resolving and record-querying. + + To do: + - remove TXTRecord tests + - remove diagnostic printf's + - implement better coloring algorithm + */ + + +import java.awt.*; +import java.awt.event.*; +import java.text.*; +import java.net.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +import com.apple.dnssd.*; + + +class SimpleChat implements ResolveListener, RegisterListener, QueryListener, + ActionListener, ItemListener, Runnable +{ + Document textDoc; // Holds all the chat text + JTextField inputField; // Holds a pending chat response + String ourName; // name used to identify this user in chat + DNSSDService browser; // object that actively browses for other chat clients + DNSSDService resolver; // object that resolves other chat clients + DNSSDRegistration registration; // object that maintains our connection advertisement + JComboBox targetPicker; // Indicates who we're talking to + TargetListModel targetList; // and its list model + JButton sendButton; // Will send text in inputField to target + InetAddress buddyAddr; // and address + int buddyPort; // and port + DatagramPacket dataPacket; // Inbound data packet + DatagramSocket outSocket; // Outbound data socket + SimpleAttributeSet textAttribs; + + static final String kChatExampleRegType = "_p2pchat._udp"; + static final String kWireCharSet = "ISO-8859-1"; + + public SimpleChat() throws Exception + { + JFrame frame = new JFrame("SimpleChat"); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + }); + + ourName = System.getProperty( "user.name"); + targetList = new TargetListModel(); + textAttribs = new SimpleAttributeSet(); + DatagramSocket inSocket = new DatagramSocket(); + dataPacket = new DatagramPacket( new byte[ 4096], 4096); + outSocket = new DatagramSocket(); + + this.setupSubPanes( frame.getContentPane(), frame.getRootPane()); + frame.pack(); + frame.setVisible(true); + inputField.requestFocusInWindow(); + + browser = DNSSD.browse( 0, 0, kChatExampleRegType, "", new SwingBrowseListener( targetList)); + + TXTRecord tRec = new TXTRecord(); + tRec.set( "name", "roger"); + tRec.set( "color", "blue"); + + registration = DNSSD.register( 0, 0, ourName, kChatExampleRegType, "", "", inSocket.getLocalPort(), tRec, this); + + new ListenerThread( this, inSocket, dataPacket).start(); + } + + protected void setupSubPanes( Container parent, JRootPane rootPane) + { + parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS)); + + JPanel textRow = new JPanel(); + textRow.setLayout( new BoxLayout( textRow, BoxLayout.X_AXIS)); + textRow.add( Box.createRigidArea( new Dimension( 16, 0))); + JEditorPane textPane = new JEditorPane( "text/html", "
"); + textPane.setPreferredSize( new Dimension( 400, 300)); + textPane.setEditable( false); + JScrollPane textScroller = new JScrollPane( textPane); + textRow.add( textScroller); + textRow.add( Box.createRigidArea( new Dimension( 16, 0))); + textDoc = textPane.getDocument(); + + JPanel addressRow = new JPanel(); + addressRow.setLayout( new BoxLayout( addressRow, BoxLayout.X_AXIS)); + targetPicker = new JComboBox( targetList); + targetPicker.addItemListener( this); + addressRow.add( Box.createRigidArea( new Dimension( 16, 0))); + addressRow.add( new JLabel( "Talk to: ")); + addressRow.add( targetPicker); + addressRow.add( Box.createHorizontalGlue()); + + JPanel buttonRow = new JPanel(); + buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS)); + buttonRow.add( Box.createRigidArea( new Dimension( 16, 0))); + inputField = new JTextField(); + // prevent inputField from hijacking key + inputField.getKeymap().removeKeyStrokeBinding( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0)); + buttonRow.add( inputField); + sendButton = new JButton( "Send"); + buttonRow.add( Box.createRigidArea( new Dimension( 8, 0))); + buttonRow.add( sendButton); + buttonRow.add( Box.createRigidArea( new Dimension( 16, 0))); + rootPane.setDefaultButton( sendButton); + sendButton.addActionListener( this); + sendButton.setEnabled( false); + + parent.add( Box.createRigidArea( new Dimension( 0, 16))); + parent.add( textRow); + parent.add( Box.createRigidArea( new Dimension( 0, 8))); + parent.add( addressRow); + parent.add( Box.createRigidArea( new Dimension( 0, 8))); + parent.add( buttonRow); + parent.add( Box.createRigidArea( new Dimension( 0, 16))); + } + + public void serviceRegistered( DNSSDRegistration registration, int flags, + String serviceName, String regType, String domain) + { + ourName = serviceName; // might have been renamed on collision +System.out.println( "ServiceRegisterCallback() invoked as " + serviceName); + } + + public void operationFailed( DNSSDService service, int errorCode) + { + System.out.println( "Service reported error " + String.valueOf( errorCode)); + } + + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { +String a; +for ( int i=0; null != ( a = txtRecord.getKey( i)); i++) + System.out.println( "attr/val " + String.valueOf( i) + ": " + a + "," + txtRecord.getValueAsString( i)); + + buddyPort = port; + try { + // Start a record query to obtain IP address from hostname + DNSSD.queryRecord( 0, ifIndex, hostName, 1 /* ns_t_a */, 1 /* ns_c_in */, + new SwingQueryListener( this)); + } + catch ( Exception e) { terminateWithException( e); } + resolver.stop(); + } + + public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName, + int rrtype, int rrclass, byte[] rdata, int ttl) + { + try { + buddyAddr = InetAddress.getByAddress( rdata); + } + catch ( Exception e) { terminateWithException( e); } + sendButton.setEnabled( true); + } + + public void actionPerformed( ActionEvent e) // invoked when Send button is hit + { + try + { + String sendString = ourName + ": " + inputField.getText(); + byte[] sendData = sendString.getBytes( kWireCharSet); + outSocket.send( new DatagramPacket( sendData, sendData.length, buddyAddr, buddyPort)); + StyleConstants.setForeground( textAttribs, Color.black); + textDoc.insertString( textDoc.getLength(), inputField.getText() + "\n", textAttribs); + inputField.setText( ""); + } + catch ( Exception exception) { terminateWithException( exception); } + } + + public void itemStateChanged( ItemEvent e) // invoked when Target selection changes + { + sendButton.setEnabled( false); + if ( e.getStateChange() == ItemEvent.SELECTED) + { + try { + TargetListElem sel = (TargetListElem) targetList.getSelectedItem(); + resolver = DNSSD.resolve( 0, sel.fInt, sel.fServiceName, sel.fType, sel.fDomain, this); + } + catch ( Exception exception) { terminateWithException( exception); } + } + } + + public void run() // invoked on event thread when inbound packet arrives + { + try + { + String inMessage = new String( dataPacket.getData(), 0, dataPacket.getLength(), kWireCharSet); + StyleConstants.setForeground( textAttribs, this.getColorFor( dataPacket.getData(), dataPacket.getLength())); + textDoc.insertString( textDoc.getLength(), inMessage + "\n", textAttribs); + } + catch ( Exception e) { terminateWithException( e); } + } + + protected Color getColorFor( byte[] chars, int length) + // Produce a mapping from a string to a color, suitable for text display + { + int rgb = 0; + for ( int i=0; i < length && chars[i] != ':'; i++) + rgb = rgb ^ ( (int) chars[i] << (i%3+2) * 8); + return new Color( rgb & 0x007F7FFF); // mask off high bits so it is a dark color + +// for ( int i=0; i < length && chars[i] != ':'; i++) + + } + + protected static void terminateWithException( Exception e) + { + e.printStackTrace(); + System.exit( -1); + } + + public static void main(String s[]) + { + try { + new SimpleChat(); + } + catch ( Exception e) { terminateWithException( e); } + } +} + + + +class TargetListElem +{ + public TargetListElem( String serviceName, String domain, String type, int ifIndex) + { fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; } + + public String toString() { return fServiceName; } + + public String fServiceName, fDomain, fType; + public int fInt; +} + +class TargetListModel extends DefaultComboBoxModel implements BrowseListener +{ + /* The Browser invokes this callback when a service is discovered. */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well. + + if ( match == null) + this.addElement( new TargetListElem( serviceName, domain, regType, ifIndex)); + } + + /* The Browser invokes this callback when a service disappears. */ + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well. + + if ( match != null) + this.removeElement( match); + } + + /* The Browser invokes this callback when a service disappears. */ + public void operationFailed( DNSSDService service, int errorCode) + { + System.out.println( "Service reported error " + String.valueOf( errorCode)); + } + + protected TargetListElem findMatching( String match) + { + for ( int i = 0; i < this.getSize(); i++) + if ( match.equals( this.getElementAt( i).toString())) + return (TargetListElem) this.getElementAt( i); + return null; + } + +} + + +// A ListenerThread runs its owner when datagram packet p appears on socket s. +class ListenerThread extends Thread +{ + public ListenerThread( Runnable owner, DatagramSocket s, DatagramPacket p) + { fOwner = owner; fSocket = s; fPacket = p; } + + public void run() + { + while ( true ) + { + try + { + fSocket.receive( fPacket); + SwingUtilities.invokeAndWait( fOwner); // process data on main thread + } + catch( Exception e) + { + break; // terminate thread + } + } + } + + protected Runnable fOwner; + protected DatagramSocket fSocket; + protected DatagramPacket fPacket; +} + + + diff --git a/Clients/Java/SwingBrowseListener.java b/Clients/Java/SwingBrowseListener.java new file mode 100644 index 0000000..4855dde --- /dev/null +++ b/Clients/Java/SwingBrowseListener.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SwingBrowseListener.java,v $ +Revision 1.2 2004/04/30 21:53:35 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule BrowseListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingBrowseListener implements Runnable, BrowseListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingBrowseListener( BrowseListener listener) + { fListener = listener; fErrorCode = 0; } + + /** (Clients should not call this method directly.) */ + public void operationFailed( DNSSDService service, int errorCode) + { + fBrowser = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + + { + fBrowser = browser; + fIsAdd = true; + fFlags = flags; + fIndex = ifIndex; + fService = serviceName; + fRegType = regType; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + fBrowser = browser; + fIsAdd = false; + fFlags = flags; + fIndex = ifIndex; + fService = serviceName; + fRegType = regType; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fBrowser, fErrorCode); + else if ( fIsAdd) + fListener.serviceFound( fBrowser, fFlags, fIndex, fService, fRegType, fDomain); + else + fListener.serviceLost( fBrowser, fFlags, fIndex, fService, fRegType, fDomain); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected BrowseListener fListener; + + protected boolean fIsAdd; + protected DNSSDService fBrowser; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fService; + protected String fRegType; + protected String fDomain; +} + diff --git a/Clients/Java/SwingDomainListener.java b/Clients/Java/SwingDomainListener.java new file mode 100644 index 0000000..2c5840f --- /dev/null +++ b/Clients/Java/SwingDomainListener.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SwingDomainListener.java,v $ +Revision 1.2 2004/04/30 21:53:35 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule DomainListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingDomainListener implements Runnable, DomainListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingDomainListener( DomainListener listener) + { fListener = listener; fErrorCode = 0; } + + /** (Clients should not call this method directly.) */ + public void operationFailed( DNSSDService service, int errorCode) + { + fEnumerator = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain) + + { + fEnumerator = domainEnum; + fIsAdd = true; + fFlags = flags; + fIndex = ifIndex; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain) + { + fEnumerator = domainEnum; + fIsAdd = false; + fFlags = flags; + fIndex = ifIndex; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fEnumerator, fErrorCode); + else if ( fIsAdd) + fListener.domainFound( fEnumerator, fFlags, fIndex, fDomain); + else + fListener.domainLost( fEnumerator, fFlags, fIndex, fDomain); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected DomainListener fListener; + + protected boolean fIsAdd; + protected DNSSDService fEnumerator; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fDomain; +} + diff --git a/Clients/Java/SwingQueryListener.java b/Clients/Java/SwingQueryListener.java new file mode 100644 index 0000000..4b58e67 --- /dev/null +++ b/Clients/Java/SwingQueryListener.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SwingQueryListener.java,v $ +Revision 1.2 2004/04/30 21:53:35 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule QueryListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingQueryListener implements Runnable, QueryListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingQueryListener( QueryListener listener) + { fListener = listener; } + + public void operationFailed( DNSSDService service, int errorCode) + { + fQuery = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName, + int rrtype, int rrclass, byte[] rdata, int ttl) + { + fQuery = query; + fFlags = flags; + fIndex = ifIndex; + fFullName = fullName; + fType = rrtype; + fClass = rrclass; + fData = rdata; + fTTL = ttl; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fQuery, fErrorCode); + else + fListener.queryAnswered( fQuery, fFlags, fIndex, fFullName, fType, fClass, fData, fTTL); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected QueryListener fListener; + + protected DNSSDService fQuery; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fFullName; + protected int fType; + protected int fClass; + protected byte[] fData; + protected int fTTL; +} + diff --git a/Clients/Java/SwingResolveListener.java b/Clients/Java/SwingResolveListener.java new file mode 100644 index 0000000..1422a6e --- /dev/null +++ b/Clients/Java/SwingResolveListener.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SwingResolveListener.java,v $ +Revision 1.2 2004/06/14 23:46:55 cheshire +Recreate CVS state + +Revision 1.1 2004/06/14 23:46:26 cheshire +First checkin + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule ResolveListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingResolveListener implements Runnable, ResolveListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingResolveListener( ResolveListener listener) + { fListener = listener; } + + public void operationFailed( DNSSDService service, int errorCode) + { + fResolver = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { + fResolver = resolver; + fFlags = flags; + fIndex = ifIndex; + fFullName = fullName; + fHostName = hostName; + fPort = port; + fTXTRecord = txtRecord; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fResolver, fErrorCode); + else + fListener.serviceResolved( fResolver, fFlags, fIndex, fFullName, fHostName, fPort, fTXTRecord); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected ResolveListener fListener; + + protected DNSSDService fResolver; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fFullName; + protected String fHostName; + protected int fPort; + protected TXTRecord fTXTRecord; +} + diff --git a/Clients/Makefile b/Clients/Makefile new file mode 100755 index 0000000..11f4a83 --- /dev/null +++ b/Clients/Makefile @@ -0,0 +1,62 @@ +# Copyright (c) 2002-2004 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@ +# +# $Log: Makefile,v $ +# Revision 1.4 2004/05/21 17:25:56 cheshire +# Fixes to make sample client work on Linux +# +# Revision 1.3 2004/03/12 08:05:32 cheshire +# Add a "make clean" target +# +# Revision 1.2 2004/02/11 20:59:26 cheshire +# Fix Makefile so it creates the "build" directory if necessary +# +# Revision 1.1 2004/02/06 03:19:09 cheshire +# Check in code to make command-line "dns-sd" testing tool +# +# +# Notes: +# $@ 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 variables, see +# + +############################################################################# + + +# If library /usr/lib/libmdns.* exists, then link it +ifneq "$(wildcard /usr/lib/libmdns.*)" "" +LIBS = -lmdns +else +LIBS = +endif + +targets: build/dns-sd + +clean: + rm -rf build + +build: + mkdir build + +build/dns-sd: build dns-sd.c + cc $(filter %.c %.o, $+) $(LIBS) -o $@ diff --git a/Clients/ReadMe.txt b/Clients/ReadMe.txt new file mode 100644 index 0000000..83dd242 --- /dev/null +++ b/Clients/ReadMe.txt @@ -0,0 +1,25 @@ +This directory contains a variety of clients that use the +"/usr/include/dns_sd.h" APIs. + +Some of the clients are command-line oriented; some are graphical. + +Some of the clients, like the "dns-sd" command-line tool, can be built +for a variety of platforms. Some of the clients only build for one +platform, like "DNS Service Browser" and "DNS Service Registration", +which use Objective C and Cocoa and only run on OS X 10.3 or later. + +Some of the clients can be built more than one way. For example, the +"dns-sd" command-line tool can be built for OS X using both the XCode +IDE, or using a simple command-line "make" command. + +What all clients have in common is that they all demonstrate how you +can use the "/usr/include/dns_sd.h" APIs. + +The table below gives a summary of which clients build for which platforms. + +Client Type OS X OS X Posix + XCode Make Make + +dns-sd Command-line X X X +DNS Service Browser Graphical X +DNS Service Registration Graphical X diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c new file mode 100644 index 0000000..1fb02d0 --- /dev/null +++ b/Clients/dns-sd.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + * + * Formatting notes: + * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion + * on C indentation can be found on the web, such as , + * but for the sake of brevity here I will say just this: Curly braces are not syntactially + * part of an "if" statement; they are the beginning and ending markers of a compound statement; + * therefore common sense dictates that if they are part of a compound statement then they + * should be indented to the same level as everything else in that compound statement. + * Indenting curly braces at the same level as the "if" implies that curly braces are + * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" + * thinking that variables x and y are both of type "char*" -- and anyone who doesn't + * understand why variable y is not of type "char*" just proves the point that poor code + * layout leads people to unfortunate misunderstandings about how the C language really works.) + + Change History (most recent first): + +$Log: dns-sd.c,v $ +Revision 1.7 2004/05/28 02:20:06 cheshire +If we allow dot or empty string for domain when resolving a service, +it should be a synonym for "local" + +Revision 1.6 2004/05/21 19:57:19 cheshire +Add -Q option to exercise DNSServiceQueryRecord() API call + +Revision 1.5 2004/05/21 17:39:27 cheshire +Include extra headers to fix Xcode build + +Revision 1.4 2004/05/21 17:25:56 cheshire +Fixes to make sample client work on Linux + +Revision 1.3 2004/04/06 22:02:06 cheshire +Also show interface id when showing browse add/remove events + +Revision 1.2 2004/03/25 05:40:56 cheshire +Changes from Marc Krochmal: Fix inconsistent use of space/tab + +Revision 1.1 2004/02/06 03:19:09 cheshire +Check in code to make command-line "dns-sd" testing tool + +*/ + +#include // For stdout, stderr +#include // For exit() +#include // For strlen(), strcpy(), bzero() +#include // For getopt() and optind +#include // For errno, EINTR +#define BIND_8_COMPAT +#include // For T_HINFO, etc. +#include // For struct timeval +#include + +//************************************************************************************************************* +// Globals + +typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; + +static int operation; +static DNSServiceRef client = NULL; +static int num_printed; +static char addtest = 0; +static DNSRecordRef record = NULL; +static char myhinfo9[11] = "\003Mac\006OS 9.2"; +static char myhinfoX[ 9] = "\003Mac\004OS X"; +static char updatetest[3] = "\002AA"; +static char bigNULL[4096]; + +#define LONG_TIME 0x4000000 +static volatile int stopNow = 0; +static volatile int timeOut = LONG_TIME; + +//************************************************************************************************************* +// Supporting Utility Function + +//************************************************************************************************************* +// 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("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec/1000); + } + +#define DomainMsg(X) (((X) & kDNSServiceFlagsDefault) ? "(Default)" : \ + ((X) & kDNSServiceFlagsAdd) ? "Added" : "Removed") + +static void regdom_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interface, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) + { + (void)interface; // Unused + (void)errorCode; // Unused + (void)context; // Unused + printtimestamp(); + printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(flags)); + if (flags) printf(" Flags: %X", flags); + printf("\n"); + } + +static void browsedom_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) + { + (void)client; // Unused + (void)interface; // Unused + (void)errorCode; // Unused + (void)context; // Unused + printtimestamp(); + printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(flags)); + if (flags) printf(" Flags: %X", flags); + printf("\n"); + } + +static void browse_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, + const char *replyName, const char *replyType, const char *replyDomain, void *context) + { + char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; + (void)client; // Unused + (void)interface; // Unused + (void)errorCode; // Unused + (void)context; // Unused + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-24s %-24s %s\n", "Domain", "Service Type", "Instance Name"); + printtimestamp(); + printf("%s%6X%3d %-24s %-24s %s\n", op, flags, interface, replyDomain, replyType, replyName); + } + +static void resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, + const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const char *txtRecord, void *context) + { + const char *src = txtRecord; + union { uint16_t s; u_char b[2]; } port = { opaqueport }; + uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; + + (void)client; // Unused + (void)interface; // Unused + (void)errorCode; // Unused + (void)context; // Unused + + printtimestamp(); + printf("%s can be reached at %s:%u", fullname, hosttarget, 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 myTimerCallBack(void) + { + DNSServiceErrorType err; + + switch (operation) + { + case 'A': + { + switch (addtest) + { + case 0: printf("Adding Test HINFO record\n"); + err = DNSServiceAddRecord(client, &record, 0, T_HINFO, sizeof(myhinfo9), &myhinfo9[0], 120); + addtest = 1; + break; + case 1: printf("Updating Test HINFO record\n"); + err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 120); + addtest = 2; + break; + case 2: printf("Removing Test HINFO record\n"); + err = DNSServiceRemoveRecord(client, record, 0); + 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]); + err = DNSServiceUpdateRecord(client, NULL, 0, 1+updatetest[0], &updatetest[0], 120); + } + break; + + case 'N': + { + printf("Adding big NULL record\n"); + err = DNSServiceAddRecord(client, &record, 0, T_NULL, sizeof(bigNULL), &bigNULL[0], 120); + timeOut = LONG_TIME; + } + break; + } + + if (err != kDNSServiceErr_NoError) + { + fprintf(stderr, "DNSService call failed %ld\n", (long int)err); + stopNow = 1; + } + } + +static void reg_reply(DNSServiceRef client, DNSServiceFlags flags, DNSServiceErrorType errorCode, + const char *name, const char *regtype, const char *domain, void *context) + { + (void)client; // Unused + (void)flags; // Unused + (void)context; // Unused + + printf("Got a reply for %s.%s%s: ", name, regtype, domain); + switch (errorCode) + { + case kDNSServiceErr_NoError: printf("Name now registered and active\n"); break; + case kDNSServiceErr_NameConflict: printf("Name in use, please choose another\n"); exit(-1); + default: printf("Error %d\n", errorCode); return; + } + + if (operation == 'A' || operation == 'U' || operation == 'N') timeOut = 5; + } + +void qr_reply(DNSServiceRef sdRef, 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) + { + const unsigned char *rd = rdata; + char rdb[1000]; + switch (rrtype) + { + case T_A: snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); break; + default : snprintf(rdb, sizeof(rdb), "Unknown rdata: %d bytes", rdlen); break; + } + printf("%-30s%4d%4d %s\n", fullname, rrtype, rrclass, rdb); + } + +//************************************************************************************************************* +// The main test function + +static void HandleEvents(void) + { + int dns_sd_fd = DNSServiceRefSockFD(client); + int nfds = dns_sd_fd + 1; + fd_set readfds; + struct timeval tv; + int result; + + while (!stopNow) + { + // 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. Add the fd for our client(s) to the fd_set + FD_SET(dns_sd_fd, &readfds); + + // 3. Set up the timeout. + tv.tv_sec = timeOut; + tv.tv_usec = 0; + + result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (result > 0) + { + if (FD_ISSET(dns_sd_fd, &readfds)) DNSServiceProcessResult(client); + } + else if (result == 0) + myTimerCallBack(); + else + { + printf("select() returned %d errno %d %s\n", result, errno, strerror(errno)); + if (errno != EINTR) stopNow = 1; + } + } + } + +int main(int argc, char **argv) + { + DNSServiceErrorType err; + 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, "EFBLQRAUNTMI"); + if (operation == -1) goto Fail; + + switch (operation) + { + case 'E': printf("Looking for recommended registration domains:\n"); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, 0, regdom_reply, NULL); + break; + + case 'F': printf("Looking for recommended browsing domains:\n"); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, 0, browsedom_reply, NULL); + 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); + err = DNSServiceBrowse(&client, 0, 0, argv[optind+0], dom, browse_reply, NULL); + break; + + case 'L': if (argc < optind+2) goto Fail; + dom = (argc < optind+3) ? "local" : argv[optind+2]; + if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local" + printf("Lookup %s.%s.%s\n", argv[optind+0], argv[optind+1], dom); + err = DNSServiceResolve(&client, 0, 0, argv[optind+0], argv[optind+1], dom, resolve_reply, NULL); + 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 + + for (i = optind+4; i < argc; i++) + { + char *len = ptr++; + *len = strlen(argv[i]); + strcpy(ptr, argv[i]); + ptr += *len; + } + + printf("Registering Service %s.%s%s port %s %s\n", nam, typ, dom, argv[optind+3], txt); + err = DNSServiceRegister(&client, 0, 0, nam, typ, dom, NULL, registerPort.NotAnInteger, ptr-txt, txt, reg_reply, NULL); + break; + } + + case 'Q': { + if (argc < optind+1) goto Fail; + uint16_t rrtype = (argc <= optind+1) ? T_A : atoi(argv[optind+1]); + uint16_t rrclass = (argc <= optind+2) ? C_IN : atoi(argv[optind+2]); + err = DNSServiceQueryRecord(&client, 0, 0, argv[optind+0], rrtype, rrclass, qr_reply, NULL); + break; + } + + case 'A': + case 'U': + case 'N': { + Opaque16 registerPort = { { 0x12, 0x34 } }; + static const char TXT[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; + printf("Registering Service Test._testupdate._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, 0, "Test", "_testupdate._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT)-1, TXT, reg_reply, NULL); + break; + } + + case 'T': { + Opaque16 registerPort = { { 0x23, 0x45 } }; + char TXT[1024]; + unsigned int i; + for (i=0; i> 5); + printf("Registering Service Test._testlargetxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, 0, "Test", "_testlargetxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT), TXT, reg_reply, NULL); + break; + } + + case 'M': { + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; + static const char TXT1[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; + static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String" "\xC" "Sixth String"; + printf("Registering Service Test._testdualtxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, 0, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL); + if (!err) err = DNSServiceAddRecord(client, &record, 0, T_TXT, sizeof(TXT2)-1, TXT2, 120); + break; + } + + case 'I': { + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; + static const char TXT[] = "\x09" "Test Data"; + printf("Registering Service Test._testtxt._tcp.local.\n"); + err = DNSServiceRegister(&client, 0, 0, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); + if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 120); + break; + } + + default: goto Fail; + } + + if (!client || err != kDNSServiceErr_NoError) { fprintf(stderr, "DNSService call failed %ld\n", (long int)err); return (-1); } + HandleEvents(); + + // Be sure to deallocate the DNSServiceRef when you're finished + DNSServiceRefDeallocate(client); + return 0; + +Fail: + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", argv[0]); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", argv[0]); + fprintf(stderr, "%s -B (Browse for services instances)\n", argv[0]); + fprintf(stderr, "%s -L (Look up a service instance)\n", argv[0]); + fprintf(stderr, "%s -R [...] (Register a service)\n", argv[0]); + fprintf(stderr, "%s -Q (Generic query for any record type)\n", argv[0]); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", argv[0]); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", argv[0]); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", argv[0]); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", argv[0]); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", argv[0]); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", argv[0]); + return 0; + } diff --git a/Makefile b/Makefile index e6dbb70..6535aa1 100644 --- a/Makefile +++ b/Makefile @@ -16,16 +16,16 @@ include /Developer/Makefiles/pb_makefiles/platform.make -MVERS = "mDNSResponder-58.8.1" +MVERS = "mDNSResponder-66.3" install: - cd "$(SRCROOT)/mDNSMacOSX"; pbxbuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) + cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) installsrc: ditto . ${SRCROOT} installhdrs:: - cd "$(SRCROOT)/mDNSMacOSX"; pbxbuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) + cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) clean:: echo clean diff --git a/README.txt b/README.txt index 60abf83..fa70ecf 100644 --- a/README.txt +++ b/README.txt @@ -3,7 +3,7 @@ What is mDNSResponder? The mDNSResponder project is a component of Rendezvous, Apple's ease-of-use IP networking initiative: - + Apple's Rendezvous software derives from the ongoing standardization work of the IETF Zero Configuration Networking Working Group: @@ -92,16 +92,15 @@ the functions it needs -- 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. +Apple currently provides "Platform Support" layers for Mac OS 9, Mac OS X, +Microsoft Windows, VxWorks, and for POSIX platforms like Linux, Solaris, +FreeBSD, etc. -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: +Note: Developers writing applications for OS X do not need to incorporate +this code into their applications, since OS X 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 | @@ -111,13 +110,12 @@ then we would end up with a situation like the picture below: | 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. +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 provides a common system service which client +software should access through the "/usr/include/dns_sd.h" APIs. -The situation on OS X 10.2 looks more like the picture below: +The situation on OS X looks more like the picture below: ------------------- / \ @@ -129,7 +127,7 @@ The situation on OS X 10.2 looks more like the picture below: | Platform Support | +------------------+ -Applications on OS X 10.2 make calls to the single mDNSResponder daemon +Applications on OS X 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 @@ -142,4 +140,4 @@ 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. +building an mDNSResponder on a POSIX Operating System. diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c new file mode 100644 index 0000000..6e317a9 --- /dev/null +++ b/mDNSCore/DNSCommon.c @@ -0,0 +1,1684 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSCommon.c,v $ +Revision 1.35 2004/06/05 00:14:44 cheshire +Fix signed/unsigned and other compiler warnings + +Revision 1.34 2004/06/04 00:25:25 cheshire +Fix misaligned write exception that occurs on some platforms + +Revision 1.33 2004/06/04 00:16:18 cheshire +Remove non-portable use of 'inline' + +Revision 1.32 2004/06/03 03:09:58 ksekar +: Garbage Collection for Dynamic Updates + +Revision 1.31 2004/05/28 23:42:36 ksekar +: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.30 2004/05/26 09:08:04 bradley +Added cast to correct structure pointer when allocating domain name list element to fix C++ builds. + +Revision 1.29 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.28 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.27 2004/04/22 20:29:07 cheshire +Log error message if no count field passed to PutResourceRecordTTL() + +Revision 1.26 2004/04/22 04:07:01 cheshire +Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it + +Revision 1.25 2004/04/22 03:05:28 cheshire +kDNSClass_ANY should be kDNSQClass_ANY + +Revision 1.24 2004/04/22 02:51:20 cheshire +Use common code for HINFO/TXT and TSIG cases in putRData + +Revision 1.23 2004/04/15 00:51:28 bradley +Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. +Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. + +Revision 1.22 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.21 2004/04/09 16:47:28 cheshire +: mDNSResponder escape handling inconsistent with BIND + +Revision 1.20 2004/04/09 16:37:15 cheshire +Suggestion from Bob Bradley: +Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers + +Revision 1.19 2004/04/02 19:34:38 cheshire +Fix broken comment + +Revision 1.18 2004/03/30 06:45:00 cheshire +Compiler warning fixes from Don Woodward at Roku Labs + +Revision 1.17 2004/03/19 22:25:20 cheshire +: Need to limit service types to fourteen characters +Won't actually do this for now, but keep the code around just in case + +Revision 1.16 2004/03/08 02:45:35 cheshire +Minor change to make a couple of the log messages a bit shorter + +Revision 1.15 2004/03/08 02:44:09 cheshire +: Need to limit service types to fourteen characters + +Revision 1.14 2004/02/21 02:06:24 cheshire +Can't use anonymous unions -- they're non-standard and don't work on all compilers + +Revision 1.13 2004/02/06 23:04:18 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.12 2004/02/03 22:37:10 cheshire +Delete unused (commented-out) code + +Revision 1.11 2004/02/03 22:35:34 cheshire +: Should not allow empty string for resolve domain + +Revision 1.10 2004/02/03 19:47:36 ksekar +Added an asyncronous state machine mechanism to uDNS.c, including +calls to find the parent zone for a domain name. Changes include code +in repository previously dissabled via "#if 0 //incomplete". Codepath +is currently unused, and will be called to create update records, etc. + +Revision 1.9 2004/01/27 20:15:22 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.8 2004/01/24 23:24:36 cheshire +Expanded out the list of local domains to reduce risk of mistakes in future + +Revision 1.7 2004/01/24 08:32:30 bradley +Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds. +Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting +to signal potentially hidden errors about the subsequent digit not being part of the octal sequence. + +Revision 1.6 2004/01/24 04:59:15 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.5 2004/01/23 23:23:14 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.4 2004/01/22 02:15:33 cheshire +: Link-local reverse-mapping domains need to be resolved using link-local multicast + +Revision 1.3 2004/01/21 21:16:29 cheshire +Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc. + +Revision 1.2 2003/12/13 05:47:48 bradley +Made local ptr const to fix error when assigning from const structure. Disable benign conditional +expression is constant warning when building with Microsoft compilers. + +Revision 1.1 2003/12/13 03:05:27 ksekar +: DynDNS: Unicast query of service records + + */ + +// Set mDNS_InstantiateInlines to tell mDNSClientAPI.h to instantiate inline functions, if necessary +#define mDNS_InstantiateInlines 1 +#include "DNSCommon.h" + +// Disable certain benign warnings with Microsoft compilers +#if (defined(_MSC_VER)) + // 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) +#endif + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNameList copy/deallocation routines +#endif + +mDNSexport DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig) + { + DNameListElem *copy = mDNSNULL, *newelem; + const DNameListElem *ptr; + + for (ptr = orig; ptr; ptr = ptr->next) + { + newelem = (DNameListElem*)mDNSPlatformMemAllocate(sizeof(DNameListElem)); + if (!newelem) { LogMsg("ERROR: malloc"); return mDNSNULL; } + mDNSPlatformStrCopy(ptr->name.c, newelem->name.c); + newelem->next = copy; + copy = newelem; + } + return copy; + } + +mDNSexport void mDNS_FreeDNameList(DNameListElem *list) + { + DNameListElem *fptr; + + while (list) + { + fptr = list; + list = list->next; + mDNSPlatformMemFree(fptr); + } + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - General Utility Functions +#endif + +mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf) + { + while (intf && !intf->InterfaceActive) intf = intf->next; + return(intf); + } + +mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf) + { + const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); + if (next) return(next->InterfaceID); else return(mDNSNULL); + } + +mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id) + { + mDNSu32 slot, used = 0; + CacheRecord *rr; + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) + if (rr->resrec.InterfaceID == id) used++; + return(used); + } + +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); + } + +mDNSexport 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); + } + +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); + } + +mDNSexport 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); + } + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Domain Name Utility Functions +#endif + +mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b) + { + int i; + const int len = *a++; + + if (len > MAX_DOMAIN_LABEL) + { debugf("Malformed label (too long)"); return(mDNSfalse); } + + if (len != *b++) return(mDNSfalse); + for (i=0; ic; + const mDNSu8 * b = d2->c; + const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid + + while (*a || *b) + { + if (a + 1 + *a >= max) + { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse); } + if (!SameDomainLabel(a, b)) return(mDNSfalse); + a += 1 + *a; + b += 1 + *b; + } + + return(mDNStrue); + } + +mDNSexport mDNSBool IsLocalDomain(const domainname *d) + { + // Domains that are defined to be resolved via link-local multicast are: + // local., 254.169.in-addr.arpa., and 0.8.E.F.ip6.arpa. + static const domainname *n0 = (domainname*)"\x5" "local"; + static const domainname *n1 = (domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa"; + static const domainname *n2 = (domainname*)"\x1" "0" "\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; + + const domainname *d1, *d2, *d3, *d4, *d5, *d6; // Top-level domain, second-level domain, etc. + d1 = d2 = d3 = d4 = d5 = d6 = mDNSNULL; + while (d->c[0]) + { + d6 = d5; d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d; + d = (domainname*)(d->c + 1 + d->c[0]); + } + + if (d1 && SameDomainName(d1, n0)) return(mDNStrue); + if (d4 && SameDomainName(d4, n1)) return(mDNStrue); + if (d6 && SameDomainName(d6, n2)) return(mDNStrue); + return(mDNSfalse); + } + +// 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 +// (length, three data bytes, length, three more data bytes, final zero). +// In the case where a parent domain name is provided, and the given name is a child +// of that parent, CompressedDomainNameLength returns the length of the prefix portion +// 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). +mDNSexport 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((mDNSu16)(src - name->c + 2)); + src += 1 + *src; + if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); + } + return((mDNSu16)(src - name->c + 1)); + } + +// 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 +// 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 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 + + 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 + } + +// 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) + { + 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 (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label... + { + mDNSu8 c = (mDNSu8)*cstr++; // Read the character + if (c == '\\') // If escape character, check next character + { + c = (mDNSu8)*cstr++; // Assume we'll just take the next character + if (mdnsIsDigit(cstr[-1]) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1])) + { // If three decimal digits, + int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal + int v1 = cstr[ 0] - '0'; + int v2 = cstr[ 1] - '0'; + int val = v0 * 100 + v1 * 10 + v2; + if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it + } + } + *ptr++ = c; // Write the character + } + 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 + } + + *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]) + { + 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); + } + +// 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 + } + +// 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 + while (src < end) // While we have characters in the label + { + mDNSu8 c = *src++; + if (esc) + { + 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); + c = (mDNSu8)('0' + (c ) % 10); + } + } + *ptr++ = (char)c; // Copy the character + } + *ptr = 0; // Null-terminate the string + return(ptr); // and return + } + +// Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes) +mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) + { + const mDNSu8 *src = name->c; // Domain name we're reading + const mDNSu8 *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(mDNSNULL); + ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); + if (!ptr) return(mDNSNULL); + src += 1 + *src; + *ptr++ = '.'; // Write the dot after the label + } + + *ptr++ = 0; // Null-terminate the string + return(ptr); // and return + } + +// 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 + +mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel) + { + const mDNSu8 * src = &UTF8Name[1]; + const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0]; + mDNSu8 * ptr = &hostlabel->c[1]; + const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL; + while (src < end) + { + // Delete apostrophes from source name + if (src[0] == '\'') { src++; continue; } // Standard straight single quote + if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99) + { src += 3; continue; } // Unicode curly apostrophe + if (ptr < lim) + { + if (mdnsValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src; + else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-'; + } + src++; + } + while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks + hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]); + } + +#if MDNS_ENFORCE_SERVICE_TYPE_LENGTH +mDNSlocal mDNSBool AllowedServiceNameException(const mDNSu8 *const src) + { + if (SameDomainLabel(src, (mDNSu8*)"\x12_MacOSXDupSuppress")) return(mDNStrue); + LogMsg("Application protocol name %#s too long; see ", src); + return(mDNSfalse); + } +#endif + +mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, + const domainlabel *name, const domainname *type, const domainname *const domain) + { + int i, len; + mDNSu8 *dst = fqdn->c; + 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 && name->c[0]) + { + src = name->c; // Put the service name into the domain name + len = *src; + 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 MDNS_ENFORCE_SERVICE_TYPE_LENGTH + if (len < 2 || len > 15) + if (!AllowedServiceNameException(src)) // If length not legal, check our grandfather-exceptions list + { errormsg="Application protocol name must be underscore plus 1-14 characters"; goto fail; } +#else + if (len < 2 || len >= 0x40) { errormsg="Application protocol name should be underscore plus 1-14 characters"; goto fail; } +#endif + if (src[1] != '_') { errormsg="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="Application protocol name must contain only letters, digits, and hyphens"; goto fail; } + for (i=0; i<=len; i++) *dst++ = *src++; + + len = *src; + 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) { errormsg="Service type must have only two labels"; goto fail; } + + *dst = 0; + if (!domain->c[0]) { errormsg="Service domain must be non-empty"; goto fail; } + 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, + domainlabel *const name, domainname *const type, domainname *const domain) + { + int i, len; + const mDNSu8 *src = fqdn->c; + const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME; + mDNSu8 *dst; + + dst = name->c; // Extract the service name from the domain name + len = *src; + if (len >= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + + dst = type->c; // Extract the service type from the domain name + len = *src; + if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + + len = *src; + if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + *dst++ = 0; // Put the null root label on the end of the service type + + dst = domain->c; // Extract the service domain from the domain name + while (*src) + { + len = *src; + if (len >= 0x40) + { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse); } + if (src + 1 + len + 1 >= max) + { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse); } + for (i=0; i<=len; i++) *dst++ = *src++; + } + *dst++ = 0; // Put the null root label on the end + + return(mDNStrue); + } + +// 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. +mDNSexport 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. +mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText) + { + 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]--; } + + // 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). +mDNSexport void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText) + { + mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2") + if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)") + + // 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 (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; } + else { name->c[++name->c[0]] = '-'; } + + while (divisor) + { + name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor); + 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 COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Resource Record Utility Functions +#endif + +mDNSexport mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb) + { + 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); + } + +mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2) + { + 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->rdata->u.name, &r2->rdata->u.name)); + + 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->rdata->u.data, r2->rdata->u.data, r1->rdlength)); + } + } + +mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) + { + 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->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)); + } + +mDNSexport 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(rd->ip)); + case kDNSType_CNAME:// Same as PTR + case kDNSType_NS: // Same as PTR + 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)); + case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name)); + case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) + + CompressedDomainNameLength(&rd->soa.rname, name) + + 5 * sizeof(mDNSOpaque32)); + case kDNSType_OPT: return(rr->rdlength); + default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype); + return(rr->rdlength); + } + } + +mDNSexport 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 + } + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - DNS Message Creation Functions +#endif + +mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags) + { + h->id = id; + h->flags = flags; + h->numQuestions = 0; + h->numAnswers = 0; + h->numAuthorities = 0; + h->numAdditionals = 0; + } + +mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname) + { + const mDNSu8 *result = end - *domname - 1; + + if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label + + // This loop examines each possible starting position in packet, starting end of the packet and working backwards + while (result >= base) + { + // If the length byte and first character of the label match, then check further to see + // if this location in the packet will yield a useful name compression pointer. + if (result[0] == domname[0] && result[1] == domname[1]) + { + const mDNSu8 *name = domname; + const mDNSu8 *targ = result; + while (targ + *name < end) + { + // First see if this label matches + int i; + const mDNSu8 *pointertarget; + for (i=0; i <= *name; i++) if (targ[i] != name[i]) break; + if (i <= *name) break; // If label did not match, bail out + targ += 1 + *name; // Else, did match, so advance target pointer + name += 1 + *name; // and proceed to check next label + if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match! + if (*name == 0) break; // If no more labels to match, we failed, so bail out + + // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches + if (targ[0] < 0x40) continue; // If length value, continue to check next label + if (targ[0] < 0xC0) break; // If 40-BF, not valid + if (targ+1 >= end) break; // Second byte not present! + pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1]; + if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet + if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte + targ = pointertarget; + } + } + result--; // We failed to match at this search position, so back up the tentative result pointer and try again + } + return(mDNSNULL); + } + +// Put a string of dot-separated labels as length-prefixed labels +// domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't) +// msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers) +// end points to the end of the message so far +// ptr points to where we want to put the name +// limit points to one byte past the end of the buffer that we must not overrun +// domainname is the name to put +mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, + mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name) + { + const mDNSu8 *const base = (const mDNSu8 *)msg; + const mDNSu8 * np = name->c; + const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid + const mDNSu8 * pointer = mDNSNULL; + const mDNSu8 *const searchlimit = ptr; + + 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) + { 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 + { + mDNSu16 offset = (mDNSu16)(pointer - base); + *ptr++ = (mDNSu8)(0xC0 | (offset >> 8)); + *ptr++ = (mDNSu8)( offset & 0xFF); + return(ptr); + } + else // Else copy one label and try again + { + int i; + mDNSu8 len = *np++; + if (ptr + 1 + len >= limit) return(mDNSNULL); + *ptr++ = len; + for (i=0; i> 8 ) & 0xFF); + ptr[1] = (mDNSu8)((val ) & 0xFF); + return ptr + sizeof(mDNSOpaque16); + } + +mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr) + { + int nput = 0; + rdataOpt *opt; + + while (nput < rr->rdlength) + { + // check if space for opt/optlen + if (ptr + (2 * sizeof(mDNSu16)) > limit) goto space_err; + (mDNSu8 *)opt = rr->rdata->u.data + nput; + ptr = putVal16(ptr, opt->opt); + ptr = putVal16(ptr, opt->optlen); + nput += 2 * sizeof(mDNSu16); + if (opt->opt == kDNSOpt_LLQ) + { + if (ptr + sizeof(LLQOptData) > limit) goto space_err; + ptr = putVal16(ptr, opt->OptData.llq.vers); + ptr = putVal16(ptr, opt->OptData.llq.llqOp); + ptr = putVal16(ptr, opt->OptData.llq.err); + mDNSPlatformMemCopy(opt->OptData.llq.id, ptr, 8); // 8-byte id + ptr += 8; + *(mDNSOpaque32 *)ptr = mDNSOpaque32fromIntVal(opt->OptData.llq.lease); + ptr += sizeof(mDNSOpaque32); + nput += sizeof(LLQOptData); + } + else if (opt->opt == kDNSOpt_Lease) + { + if (ptr + sizeof(mDNSs32) > limit) goto space_err; + *(mDNSOpaque32 *)ptr = mDNSOpaque32fromIntVal(opt->OptData.lease); + ptr += sizeof(mDNSs32); + nput += sizeof(mDNSs32); + } + else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; } + } + + return ptr; + + space_err: + LogMsg("ERROR: putOptRData - out of space"); + return mDNSNULL; + } + +mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr) + { + mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]); + *ptr += sizeof(mDNSOpaque16); + return val; + } + +mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr, mDNSu16 pktRDLen) + { + int nread = 0; + rdataOpt *opt; + + while (nread < pktRDLen) + { + opt = (rdataOpt *)(rr->rdata->u.data + nread); + // space for opt + optlen + if (nread + (2 * sizeof(mDNSu16)) > rr->rdata->MaxRDLength) goto space_err; + opt->opt = getVal16(&ptr); + opt->optlen = getVal16(&ptr); + nread += 2 * sizeof(mDNSu16); + if (opt->opt == kDNSOpt_LLQ) + { + if ((unsigned)(limit - ptr) < sizeof(LLQOptData)) goto space_err; + opt->OptData.llq.vers = getVal16(&ptr); + opt->OptData.llq.llqOp = getVal16(&ptr); + opt->OptData.llq.err = getVal16(&ptr); + mDNSPlatformMemCopy(ptr, opt->OptData.llq.id, 8); + ptr += 8; + opt->OptData.llq.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); + if (opt->OptData.llq.lease > 0x70000000UL / mDNSPlatformOneSecond) + opt->OptData.llq.lease = 0x70000000UL / mDNSPlatformOneSecond; + ptr += sizeof(mDNSOpaque32); + nread += sizeof(LLQOptData); + } + else if (opt->opt == kDNSOpt_Lease) + { + if ((unsigned)(limit - ptr) < sizeof(mDNSs32)) goto space_err; + + opt->OptData.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); + if (opt->OptData.lease > 0x70000000UL / mDNSPlatformOneSecond) + opt->OptData.lease = 0x70000000UL / mDNSPlatformOneSecond; + ptr += sizeof(mDNSs32); + nread += sizeof(mDNSs32); + } + else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; } + } + + rr->rdlength = pktRDLen; + return ptr; + + space_err: + LogMsg("ERROR: getLLQRdata - out of space"); + return mDNSNULL; + } + +mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr) + { + switch (rr->rrtype) + { + case kDNSType_A: if (rr->rdlength != 4) + { + debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); + return(mDNSNULL); + } + if (ptr + 4 > limit) return(mDNSNULL); + *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, &rr->rdata->u.name)); + + 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)); + + case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority >> 8); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority & 0xFF); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8); + *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight & 0xFF); + *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)); + case kDNSType_OPT: return putOptRData(ptr, limit, rr); + + default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype); + // Fall through to common code below + case kDNSType_HINFO: + case kDNSType_TXT: + case kDNSType_TSIG: if (ptr + rr->rdlength > limit) return(mDNSNULL); + mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength); + return(ptr + rr->rdlength); + } + } + +mDNSexport mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl) + { + mDNSu8 *endofrdata; + 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, + // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet + if (msg->h.numAnswers || msg->h.numAuthorities || msg->h.numAdditionals) + limit = msg->data + NormalMaxDNSMessageData; + + if (rr->RecordType == kDNSRecordTypeUnregistered) + { + LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); + return(ptr); + } + + 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); + ptr[1] = (mDNSu8)(rr->rrtype & 0xFF); + ptr[2] = (mDNSu8)(rr->rrclass >> 8); + ptr[3] = (mDNSu8)(rr->rrclass & 0xFF); + ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF); + ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF); + ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF); + ptr[7] = (mDNSu8)( ttl & 0xFF); + 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 = (mDNSu16)(endofrdata - ptr - 10); + ptr[8] = (mDNSu8)(actualLength >> 8); + ptr[9] = (mDNSu8)(actualLength & 0xFF); + + if (count) (*count)++; + else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); + return(endofrdata); + } + +mDNSexport 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)); + } + +mDNSexport mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, + mDNSu16 *count, const AuthRecord *rr) + { + ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->resrec.name); + if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type + ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF); + ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class + ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF); + ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero + ptr[8] = ptr[9] = 0; // RDATA length is zero + (*count)++; + return(ptr + 10); + } + +mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass) + { + ptr = putDomainNameAsLabels(msg, ptr, limit, name); + if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rrtype >> 8); + ptr[1] = (mDNSu8)(rrtype & 0xFF); + ptr[2] = (mDNSu8)(rrclass >> 8); + ptr[3] = (mDNSu8)(rrclass & 0xFF); + msg->h.numQuestions++; + return(ptr+4); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNS Message Parsing Functions +#endif + +mDNSexport 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); + } + +mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength) + { + 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; + } + +mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end) + { + mDNSu16 total = 0; + + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } + + while (1) // Read sequence of labels + { + const mDNSu8 len = *ptr++; // Read length of this label + if (len == 0) return(ptr); // If length is zero, that means this name is complete + switch (len & 0xC0) + { + case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label + { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } + if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label + { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); } + ptr += len; + total += 1 + len; + break; + + case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL); + case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL); + case 0xC0: return(ptr+1); + } + } + } + +// Routine to fetch an FQDN from the DNS message, following compression pointers if necessary. +mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, + domainname *const name) + { + const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers + mDNSu8 *np = name->c; // Name pointer + const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer + + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } + + *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) + + while (1) // Read sequence of labels + { + const mDNSu8 len = *ptr++; // Read length of this label + if (len == 0) break; // If length is zero, that means this name is complete + switch (len & 0xC0) + { + int i; + mDNSu16 offset; + + case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label + { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } + if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label + { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); } + *np++ = len; + for (i=0; ic); + return(mDNSNULL); + + case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL); + + case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++); + if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers + ptr = (mDNSu8 *)msg + offset; + if (ptr < (mDNSu8*)msg || ptr >= end) + { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); } + if (*ptr & 0xC0) + { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); } + break; + } + } + + if (nextbyte) return(nextbyte); + else return(ptr); + } + +mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) + { + mDNSu16 pktrdlength; + + ptr = skipDomainName(msg, ptr, end); + if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); } + + if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } + pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); + ptr += 10; + if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + + return(ptr + pktrdlength); + } + +mDNSexport const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, + const mDNSu8 * const end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, CacheRecord *rr, RData *RDataStorage) + { + mDNSu16 pktrdlength; + + rr->next = mDNSNULL; + rr->resrec.RecordType = RecordType; + + 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->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 && (mDNSs32)rr->resrec.rroriginalttl != -1) + 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->resrec.RecordType |= kDNSRecordTypePacketUniqueMask; + ptr += 10; + if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + + if (RDataStorage) + rr->resrec.rdata = RDataStorage; + else + { + rr->resrec.rdata = (RData*)&rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); + } + + switch (rr->resrec.rrtype) + { + 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:// Same as PTR + case kDNSType_NS: + 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_NULL: //Same as TXT + case kDNSType_HINFO://Same as TXT + case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength) + { + debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)", + DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); + return(mDNSNULL); + } + rr->resrec.rdlength = pktrdlength; + mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength); + break; + + case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6)); + break; + + 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; + + case kDNSType_SOA: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname) || + !getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname)) + { debugf("GetResourceRecord: Malformed SOA RDATA mname/rname"); return mDNSNULL; } + if ((unsigned)(end - ptr) < 5 * sizeof(mDNSOpaque32)) + { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL; } + rr->resrec.rdata->u.soa.serial.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4; + rr->resrec.rdata->u.soa.refresh.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4; + rr->resrec.rdata->u.soa.retry.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4; + rr->resrec.rdata->u.soa.expire.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; ptr += 4; + rr->resrec.rdata->u.soa.min.NotAnInteger = ((mDNSOpaque32 *)ptr)->NotAnInteger; + break; + + case kDNSType_OPT: getOptRdata(ptr, end, &rr->resrec, pktrdlength); break; + + default: if (pktrdlength > rr->resrec.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); + } + 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->resrec.rdlength = pktrdlength; + mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength); + break; + } + + rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); + + return(ptr + pktrdlength); + } + +mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) + { + ptr = skipDomainName(msg, ptr, end); + if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); } + if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } + return(ptr+4); + } + +mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, + DNSQuestion *question) + { + 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->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); + } + +mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end) + { + int i; + const mDNSu8 *ptr = msg->data; + for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end); + return(ptr); + } + +mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end) + { + int i; + const mDNSu8 *ptr = LocateAnswers(msg, end); + for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end); + return(ptr); + } + +mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end) + { + int i; + const mDNSu8 *ptr = LocateAuthorities(msg, end); + for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end); + return (ptr); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - Packet Sending Functions +#endif + +mDNSlocal mStatus sendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo) + { + mStatus status; + int nsent; + mDNSs32 msglen; + mDNSu8 lenbuf[2]; + mDNSu16 numQuestions = msg->h.numQuestions; + mDNSu16 numAnswers = msg->h.numAnswers; + mDNSu16 numAuthorities = msg->h.numAuthorities; + mDNSu16 numAdditionals = msg->h.numAdditionals; + mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; + + // Put all the integer values in IETF byte-order (MSB first, LSB second) + *ptr++ = (mDNSu8)(numQuestions >> 8); + *ptr++ = (mDNSu8)(numQuestions & 0xFF); + *ptr++ = (mDNSu8)(numAnswers >> 8); + *ptr++ = (mDNSu8)(numAnswers & 0xFF); + *ptr++ = (mDNSu8)(numAuthorities >> 8); + *ptr++ = (mDNSu8)(numAuthorities & 0xFF); + *ptr++ = (mDNSu8)(numAdditionals >> 8); + *ptr++ = (mDNSu8)(numAdditionals & 0xFF); + + if (authInfo) + { + end = DNSDigest_SignMessage(msg, &end, &numAdditionals, authInfo); + if (!end) return mStatus_UnknownErr; + } + + // Send the packet on the wire + + if (sd >= 0) + { + msglen = (mDNSu16)(end - (mDNSu8 *)msg); + lenbuf[0] = (mDNSu8)(msglen >> 8); // host->network byte conversion + lenbuf[1] = (mDNSu8)(msglen & 0xFF); + nsent = mDNSPlatformWriteTCP(sd, (char*)lenbuf, 2); + //!!!KRS make sure kernel is sending these as 1 packet! + if (nsent != 2) goto tcp_error; + nsent = mDNSPlatformWriteTCP(sd, (char *)msg, msglen); + if (nsent != msglen) goto tcp_error; + status = mStatus_NoError; + } + else + { + status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, dst, dstport); + } + + // Put all the integer values back the way they were before we return + msg->h.numQuestions = numQuestions; + msg->h.numAnswers = numAnswers; + msg->h.numAuthorities = numAuthorities; + msg->h.numAdditionals = (mDNSu16)(authInfo ? numAdditionals - 1 : numAdditionals); + + return(status); + + tcp_error: + LogMsg("sendDNSMessage: error sending message over tcp"); + return mStatus_UnknownErr; + + } + +mDNSexport mStatus mDNSSendDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd) + { + if (sd < 0) { LogMsg("mDNSSendDNSMessage_tcp: invalid desciptor %d", sd); return mStatus_UnknownErr; } + return sendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, mDNSNULL); + } + +mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport) + { + return sendDNSMessage(m, msg, end, InterfaceID, dst, dstport, -1, mDNSNULL); + } + +mDNSexport mStatus mDNSSendSignedDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, uDNS_AuthInfo *authInfo) + { + return sendDNSMessage(m, msg, end, InterfaceID, dst, dstport, -1, authInfo); + } + +mDNSexport mStatus mDNSSendSignedDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd, uDNS_AuthInfo *authInfo) + { + if (sd < 0) { LogMsg("mDNSSendDNSMessage_tcp: invalid desciptor %d", sd); return mStatus_UnknownErr; } + return sendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, authInfo); + } diff --git a/mDNSCore/DNSCommon.h b/mDNSCore/DNSCommon.h new file mode 100644 index 0000000..674bb7a --- /dev/null +++ b/mDNSCore/DNSCommon.h @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSCommon.h,v $ +Revision 1.14 2004/05/28 23:42:36 ksekar +: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.13 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.12 2004/04/22 04:03:59 cheshire +Headers should use "extern" declarations, not "mDNSexport" + +Revision 1.11 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.10 2004/03/13 01:57:33 ksekar +: DynDNS: Dynamic update of service records + +Revision 1.9 2004/02/21 08:56:58 bradley +Wrap prototypes with extern "C" for C++ builds. + +Revision 1.8 2004/02/06 23:04:18 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.7 2004/02/03 19:47:36 ksekar +Added an asyncronous state machine mechanism to uDNS.c, including +calls to find the parent zone for a domain name. Changes include code +in repository previously dissabled via "#if 0 //incomplete". Codepath +is currently unused, and will be called to create update records, etc. + +Revision 1.6 2004/01/27 20:15:22 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.5 2004/01/24 03:40:56 cheshire +Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSClientAPI.h so clients can use it + +Revision 1.4 2004/01/24 03:38:27 cheshire +Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport" + +Revision 1.3 2004/01/23 23:23:14 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.2 2004/01/21 21:12:23 cheshire +Add missing newline at end of file to make Unix tools happier + +Revision 1.1 2003/12/13 03:05:27 ksekar +: DynDNS: Unicast query of service records + + + */ + +#ifndef __DNSCOMMON_H_ +#define __DNSCOMMON_H_ + +#include "mDNSClientAPI.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - DNS Protocol Constants +#endif + +typedef enum + { + kDNSFlag0_QR_Mask = 0x80, // Query or response? + kDNSFlag0_QR_Query = 0x00, + kDNSFlag0_QR_Response = 0x80, + + kDNSFlag0_OP_Mask = 0x78, // Operation type + kDNSFlag0_OP_StdQuery = 0x00, + kDNSFlag0_OP_Iquery = 0x08, + kDNSFlag0_OP_Status = 0x10, + kDNSFlag0_OP_Unused3 = 0x18, + kDNSFlag0_OP_Notify = 0x20, + kDNSFlag0_OP_Update = 0x28, + + kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask, + + kDNSFlag0_AA = 0x04, // Authoritative Answer? + kDNSFlag0_TC = 0x02, // Truncated? + kDNSFlag0_RD = 0x01, // Recursion Desired? + kDNSFlag1_RA = 0x80, // Recursion Available? + + kDNSFlag1_Zero = 0x40, // Reserved; must be zero + kDNSFlag1_AD = 0x20, // Authentic Data [RFC 2535] + kDNSFlag1_CD = 0x10, // Checking Disabled [RFC 2535] + + kDNSFlag1_RC = 0x0F, // Response code + kDNSFlag1_RC_NoErr = 0x00, + kDNSFlag1_RC_FmtErr = 0x01, + kDNSFlag1_RC_SrvErr = 0x02, + kDNSFlag1_RC_NXDomain = 0x03, + kDNSFlag1_RC_NotImpl = 0x04, + kDNSFlag1_RC_Refused = 0x05, + kDNSFlag1_RC_YXDomain = 0x06, + kDNSFlag1_RC_YXRRSet = 0x07, + kDNSFlag1_RC_NXRRSet = 0x08, + kDNSFlag1_RC_NotAuth = 0x09, + kDNSFlag1_RC_NotZone = 0x0A + } DNS_Flags; + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - General Utility Functions +#endif + +extern const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf); +extern mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf); + +extern mDNSu32 mDNSRandom(mDNSu32 max); + +#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) + + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Domain Name Utility Functions +#endif + +#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)) + + +#define mdnsValidHostChar(X, notfirst, notlast) (mdnsIsLetter(X) || mdnsIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') ) + +extern mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent); + +extern mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText); +extern mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText); +extern void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText); +extern void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); +#define ValidateDomainName(N) (DomainNameLength(N) <= MAX_DOMAIN_NAME) + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Resource Record Utility Functions +#endif + +extern mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb); + +extern mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2); + +extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); + + +extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate); + +#define GetRRDomainNameTarget(RR) ( \ + ((RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_NS) \ + ? &(RR)->rdata->u.name : \ + ((RR)->rrtype == kDNSType_SRV ) ? &(RR)->rdata->u.srv.target : mDNSNULL ) + +extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd); + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - DNS Message Creation Functions +#endif + +extern void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags); +extern const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname); + +extern mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name); + +extern mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr); + +extern mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl); + +extern mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 maxttl); + +extern mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr); + +extern mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass); + +#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - DNS Message Parsing Functions +#endif + +extern mDNSu32 DomainNameHashValue(const domainname *const name); + +extern void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength); + + +extern const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end); + +extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, + domainname *const name); + +extern const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); + +extern const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, + const mDNSu8 * const end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, CacheRecord *rr, RData *RDataStorage); + +extern const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); + +extern const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, + DNSQuestion *question); + +extern const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end); + +extern const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end); + +extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end); + +#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)) + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - +#pragma mark - Packet Sending Functions +#endif + +extern mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport); +extern mStatus mDNSSendDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd); +extern mStatus mDNSSendSignedDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, uDNS_AuthInfo *authInfo); +extern mStatus mDNSSendSignedDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd, uDNS_AuthInfo *authInfo); + +#ifdef __cplusplus + } +#endif + +#endif // __DNSCOMMON_H_ diff --git a/mDNSCore/DNSDigest.c b/mDNSCore/DNSDigest.c new file mode 100644 index 0000000..370e706 --- /dev/null +++ b/mDNSCore/DNSDigest.c @@ -0,0 +1,1440 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSDigest.c,v $ +Revision 1.6 2004/06/02 00:17:46 ksekar +Referenced original OpenSSL license headers in source file description. + +Revision 1.5 2004/05/20 18:37:37 cheshire +Fix compiler warnings + +Revision 1.4 2004/04/22 20:28:20 cheshire +Use existing facility of PutResourceRecordTTL() to update count field for us + +Revision 1.3 2004/04/22 03:05:28 cheshire +kDNSClass_ANY should be kDNSQClass_ANY + +Revision 1.2 2004/04/15 00:51:28 bradley +Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. +Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. + +Revision 1.1 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + + + +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mDNSClientAPI.h" +#include "DNSCommon.h" + +// Disable certain benign warnings with Microsoft compilers +#if(defined(_MSC_VER)) + // 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) +#endif + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - MD5 Hash Functions +#endif + + +/* The source for the has is derived CommonCrypto files CommonDigest.h, md32_common.h, md5_locl.h, md5_locl.h, and openssl/md5.h. + * The following changes have been made to the original sources: + * replaced CC_LONG w/ mDNSu32 + * replaced CC_MD5* with MD5* + * replaced CC_LONG w/ mDNSu32, removed conditional #defines from md5.h + * removed extern decls for MD5_Init/Update/Final from CommonDigest.h + * removed APPLE_COMMON_DIGEST specific #defines from md5_locl.h + * + * Note: machine archetecure specific conditionals from the original sources are turned off, but are left in the code + * to aid in platform-specific optimizations and debugging. + * Sources originally distributed under the following license headers: + * CommonDigest.c - APSL + * + * md32_Common.h + * ==================================================================== + * Copyright (c) 1999-2002 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * md5_dgst.c, md5_locl.h + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + * Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + * + */ + +//from CommonDigest.h + +#define MD5_DIGEST_LENGTH 16 /* digest length in bytes */ +#define MD5_BLOCK_BYTES 64 /* block size in bytes */ +#define MD5_BLOCK_LONG (MD5_BLOCK_BYTES / sizeof(mDNSu32)) + +typedef struct MD5state_st +{ + mDNSu32 A,B,C,D; + mDNSu32 Nl,Nh; + mDNSu32 data[MD5_BLOCK_LONG]; + int num; +} MD5_CTX; + + +// from openssl/md5.h + +#define MD5_CBLOCK 64 +#define MD5_LBLOCK (MD5_CBLOCK/4) +#define MD5_DIGEST_LENGTH 16 + +int MD5_Init(MD5_CTX *c); +int MD5_Update(MD5_CTX *c, const void *data, unsigned long len); +int MD5_Final(unsigned char *md, MD5_CTX *c); +void MD5_Transform(MD5_CTX *c, const unsigned char *b); + +// From md5_locl.h + +#ifndef MD5_LONG_LOG2 +#define MD5_LONG_LOG2 2 /* default to 32 bits */ +#endif + +#ifdef MD5_ASM +# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__INTEL__) +# define md5_block_host_order md5_block_asm_host_order +# elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC) + void md5_block_asm_data_order_aligned (MD5_CTX *c, const mDNSu32 *p,int num); +# define HASH_BLOCK_DATA_ORDER_ALIGNED md5_block_asm_data_order_aligned +# endif +#endif + +void md5_block_host_order (MD5_CTX *c, const void *p,int num); +void md5_block_data_order (MD5_CTX *c, const void *p,int num); + +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__INTEL__) +/* + * *_block_host_order is expected to handle aligned data while + * *_block_data_order - unaligned. As algorithm and host (x86) + * are in this case of the same "endianness" these two are + * otherwise indistinguishable. But normally you don't want to + * call the same function because unaligned access in places + * where alignment is expected is usually a "Bad Thing". Indeed, + * on RISCs you get punished with BUS ERROR signal or *severe* + * performance degradation. Intel CPUs are in turn perfectly + * capable of loading unaligned data without such drastic side + * effect. Yes, they say it's slower than aligned load, but no + * exception is generated and therefore performance degradation + * is *incomparable* with RISCs. What we should weight here is + * costs of unaligned access against costs of aligning data. + * According to my measurements allowing unaligned access results + * in ~9% performance improvement on Pentium II operating at + * 266MHz. I won't be surprised if the difference will be higher + * on faster systems:-) + * + * + */ +#define md5_block_data_order md5_block_host_order +#endif + +#define DATA_ORDER_IS_LITTLE_ENDIAN + +#define HASH_LONG mDNSu32 +#define HASH_LONG_LOG2 MD5_LONG_LOG2 +#define HASH_CTX MD5_CTX +#define HASH_CBLOCK MD5_CBLOCK +#define HASH_LBLOCK MD5_LBLOCK + +#define HASH_UPDATE MD5_Update +#define HASH_TRANSFORM MD5_Transform +#define HASH_FINAL MD5_Final + +#define HASH_MAKE_STRING(c,s) do { \ + unsigned long ll; \ + ll=(c)->A; HOST_l2c(ll,(s)); \ + ll=(c)->B; HOST_l2c(ll,(s)); \ + ll=(c)->C; HOST_l2c(ll,(s)); \ + ll=(c)->D; HOST_l2c(ll,(s)); \ + } while (0) +#define HASH_BLOCK_HOST_ORDER md5_block_host_order +#if !defined(L_ENDIAN) || defined(md5_block_data_order) +#define HASH_BLOCK_DATA_ORDER md5_block_data_order +/* + * Little-endians (Intel and Alpha) feel better without this. + * It looks like memcpy does better job than generic + * md5_block_data_order on copying-n-aligning input data. + * But frankly speaking I didn't expect such result on Alpha. + * On the other hand I've got this with egcs-1.0.2 and if + * program is compiled with another (better?) compiler it + * might turn out other way around. + * + * + */ +#endif + + +// from md32_common.h + +/* + * This is a generic 32 bit "collector" for message digest algorithms. + * Whenever needed it collects input character stream into chunks of + * 32 bit values and invokes a block function that performs actual hash + * calculations. + * + * Porting guide. + * + * Obligatory macros: + * + * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN + * this macro defines byte order of input stream. + * HASH_CBLOCK + * size of a unit chunk HASH_BLOCK operates on. + * HASH_LONG + * has to be at lest 32 bit wide, if it's wider, then + * HASH_LONG_LOG2 *has to* be defined along + * HASH_CTX + * context structure that at least contains following + * members: + * typedef struct { + * ... + * HASH_LONG Nl,Nh; + * HASH_LONG data[HASH_LBLOCK]; + * int num; + * ... + * } HASH_CTX; + * HASH_UPDATE + * name of "Update" function, implemented here. + * HASH_TRANSFORM + * name of "Transform" function, implemented here. + * HASH_FINAL + * name of "Final" function, implemented here. + * HASH_BLOCK_HOST_ORDER + * name of "block" function treating *aligned* input message + * in host byte order, implemented externally. + * HASH_BLOCK_DATA_ORDER + * name of "block" function treating *unaligned* input message + * in original (data) byte order, implemented externally (it + * actually is optional if data and host are of the same + * "endianess"). + * HASH_MAKE_STRING + * macro convering context variables to an ASCII hash string. + * + * Optional macros: + * + * B_ENDIAN or L_ENDIAN + * defines host byte-order. + * HASH_LONG_LOG2 + * defaults to 2 if not states otherwise. + * HASH_LBLOCK + * assumed to be HASH_CBLOCK/4 if not stated otherwise. + * HASH_BLOCK_DATA_ORDER_ALIGNED + * alternative "block" function capable of treating + * aligned input message in original (data) order, + * implemented externally. + * + * MD5 example: + * + * #define DATA_ORDER_IS_LITTLE_ENDIAN + * + * #define HASH_LONG mDNSu32 + * #define HASH_LONG_LOG2 mDNSu32_LOG2 + * #define HASH_CTX MD5_CTX + * #define HASH_CBLOCK MD5_CBLOCK + * #define HASH_LBLOCK MD5_LBLOCK + * #define HASH_UPDATE MD5_Update + * #define HASH_TRANSFORM MD5_Transform + * #define HASH_FINAL MD5_Final + * #define HASH_BLOCK_HOST_ORDER md5_block_host_order + * #define HASH_BLOCK_DATA_ORDER md5_block_data_order + * + * + */ + +#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) +#error "DATA_ORDER must be defined!" +#endif + +#ifndef HASH_CBLOCK +#error "HASH_CBLOCK must be defined!" +#endif +#ifndef HASH_LONG +#error "HASH_LONG must be defined!" +#endif +#ifndef HASH_CTX +#error "HASH_CTX must be defined!" +#endif + +#ifndef HASH_UPDATE +#error "HASH_UPDATE must be defined!" +#endif +#ifndef HASH_TRANSFORM +#error "HASH_TRANSFORM must be defined!" +#endif +#ifndef HASH_FINAL +#error "HASH_FINAL must be defined!" +#endif + +#ifndef HASH_BLOCK_HOST_ORDER +#error "HASH_BLOCK_HOST_ORDER must be defined!" +#endif + +#if 0 +/* + * Moved below as it's required only if HASH_BLOCK_DATA_ORDER_ALIGNED + * isn't defined. + */ +#ifndef HASH_BLOCK_DATA_ORDER +#error "HASH_BLOCK_DATA_ORDER must be defined!" +#endif +#endif + +#ifndef HASH_LBLOCK +#define HASH_LBLOCK (HASH_CBLOCK/4) +#endif + +#ifndef HASH_LONG_LOG2 +#define HASH_LONG_LOG2 2 +#endif + +/* + * Engage compiler specific rotate intrinsic function if available. + */ +#undef ROTATE +#ifndef PEDANTIC +# if 0 /* defined(_MSC_VER) */ +# define ROTATE(a,n) _lrotl(a,n) +# elif defined(__MWERKS__) +# if defined(__POWERPC__) +# define ROTATE(a,n) (unsigned MD32_REG_T)__rlwinm((int)a,n,0,31) +# elif defined(__MC68K__) + /* Motorola specific tweak. */ +# define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) ) +# else +# define ROTATE(a,n) __rol(a,n) +# endif +# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) + /* + * Some GNU C inline assembler templates. Note that these are + * rotates by *constant* number of bits! But that's exactly + * what we need here... + * + * + */ +# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "roll %1,%0" \ + : "=r"(ret) \ + : "I"(n), "0"(a) \ + : "cc"); \ + ret; \ + }) +# elif defined(__powerpc) || defined(__ppc) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "rlwinm %0,%1,%2,0,31" \ + : "=r"(ret) \ + : "r"(a), "I"(n)); \ + ret; \ + }) +# endif +# endif + +/* + * Engage compiler specific "fetch in reverse byte order" + * intrinsic function if available. + */ +# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) + /* some GNU C inline assembler templates by */ +# if (defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)) && !defined(I386_ONLY) +# define BE_FETCH32(a) ({ register unsigned int l=(a);\ + asm ( \ + "bswapl %0" \ + : "=r"(l) : "0"(l)); \ + l; \ + }) +# elif defined(__powerpc) +# define LE_FETCH32(a) ({ register unsigned int l; \ + asm ( \ + "lwbrx %0,0,%1" \ + : "=r"(l) \ + : "r"(a)); \ + l; \ + }) + +# elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC) +# define LE_FETCH32(a) ({ register unsigned int l; \ + asm ( \ + "lda [%1]#ASI_PRIMARY_LITTLE,%0"\ + : "=r"(l) \ + : "r"(a)); \ + l; \ + }) +# endif +# endif +#endif /* PEDANTIC */ + +#if HASH_LONG_LOG2==2 /* Engage only if sizeof(HASH_LONG)== 4 */ +/* A nice byte order reversal from Wei Dai */ +#ifdef ROTATE +/* 5 instructions with rotate instruction, else 9 */ +#define REVERSE_FETCH32(a,l) ( \ + l=*(const HASH_LONG *)(a), \ + ((ROTATE(l,8)&0x00FF00FF)|(ROTATE((l&0x00FF00FF),24))) \ + ) +#else +/* 6 instructions with rotate instruction, else 8 */ +#define REVERSE_FETCH32(a,l) ( \ + l=*(const HASH_LONG *)(a), \ + l=(((l>>8)&0x00FF00FF)|((l&0x00FF00FF)<<8)), \ + ROTATE(l,16) \ + ) +/* + * Originally the middle line started with l=(((l&0xFF00FF00)>>8)|... + * It's rewritten as above for two reasons: + * - RISCs aren't good at long constants and have to explicitely + * compose 'em with several (well, usually 2) instructions in a + * register before performing the actual operation and (as you + * already realized:-) having same constant should inspire the + * compiler to permanently allocate the only register for it; + * - most modern CPUs have two ALUs, but usually only one has + * circuitry for shifts:-( this minor tweak inspires compiler + * to schedule shift instructions in a better way... + * + * + */ +#endif +#endif + +#ifndef ROTATE +#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) +#endif + +/* + * Make some obvious choices. E.g., HASH_BLOCK_DATA_ORDER_ALIGNED + * and HASH_BLOCK_HOST_ORDER ought to be the same if input data + * and host are of the same "endianess". It's possible to mask + * this with blank #define HASH_BLOCK_DATA_ORDER though... + * + * + */ +#if defined(B_ENDIAN) +# if defined(DATA_ORDER_IS_BIG_ENDIAN) +# if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2 +# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER +# endif +# elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) +# ifndef HOST_FETCH32 +# ifdef LE_FETCH32 +# define HOST_FETCH32(p,l) LE_FETCH32(p) +# elif defined(REVERSE_FETCH32) +# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# endif +# endif +# endif +#elif defined(L_ENDIAN) +# if defined(DATA_ORDER_IS_LITTLE_ENDIAN) +# if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2 +# define HASH_BLOCK_DATA_ORDER_ALIGNED HASH_BLOCK_HOST_ORDER +# endif +# elif defined(DATA_ORDER_IS_BIG_ENDIAN) +# ifndef HOST_FETCH32 +# ifdef BE_FETCH32 +# define HOST_FETCH32(p,l) BE_FETCH32(p) +# elif defined(REVERSE_FETCH32) +# define HOST_FETCH32(p,l) REVERSE_FETCH32(p,l) +# endif +# endif +# endif +#endif + +#if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) +#ifndef HASH_BLOCK_DATA_ORDER +#error "HASH_BLOCK_DATA_ORDER must be defined!" +#endif +#endif + +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + +#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++))) ), \ + l) +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + case 3: l|=((unsigned long)(*((c)++))); \ + } } +#define HOST_p_c2l_p(c,l,sc,len) { \ + switch (sc) { \ + case 0: l =((unsigned long)(*((c)++)))<<24; \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<<16; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<< 8; \ + } } +/* NOTE the pointer is not incremented at the end of this */ +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<< 8; \ + case 2: l|=((unsigned long)(*(--(c))))<<16; \ + case 1: l|=((unsigned long)(*(--(c))))<<24; \ + } } +#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff), \ + l) + +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + +#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<<24), \ + l) +#define HOST_p_c2l(c,l,n) { \ + switch (n) { \ + case 0: l =((unsigned long)(*((c)++))); \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + case 3: l|=((unsigned long)(*((c)++)))<<24; \ + } } +#define HOST_p_c2l_p(c,l,sc,len) { \ + switch (sc) { \ + case 0: l =((unsigned long)(*((c)++))); \ + if (--len == 0) break; \ + case 1: l|=((unsigned long)(*((c)++)))<< 8; \ + if (--len == 0) break; \ + case 2: l|=((unsigned long)(*((c)++)))<<16; \ + } } +/* NOTE the pointer is not incremented at the end of this */ +#define HOST_c2l_p(c,l,n) { \ + l=0; (c)+=n; \ + switch (n) { \ + case 3: l =((unsigned long)(*(--(c))))<<16; \ + case 2: l|=((unsigned long)(*(--(c))))<< 8; \ + case 1: l|=((unsigned long)(*(--(c)))); \ + } } +#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff), \ + l) + +#endif + +/* + * Time for some action:-) + */ + +int HASH_UPDATE (HASH_CTX *c, const void *data_, mDNSu32 len) + { + const unsigned char *data=(const unsigned char *)data_; + register HASH_LONG * p; + register unsigned long l; + int sw,sc,ew,ec; + + if (len==0) return 1; + + l=(c->Nl+(len<<3))&0xffffffffL; + /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to + * Wei Dai for pointing it out. */ + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh+=(len>>29); + c->Nl=l; + + if (c->num != 0) + { + p=c->data; + sw=c->num>>2; + sc=c->num&0x03; + + if ((c->num+len) >= HASH_CBLOCK) + { + l=p[sw]; HOST_p_c2l(data,l,sc); p[sw++]=l; + for (; swnum); + c->num=0; + /* drop through and do the rest */ + } + else + { + c->num+=len; + if ((sc+len) < 4) /* ugly, add char's to a word */ + { + l=p[sw]; HOST_p_c2l_p(data,l,sc,len); p[sw]=l; + } + else + { + ew=(c->num>>2); + ec=(c->num&0x03); + if (sc) + l=p[sw]; + HOST_p_c2l(data,l,sc); + p[sw++]=l; + for (; sw < ew; sw++) + { + HOST_c2l(data,l); p[sw]=l; + } + if (ec) + { + HOST_c2l_p(data,l,ec); p[sw]=l; + } + } + return 1; + } + } + + sw=(int)(len/HASH_CBLOCK); + if (sw > 0) + { +#if defined(HASH_BLOCK_DATA_ORDER_ALIGNED) + /* + * Note that HASH_BLOCK_DATA_ORDER_ALIGNED gets defined + * only if sizeof(HASH_LONG)==4. + */ + if ((((unsigned long)data)%4) == 0) + { + /* data is properly aligned so that we can cast it: */ + HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,sw); + sw*=HASH_CBLOCK; + data+=sw; + len-=sw; + } + else +#if !defined(HASH_BLOCK_DATA_ORDER) + while (sw--) + { + memcpy (p=c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED(c,p,1); + data+=HASH_CBLOCK; + len-=HASH_CBLOCK; + } +#endif +#endif +#if defined(HASH_BLOCK_DATA_ORDER) + { + HASH_BLOCK_DATA_ORDER(c,data,sw); + sw*=HASH_CBLOCK; + data+=sw; + len-=sw; + } +#endif + } + + if (len!=0) + { + p = c->data; + c->num = (int)len; + ew=(int)(len>>2); /* words to copy */ + ec=(int)(len&0x03); + for (; ew; ew--,p++) + { + HOST_c2l(data,l); *p=l; + } + HOST_c2l_p(data,l,ec); + *p=l; + } + return 1; + } + + +void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data) + { +#if defined(HASH_BLOCK_DATA_ORDER_ALIGNED) + if ((((unsigned long)data)%4) == 0) + /* data is properly aligned so that we can cast it: */ + HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,1); + else +#if !defined(HASH_BLOCK_DATA_ORDER) + { + memcpy (c->data,data,HASH_CBLOCK); + HASH_BLOCK_DATA_ORDER_ALIGNED (c,c->data,1); + } +#endif +#endif +#if defined(HASH_BLOCK_DATA_ORDER) + HASH_BLOCK_DATA_ORDER (c,data,1); +#endif + } + + +int HASH_FINAL (unsigned char *md, HASH_CTX *c) + { + register HASH_LONG *p; + register unsigned long l; + register int i,j; + static const unsigned char end[4]={0x80,0x00,0x00,0x00}; + const unsigned char *cp=end; + + /* c->num should definitly have room for at least one more byte. */ + p=c->data; + i=c->num>>2; + j=c->num&0x03; + +#if 0 + /* purify often complains about the following line as an + * Uninitialized Memory Read. While this can be true, the + * following p_c2l macro will reset l when that case is true. + * This is because j&0x03 contains the number of 'valid' bytes + * already in p[i]. If and only if j&0x03 == 0, the UMR will + * occur but this is also the only time p_c2l will do + * l= *(cp++) instead of l|= *(cp++) + * Many thanks to Alex Tang for pickup this + * 'potential bug' */ +#ifdef PURIFY + if (j==0) p[i]=0; /* Yeah, but that's not the way to fix it:-) */ +#endif + l=p[i]; +#else + l = (j==0) ? 0 : p[i]; +#endif + HOST_p_c2l(cp,l,j); p[i++]=l; /* i is the next 'undefined word' */ + + if (i>(HASH_LBLOCK-2)) /* save room for Nl and Nh */ + { + if (iNh; + p[HASH_LBLOCK-1]=c->Nl; +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + p[HASH_LBLOCK-2]=c->Nl; + p[HASH_LBLOCK-1]=c->Nh; +#endif + HASH_BLOCK_HOST_ORDER (c,p,1); + +#ifndef HASH_MAKE_STRING +#error "HASH_MAKE_STRING must be defined!" +#else + HASH_MAKE_STRING(c,md); +#endif + + c->num=0; + /* clear stuff, HASH_BLOCK may be leaving some stuff on the stack + * but I'm not worried :-) + OPENSSL_cleanse((void *)c,sizeof(HASH_CTX)); + */ + return 1; + } + +#ifndef MD32_REG_T +#define MD32_REG_T long +/* + * This comment was originaly written for MD5, which is why it + * discusses A-D. But it basically applies to all 32-bit digests, + * which is why it was moved to common header file. + * + * In case you wonder why A-D are declared as long and not + * as mDNSu32. Doing so results in slight performance + * boost on LP64 architectures. The catch is we don't + * really care if 32 MSBs of a 64-bit register get polluted + * with eventual overflows as we *save* only 32 LSBs in + * *either* case. Now declaring 'em long excuses the compiler + * from keeping 32 MSBs zeroed resulting in 13% performance + * improvement under SPARC Solaris7/64 and 5% under AlphaLinux. + * Well, to be honest it should say that this *prevents* + * performance degradation. + * + * Apparently there're LP64 compilers that generate better + * code if A-D are declared int. Most notably GCC-x86_64 + * generates better code. + * + */ +#endif + + +// from md5_locl.h (continued) + +/* +#define F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) +#define G(x,y,z) (((x) & (z)) | ((y) & (~(z)))) +*/ + +/* As pointed out by Wei Dai , the above can be + * simplified to the code below. Wei attributes these optimizations + * to Peter Gutmann's SHS code, and he attributes it to Rich Schroeppel. + */ +#define F(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) +#define G(b,c,d) ((((b) ^ (c)) & (d)) ^ (c)) +#define H(b,c,d) ((b) ^ (c) ^ (d)) +#define I(b,c,d) (((~(d)) | (b)) ^ (c)) + +#define R0(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+F((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; };\ + +#define R1(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+G((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + +#define R2(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+H((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + +#define R3(a,b,c,d,k,s,t) { \ + a+=((k)+(t)+I((b),(c),(d))); \ + a=ROTATE(a,s); \ + a+=b; }; + +// from md5_dgst.c + + +/* Implemented from RFC1321 The MD5 Message-Digest Algorithm + */ + +#define INIT_DATA_A (unsigned long)0x67452301L +#define INIT_DATA_B (unsigned long)0xefcdab89L +#define INIT_DATA_C (unsigned long)0x98badcfeL +#define INIT_DATA_D (unsigned long)0x10325476L + +int MD5_Init(MD5_CTX *c) + { + c->A=INIT_DATA_A; + c->B=INIT_DATA_B; + c->C=INIT_DATA_C; + c->D=INIT_DATA_D; + c->Nl=0; + c->Nh=0; + c->num=0; + return 1; + } + +#ifndef md5_block_host_order +void md5_block_host_order (MD5_CTX *c, const void *data, int num) + { + const mDNSu32 *X=(const mDNSu32 *)data; + register unsigned MD32_REG_T A,B,C,D; + + A=c->A; + B=c->B; + C=c->C; + D=c->D; + + for (;num--;X+=HASH_LBLOCK) + { + /* Round 0 */ + R0(A,B,C,D,X[ 0], 7,0xd76aa478L); + R0(D,A,B,C,X[ 1],12,0xe8c7b756L); + R0(C,D,A,B,X[ 2],17,0x242070dbL); + R0(B,C,D,A,X[ 3],22,0xc1bdceeeL); + R0(A,B,C,D,X[ 4], 7,0xf57c0fafL); + R0(D,A,B,C,X[ 5],12,0x4787c62aL); + R0(C,D,A,B,X[ 6],17,0xa8304613L); + R0(B,C,D,A,X[ 7],22,0xfd469501L); + R0(A,B,C,D,X[ 8], 7,0x698098d8L); + R0(D,A,B,C,X[ 9],12,0x8b44f7afL); + R0(C,D,A,B,X[10],17,0xffff5bb1L); + R0(B,C,D,A,X[11],22,0x895cd7beL); + R0(A,B,C,D,X[12], 7,0x6b901122L); + R0(D,A,B,C,X[13],12,0xfd987193L); + R0(C,D,A,B,X[14],17,0xa679438eL); + R0(B,C,D,A,X[15],22,0x49b40821L); + /* Round 1 */ + R1(A,B,C,D,X[ 1], 5,0xf61e2562L); + R1(D,A,B,C,X[ 6], 9,0xc040b340L); + R1(C,D,A,B,X[11],14,0x265e5a51L); + R1(B,C,D,A,X[ 0],20,0xe9b6c7aaL); + R1(A,B,C,D,X[ 5], 5,0xd62f105dL); + R1(D,A,B,C,X[10], 9,0x02441453L); + R1(C,D,A,B,X[15],14,0xd8a1e681L); + R1(B,C,D,A,X[ 4],20,0xe7d3fbc8L); + R1(A,B,C,D,X[ 9], 5,0x21e1cde6L); + R1(D,A,B,C,X[14], 9,0xc33707d6L); + R1(C,D,A,B,X[ 3],14,0xf4d50d87L); + R1(B,C,D,A,X[ 8],20,0x455a14edL); + R1(A,B,C,D,X[13], 5,0xa9e3e905L); + R1(D,A,B,C,X[ 2], 9,0xfcefa3f8L); + R1(C,D,A,B,X[ 7],14,0x676f02d9L); + R1(B,C,D,A,X[12],20,0x8d2a4c8aL); + /* Round 2 */ + R2(A,B,C,D,X[ 5], 4,0xfffa3942L); + R2(D,A,B,C,X[ 8],11,0x8771f681L); + R2(C,D,A,B,X[11],16,0x6d9d6122L); + R2(B,C,D,A,X[14],23,0xfde5380cL); + R2(A,B,C,D,X[ 1], 4,0xa4beea44L); + R2(D,A,B,C,X[ 4],11,0x4bdecfa9L); + R2(C,D,A,B,X[ 7],16,0xf6bb4b60L); + R2(B,C,D,A,X[10],23,0xbebfbc70L); + R2(A,B,C,D,X[13], 4,0x289b7ec6L); + R2(D,A,B,C,X[ 0],11,0xeaa127faL); + R2(C,D,A,B,X[ 3],16,0xd4ef3085L); + R2(B,C,D,A,X[ 6],23,0x04881d05L); + R2(A,B,C,D,X[ 9], 4,0xd9d4d039L); + R2(D,A,B,C,X[12],11,0xe6db99e5L); + R2(C,D,A,B,X[15],16,0x1fa27cf8L); + R2(B,C,D,A,X[ 2],23,0xc4ac5665L); + /* Round 3 */ + R3(A,B,C,D,X[ 0], 6,0xf4292244L); + R3(D,A,B,C,X[ 7],10,0x432aff97L); + R3(C,D,A,B,X[14],15,0xab9423a7L); + R3(B,C,D,A,X[ 5],21,0xfc93a039L); + R3(A,B,C,D,X[12], 6,0x655b59c3L); + R3(D,A,B,C,X[ 3],10,0x8f0ccc92L); + R3(C,D,A,B,X[10],15,0xffeff47dL); + R3(B,C,D,A,X[ 1],21,0x85845dd1L); + R3(A,B,C,D,X[ 8], 6,0x6fa87e4fL); + R3(D,A,B,C,X[15],10,0xfe2ce6e0L); + R3(C,D,A,B,X[ 6],15,0xa3014314L); + R3(B,C,D,A,X[13],21,0x4e0811a1L); + R3(A,B,C,D,X[ 4], 6,0xf7537e82L); + R3(D,A,B,C,X[11],10,0xbd3af235L); + R3(C,D,A,B,X[ 2],15,0x2ad7d2bbL); + R3(B,C,D,A,X[ 9],21,0xeb86d391L); + + A = c->A += A; + B = c->B += B; + C = c->C += C; + D = c->D += D; + } + } +#endif + +#ifndef md5_block_data_order +#ifdef X +#undef X +#endif +void md5_block_data_order (MD5_CTX *c, const void *data_, int num) + { + const unsigned char *data=data_; + register unsigned MD32_REG_T A,B,C,D,l; +#ifndef MD32_XARRAY + /* See comment in crypto/sha/sha_locl.h for details. */ + unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, + XX8, XX9,XX10,XX11,XX12,XX13,XX14,XX15; +# define X(i) XX##i +#else + mDNSu32 XX[MD5_LBLOCK]; +# define X(i) XX[i] +#endif + + A=c->A; + B=c->B; + C=c->C; + D=c->D; + + for (;num--;) + { + HOST_c2l(data,l); X( 0)=l; HOST_c2l(data,l); X( 1)=l; + /* Round 0 */ + R0(A,B,C,D,X( 0), 7,0xd76aa478L); HOST_c2l(data,l); X( 2)=l; + R0(D,A,B,C,X( 1),12,0xe8c7b756L); HOST_c2l(data,l); X( 3)=l; + R0(C,D,A,B,X( 2),17,0x242070dbL); HOST_c2l(data,l); X( 4)=l; + R0(B,C,D,A,X( 3),22,0xc1bdceeeL); HOST_c2l(data,l); X( 5)=l; + R0(A,B,C,D,X( 4), 7,0xf57c0fafL); HOST_c2l(data,l); X( 6)=l; + R0(D,A,B,C,X( 5),12,0x4787c62aL); HOST_c2l(data,l); X( 7)=l; + R0(C,D,A,B,X( 6),17,0xa8304613L); HOST_c2l(data,l); X( 8)=l; + R0(B,C,D,A,X( 7),22,0xfd469501L); HOST_c2l(data,l); X( 9)=l; + R0(A,B,C,D,X( 8), 7,0x698098d8L); HOST_c2l(data,l); X(10)=l; + R0(D,A,B,C,X( 9),12,0x8b44f7afL); HOST_c2l(data,l); X(11)=l; + R0(C,D,A,B,X(10),17,0xffff5bb1L); HOST_c2l(data,l); X(12)=l; + R0(B,C,D,A,X(11),22,0x895cd7beL); HOST_c2l(data,l); X(13)=l; + R0(A,B,C,D,X(12), 7,0x6b901122L); HOST_c2l(data,l); X(14)=l; + R0(D,A,B,C,X(13),12,0xfd987193L); HOST_c2l(data,l); X(15)=l; + R0(C,D,A,B,X(14),17,0xa679438eL); + R0(B,C,D,A,X(15),22,0x49b40821L); + /* Round 1 */ + R1(A,B,C,D,X( 1), 5,0xf61e2562L); + R1(D,A,B,C,X( 6), 9,0xc040b340L); + R1(C,D,A,B,X(11),14,0x265e5a51L); + R1(B,C,D,A,X( 0),20,0xe9b6c7aaL); + R1(A,B,C,D,X( 5), 5,0xd62f105dL); + R1(D,A,B,C,X(10), 9,0x02441453L); + R1(C,D,A,B,X(15),14,0xd8a1e681L); + R1(B,C,D,A,X( 4),20,0xe7d3fbc8L); + R1(A,B,C,D,X( 9), 5,0x21e1cde6L); + R1(D,A,B,C,X(14), 9,0xc33707d6L); + R1(C,D,A,B,X( 3),14,0xf4d50d87L); + R1(B,C,D,A,X( 8),20,0x455a14edL); + R1(A,B,C,D,X(13), 5,0xa9e3e905L); + R1(D,A,B,C,X( 2), 9,0xfcefa3f8L); + R1(C,D,A,B,X( 7),14,0x676f02d9L); + R1(B,C,D,A,X(12),20,0x8d2a4c8aL); + /* Round 2 */ + R2(A,B,C,D,X( 5), 4,0xfffa3942L); + R2(D,A,B,C,X( 8),11,0x8771f681L); + R2(C,D,A,B,X(11),16,0x6d9d6122L); + R2(B,C,D,A,X(14),23,0xfde5380cL); + R2(A,B,C,D,X( 1), 4,0xa4beea44L); + R2(D,A,B,C,X( 4),11,0x4bdecfa9L); + R2(C,D,A,B,X( 7),16,0xf6bb4b60L); + R2(B,C,D,A,X(10),23,0xbebfbc70L); + R2(A,B,C,D,X(13), 4,0x289b7ec6L); + R2(D,A,B,C,X( 0),11,0xeaa127faL); + R2(C,D,A,B,X( 3),16,0xd4ef3085L); + R2(B,C,D,A,X( 6),23,0x04881d05L); + R2(A,B,C,D,X( 9), 4,0xd9d4d039L); + R2(D,A,B,C,X(12),11,0xe6db99e5L); + R2(C,D,A,B,X(15),16,0x1fa27cf8L); + R2(B,C,D,A,X( 2),23,0xc4ac5665L); + /* Round 3 */ + R3(A,B,C,D,X( 0), 6,0xf4292244L); + R3(D,A,B,C,X( 7),10,0x432aff97L); + R3(C,D,A,B,X(14),15,0xab9423a7L); + R3(B,C,D,A,X( 5),21,0xfc93a039L); + R3(A,B,C,D,X(12), 6,0x655b59c3L); + R3(D,A,B,C,X( 3),10,0x8f0ccc92L); + R3(C,D,A,B,X(10),15,0xffeff47dL); + R3(B,C,D,A,X( 1),21,0x85845dd1L); + R3(A,B,C,D,X( 8), 6,0x6fa87e4fL); + R3(D,A,B,C,X(15),10,0xfe2ce6e0L); + R3(C,D,A,B,X( 6),15,0xa3014314L); + R3(B,C,D,A,X(13),21,0x4e0811a1L); + R3(A,B,C,D,X( 4), 6,0xf7537e82L); + R3(D,A,B,C,X(11),10,0xbd3af235L); + R3(C,D,A,B,X( 2),15,0x2ad7d2bbL); + R3(B,C,D,A,X( 9),21,0xeb86d391L); + + A = c->A += A; + B = c->B += B; + C = c->C += C; + D = c->D += D; + } + } +#endif + + + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - base64 -> binary conversion +#endif + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + + +#define mDNSisspace(x) (x == '\t' || x == '\n' || x == '\v' || x == '\f' || x == '\r' || x == ' ') + +static const char *mDNSstrchr(const char *s, int c) + { + while (1) + { + if (c == *s) return s; + if (!*s) return mDNSNULL; + s++; + } + } + +// skips all whitespace anywhere. +// converts characters, four at a time, starting at (or after) +// src from base - 64 numbers into three 8 bit bytes in the target area. +// it returns the number of data bytes stored at the target, or -1 on error. +// adapted from BIND sources + +mDNSexport mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize) + { + int tarindex, state, ch; + const char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (mDNSisspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = mDNSstrchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((mDNSu32)tarindex >= targsize) + return (-1); + target[tarindex] = (mDNSu8)((pos - Base64) << 2); + } + state = 1; + break; + case 1: + if (target) { + if ((mDNSu32)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x0f) << 4); + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((mDNSu32)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = (mDNSu8)(((pos - Base64) & 0x03) << 6); + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((mDNSu32)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + return -1; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)mDNSNULL; ch != '\0'; ch = *src++) + if (!mDNSisspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)mDNSNULL; ch != '\0'; ch = *src++) + if (!mDNSisspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); + } + + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - API exported to mDNS Core +#endif + +// Constants +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c +#define MD5_LEN 16 + +static domainname HMAC_MD5_AlgName = { { '\010', 'h', 'm', 'a', 'c', '-', 'm', 'd', '5', + '\007', 's', 'i', 'g', '-', 'a', 'l', 'g', + '\003', 'r', 'e', 'g', + '\003', 'i', 'n', 't', + '\0' } }; +// Adapted from Appendix, RFC 2104 +mDNSexport void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, mDNSu8 *key, mDNSu32 len) + { + MD5_CTX k; + mDNSu8 buf[MD5_LEN]; + int i; + + // If key is longer than HMAC_LEN reset it to MD5(key) + if (len > HMAC_LEN) + { + MD5_Init(&k); + MD5_Update(&k, key, len); + MD5_Final(buf, &k); + key = buf; + len = MD5_LEN; + } + + // store key in pads + mDNSPlatformMemZero(info->key.ipad, HMAC_LEN); + mDNSPlatformMemZero(info->key.opad, HMAC_LEN); + mDNSPlatformMemCopy(key, info->key.ipad, len); + mDNSPlatformMemCopy(key, info->key.opad, len); + + // XOR key with ipad and opad values + for (i = 0; i < HMAC_LEN; i++) + { + info->key.ipad[i] ^= HMAC_IPAD; + info->key.opad[i] ^= HMAC_OPAD; + } + + } + +mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info) + { + AuthRecord tsig; + mDNSu8 *countPtr, *rdata; + mDNSu32 utc32; + mDNSu8 utc48[6]; + mDNSu8 digest[MD5_LEN]; + mDNSu8 *ptr = *end; + mDNSu32 len; + mDNSOpaque16 buf; + MD5_CTX c; + + // Init MD5 context, digest inner key pad and message + MD5_Init(&c); + MD5_Update(&c, info->key.ipad, HMAC_LEN); + MD5_Update(&c, (mDNSu8 *)msg, (unsigned long)(*end - (mDNSu8 *)msg)); + + // Construct TSIG RR, digesting variables as apporpriate + mDNSPlatformMemZero(&tsig, sizeof(AuthRecord)); + mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + rdata = tsig.resrec.rdata->u.data; + + // key name + mDNSPlatformStrCopy(info->keyname.c, tsig.resrec.name.c); + MD5_Update(&c, info->keyname.c, mDNSPlatformStrLen(info->keyname.c)+1); + + // class + tsig.resrec.rrclass = kDNSQClass_ANY; + buf = mDNSOpaque16fromIntVal(kDNSQClass_ANY); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); + + // ttl + tsig.resrec.rroriginalttl = 0; + MD5_Update(&c, (mDNSu8 *)&tsig.resrec.rroriginalttl, sizeof(tsig.resrec.rroriginalttl)); + + // alg name + mDNSPlatformStrCopy(HMAC_MD5_AlgName.c, rdata); + len = mDNSPlatformStrLen(HMAC_MD5_AlgName.c) + 1; + rdata += len; + MD5_Update(&c, HMAC_MD5_AlgName.c, len); + + // time + // get UTC (universal time), convert to 48-bit unsigned in network byte order + utc32 = (mDNSu32)mDNSPlatformUTC(); + if (utc32 == (unsigned)-1) { LogMsg("ERROR: DNSDigest_SignMessage - mDNSPlatformUTC returned bad time -1"); return mDNSNULL; } + utc48[0] = 0; + utc48[1] = 0; + utc48[2] = (mDNSu8)((utc32 >> 24) & 0xff); + utc48[3] = (mDNSu8)((utc32 >> 16) & 0xff); + utc48[4] = (mDNSu8)((utc32 >> 8) & 0xff); + utc48[5] = (mDNSu8)( utc32 & 0xff); + + mDNSPlatformMemCopy(utc48, rdata, 6); + rdata += 6; + MD5_Update(&c, utc48, 6); + + // fudge + buf = mDNSOpaque16fromIntVal(300); // 300 sec is fudge recommended in RFC 2485 + ((mDNSOpaque16 *)rdata)->NotAnInteger = buf.NotAnInteger; + rdata += sizeof(mDNSOpaque16); + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); + + // digest error and other data len (both zero) - we'll add them to the rdata later + buf.NotAnInteger = 0; + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // error + MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); // other data len + + // finish the message & tsig var hash + MD5_Final(digest, &c); + + // perform outer MD5 (outer key pad, inner digest) + MD5_Init(&c); + MD5_Update(&c, info->key.opad, HMAC_LEN); + MD5_Update(&c, digest, MD5_LEN); + MD5_Final(digest, &c); + + // set remaining rdata fields + *(mDNSOpaque16 *)rdata = mDNSOpaque16fromIntVal(MD5_LEN); // MAC size + rdata += sizeof(mDNSOpaque16); + mDNSPlatformMemCopy(digest, rdata, MD5_LEN); // MAC + rdata += MD5_LEN; + ((mDNSOpaque16 *)rdata)->NotAnInteger = msg->h.id.NotAnInteger; // original ID + rdata += sizeof(mDNSOpaque16); + ((mDNSOpaque16 *)rdata)->NotAnInteger = 0; // no error + rdata += sizeof(mDNSOpaque16); + ((mDNSOpaque16 *)rdata)->NotAnInteger = 0; // other data len + rdata += sizeof(mDNSOpaque16); + + tsig.resrec.rdlength = (mDNSu16)(rdata - tsig.resrec.rdata->u.data); + *end = PutResourceRecordTTL(msg, ptr, numAdditionals, &tsig.resrec, 0); + if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); return mDNSNULL; } + + // update num additionals + countPtr = (mDNSu8 *)&msg->h.numAdditionals; // increment (network-byte ordered) header value + *countPtr++ = (mDNSu8)(*numAdditionals >> 8); + *countPtr++ = (mDNSu8)(*numAdditionals & 0xFF); + + return *end; + } + + +#ifdef __cplusplus +} +#endif diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index 556bdd1..b5364c2 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -44,48 +46,257 @@ Change History (most recent first): $Log: mDNS.c,v $ -Revision 1.307.2.13 2005/01/28 05:39:12 cheshire -Remove LogMsg() +Revision 1.382 2004/06/08 04:59:40 cheshire +Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it + +Revision 1.381 2004/06/05 00:57:30 cheshire +Remove incorrect LogMsg() + +Revision 1.380 2004/06/05 00:04:26 cheshire +: wide-area domains should be returned in reg. domain enumeration + +Revision 1.379 2004/05/28 23:42:36 ksekar +: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.378 2004/05/25 17:25:25 cheshire +Remove extraneous blank lines and white space + +Revision 1.377 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.376 2004/05/05 18:30:44 ksekar +Restored surpressed Cache Tail debug messages. + +Revision 1.375 2004/04/26 21:36:25 cheshire +Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive +is indicated as being available on that interface + +Revision 1.374 2004/04/21 02:53:26 cheshire +Typo in debugf statement + +Revision 1.373 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.372 2004/04/21 02:38:51 cheshire +Add debugging checks + +Revision 1.371 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.370 2004/04/09 17:40:26 cheshire +Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field + +Revision 1.369 2004/04/09 16:34:00 cheshire +Debugging code for later; currently unused + +Revision 1.368 2004/04/02 19:19:48 cheshire +Add code to do optional logging of multi-packet KA list time intervals + +Revision 1.367 2004/03/20 03:16:10 cheshire +Minor refinement to "Excessive update rate" message -Revision 1.307.2.12 2005/01/28 05:02:05 cheshire - SUPan: Replace IP TTL 255 check with local-subnet check +Revision 1.366 2004/03/20 03:12:57 cheshire +: UpdateCredits not granted promptly enough -Revision 1.307.2.11 2005/01/28 04:03:23 cheshire - SUPan: Current method of doing subtypes causes name collisions -Summary: Pulled in ConstructServiceName, CountSubTypes and AllocateSubTypes from Tiger version. +Revision 1.365 2004/03/19 23:51:22 cheshire +Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60) -Revision 1.307.2.10 2004/06/18 17:28:18 cheshire - Current method of doing subtypes causes name collisions +Revision 1.364 2004/03/13 01:57:33 ksekar +: DynDNS: Dynamic update of service records -Revision 1.307.2.9 2004/06/18 00:31:51 cheshire - mDNSResponder escape handling inconsistent with BIND -(Prerequisite for fixing ) +Revision 1.363 2004/03/12 21:00:51 cheshire +Also show port numbers when logging "apparent spoof mDNS Response" messages -Revision 1.307.2.8 2004/04/03 05:18:19 bradley -Added cast to fix signed/unsigned warning due to int promotion. +Revision 1.362 2004/03/12 08:58:18 cheshire +Guard against empty TXT records -Revision 1.307.2.7 2004/03/30 06:46:24 cheshire -Compiler warning fixes from Don Woodward at Roku Labs +Revision 1.361 2004/03/09 03:00:46 cheshire + Don't take lock until after mDNS_Update() has validated that the data is good. -Revision 1.307.2.6 2004/03/09 03:03:38 cheshire - Don't take lock until after mDNS_Update() has validated that the data is good +Revision 1.360 2004/03/08 02:52:41 cheshire +Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages -Revision 1.307.2.5 2004/03/02 02:55:24 cheshire +Revision 1.359 2004/03/02 03:21:56 cheshire Properly support "_services._dns-sd._udp" meta-queries -Revision 1.307.2.4 2004/02/18 01:55:08 cheshire -: Increase delay to 400ms when answering queries with multi-packet KA lists +Revision 1.358 2004/02/20 08:18:34 cheshire +: mDNSResponder sometimes announces AAAA records unnecessarily -Revision 1.307.2.3 2004/01/28 23:08:45 cheshire -: Hard code domain enumeration functions to return ".local" only +Revision 1.357 2004/02/18 01:47:41 cheshire +: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms -Revision 1.307.2.2 2003/12/20 01:51:40 cheshire +Revision 1.356 2004/02/06 23:04:19 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.355 2004/02/05 09:32:33 cheshire +Fix from Bob Bradley: When using the "%.*s" string form, +guard against truncating in the middle of a multi-byte UTF-8 character. + +Revision 1.354 2004/02/05 09:30:22 cheshire +Update comments + +Revision 1.353 2004/01/28 03:41:00 cheshire +: Need ability to do targeted queries as well as multicast queries + +Revision 1.352 2004/01/28 02:30:07 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.351 2004/01/27 20:15:22 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.350 2004/01/24 23:38:16 cheshire +Use mDNSVal16() instead of shifting and ORing operations + +Revision 1.349 2004/01/23 23:23:14 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.348 2004/01/22 03:54:11 cheshire +Create special meta-interface 'mDNSInterface_ForceMCast' (-2), +which means "do this query via multicast, even if it's apparently a unicast domain" + +Revision 1.347 2004/01/22 03:50:49 cheshire +If the client has specified an explicit InterfaceID, then do query by multicast, not unicast + +Revision 1.346 2004/01/22 03:48:41 cheshire +Make sure uDNS client doesn't accidentally use query ID zero + +Revision 1.345 2004/01/22 03:43:08 cheshire +Export constants like mDNSInterface_LocalOnly so that the client layers can use them + +Revision 1.344 2004/01/21 21:53:18 cheshire +: Don't try to receive unicast responses if we're not the first to bind to the UDP port + +Revision 1.343 2003/12/23 00:07:47 cheshire +Make port number in debug message be five-character field, left justified + +Revision 1.342 2003/12/20 01:34:28 cheshire : Error putting additional records into packets Another fix from Rampi: responseptr needs to be updated inside the "for" loop, after every record, not once at the end. -Revision 1.307.2.1 2003/12/03 11:20:27 cheshire -: Stop and start of a service uses old ip address (with old port number) +Revision 1.341 2003/12/18 22:56:12 cheshire +: Reduce syslog messages about ignored spoof packets + +Revision 1.340 2003/12/16 02:31:37 cheshire +Minor update to comments + +Revision 1.339 2003/12/13 05:50:33 bradley +Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform +layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to +fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf. + +Revision 1.338 2003/12/13 03:05:27 ksekar +: DynDNS: Unicast query of service records + +Revision 1.337 2003/12/01 21:46:05 cheshire +mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist + +Revision 1.336 2003/12/01 21:26:19 cheshire +Guard against zero-length sbuffer in mDNS_vsnprintf() + +Revision 1.335 2003/12/01 20:27:48 cheshire +Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors + +Revision 1.334 2003/11/20 22:59:53 cheshire +Changed runtime checks in mDNS.c to be compile-time checks in mDNSClientAPI.h +Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. + +Revision 1.333 2003/11/20 20:49:53 cheshire +Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures + +Revision 1.332 2003/11/20 05:47:37 cheshire +: Don't exclude known answers whose expiry time is before the next query +Now that we only include answers in the known answer list if they are less than +halfway to expiry, the check to also see if we have another query scheduled +before the record expires is no longer necessary (and in fact, not correct). + +Revision 1.331 2003/11/19 22:31:48 cheshire +When automatically adding A records to SRVs, add them as additionals, not answers + +Revision 1.330 2003/11/19 22:28:50 cheshire +Increment/Decrement mDNS_reentrancy around calls to m->MainCallback() +to allow client to make mDNS calls (specifically the call to mDNS_GrowCache()) + +Revision 1.329 2003/11/19 22:19:24 cheshire +Show log message when ignoring packets with bad TTL. +This is to help diagnose problems on Linux versions that may not report the TTL reliably. + +Revision 1.328 2003/11/19 22:06:38 cheshire +Show log messages when a service or hostname is renamed + +Revision 1.327 2003/11/19 22:03:44 cheshire +Move common "m->NextScheduledResponse = m->timenow" to before "if" statement + +Revision 1.326 2003/11/17 22:27:02 cheshire +Another fix from ramaprasad.kr@hp.com: Improve reply delay computation +on platforms that have native clock rates below fifty ticks per second. + +Revision 1.325 2003/11/17 20:41:44 cheshire +Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls. + +Revision 1.324 2003/11/17 20:36:32 cheshire +Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and +DeadvertiseInterface() -- they're internal private routines, not API routines. + +Revision 1.323 2003/11/14 20:59:08 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.322 2003/11/14 19:47:52 cheshire +Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString + +Revision 1.321 2003/11/14 19:18:34 cheshire +Move AssignDomainName macro to mDNSClientAPI.h to that client layers can use it too + +Revision 1.320 2003/11/13 06:45:04 cheshire +Fix compiler warning on certain compilers + +Revision 1.319 2003/11/13 00:47:40 cheshire + We should delay AAAA record query if A record already in cache. + +Revision 1.318 2003/11/13 00:33:26 cheshire +Change macro "RRIsAddressType" to "RRTypeIsAddressType" + +Revision 1.317 2003/11/13 00:10:49 cheshire +: Verify that rr data is different before updating. + +Revision 1.316 2003/11/08 23:37:54 cheshire +Give explicit zero initializers to blank static structure, required by certain compilers. +(Thanks to ramaprasad.kr@hp.com for reporting this.) + +Revision 1.315 2003/11/07 03:32:56 cheshire + mDNSResponder delivers answers in inconsistent order +This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes +purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is +to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR() +can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place. + +Revision 1.314 2003/11/07 03:19:49 cheshire +Minor variable renaming for clarity + +Revision 1.313 2003/11/07 03:14:49 cheshire +Previous checkin proved to be overly simplistic; reversing + +Revision 1.312 2003/11/03 23:45:15 cheshire + mDNSResponder delivers answers in inconsistent order +Build cache lists in FIFO order, not customary C LIFO order +(Append new elements to tail of cache list, instead of prepending at the head.) + +Revision 1.311 2003/10/09 18:00:11 cheshire +Another compiler warning fix. + +Revision 1.310 2003/10/07 20:27:05 cheshire +Patch from Bob Bradley, to fix warning and compile error on Windows + +Revision 1.309 2003/09/26 01:06:36 cheshire + Set kDNSClass_UniqueRRSet bit for updates too +Made new routine HaveSentEntireRRSet() to check if flag should be set + +Revision 1.308 2003/09/23 01:05:01 cheshire +Minor changes to comments and debugf() message Revision 1.307 2003/09/09 20:13:30 cheshire Don't send a Goodbye record if we never announced it @@ -236,7 +447,7 @@ Revision 1.271 2003/08/14 02:17:05 cheshire Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord Revision 1.270 2003/08/13 17:07:28 ksekar -Bug #: : Extra RR linked to list even if registration fails - causes crash +: 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 @@ -741,7 +952,7 @@ Revision 1.128 2003/05/23 01:55:13 cheshire After name change, mDNSResponder needs to re-probe for name uniqueness Revision 1.127 2003/05/23 01:02:15 ksekar -Bug #: : mDNSResponder needs to include unique id in default name +: mDNSResponder needs to include unique id in default name Revision 1.126 2003/05/22 02:29:22 cheshire SendQueries needs to handle multihoming better @@ -761,7 +972,7 @@ Revision 1.122 2003/05/21 19:59:04 cheshire Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character Revision 1.121 2003/05/21 17:54:07 ksekar -Bug #: ER: Tweak responder's default name conflict behavior + 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 @@ -827,22 +1038,21 @@ Revision 1.104 2003/04/21 19:15:52 cheshire Fix some compiler warnings Revision 1.103 2003/04/19 02:26:35 cheshire -Bug #: Incorrect goodbye packet after conflict + Incorrect goodbye packet after conflict Revision 1.102 2003/04/17 03:06:28 cheshire -Bug #: No need to query again when a service goes away + 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. + Added a hash to lookup records in the cache. Revision 1.100 2003/04/15 18:53:14 cheshire -Bug #: Bug in ScheduleNextTask + 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. @@ -877,7 +1087,7 @@ Revision 1.92 2003/03/27 03:30:55 cheshire 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 +2. Make HostNameCallback() use DeadvertiseInterface() instead (it never really needed to deregister the interface at all) Revision 1.91 2003/03/15 04:40:36 cheshire @@ -899,41 +1109,41 @@ 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! + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 @@ -948,19 +1158,19 @@ 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 + 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 + 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 + 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() @@ -969,30 +1179,30 @@ 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 + 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 + 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 + computer name changes not handled properly + service name changes are not properly handled + 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 + 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 @@ -1004,7 +1214,7 @@ 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-existence +Added mDNS_RegisterNoSuchService() function for assertion of non-existance of a particular named service Revision 1.59 2002/09/19 21:25:34 cheshire @@ -1022,9 +1232,9 @@ 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 "mDNSClientAPI.h" // Defines the interface provided to the client layer above +#include "DNSCommon.h" // Defines general DNS untility routines +#include "uDNS.h" // Defines entry points into unicast-specific routines // Disable certain benign warnings with Microsoft compilers #if(defined(_MSC_VER)) // Disable "conditional expression is constant" warning for debug macros. @@ -1032,11 +1242,6 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release // 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. @@ -1045,66 +1250,25 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release #pragma warning(disable:4706) #endif -// *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - DNS Protocol Constants -#endif - -typedef enum - { - kDNSFlag0_QR_Mask = 0x80, // Query or response? - kDNSFlag0_QR_Query = 0x00, - kDNSFlag0_QR_Response = 0x80, - - kDNSFlag0_OP_Mask = 0x78, // Operation type - kDNSFlag0_OP_StdQuery = 0x00, - kDNSFlag0_OP_Iquery = 0x08, - kDNSFlag0_OP_Status = 0x10, - kDNSFlag0_OP_Unused3 = 0x18, - kDNSFlag0_OP_Notify = 0x20, - kDNSFlag0_OP_Update = 0x28, - - kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask, - - kDNSFlag0_AA = 0x04, // Authoritative Answer? - kDNSFlag0_TC = 0x02, // Truncated? - kDNSFlag0_RD = 0x01, // Recursion Desired? - kDNSFlag1_RA = 0x80, // Recursion Available? - - kDNSFlag1_Zero = 0x40, // Reserved; must be zero - kDNSFlag1_AD = 0x20, // Authentic Data [RFC 2535] - kDNSFlag1_CD = 0x10, // Checking Disabled [RFC 2535] - - kDNSFlag1_RC = 0x0F, // Response code - kDNSFlag1_RC_NoErr = 0x00, - kDNSFlag1_RC_FmtErr = 0x01, - kDNSFlag1_RC_SrvErr = 0x02, - kDNSFlag1_RC_NXDomain = 0x03, - kDNSFlag1_RC_NotImpl = 0x04, - kDNSFlag1_RC_Refused = 0x05, - kDNSFlag1_RC_YXDomain = 0x06, - kDNSFlag1_RC_YXRRSet = 0x07, - kDNSFlag1_RC_NXRRSet = 0x08, - kDNSFlag1_RC_NotAuth = 0x09, - kDNSFlag1_RC_NotZone = 0x0A - } DNS_Flags; - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - #pragma mark - Program Constants #endif -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 mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; + +mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; +mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-1; +mDNSexport const mDNSInterfaceID mDNSInterface_ForceMCast = (mDNSInterfaceID)-2; -mDNSexport const mDNSInterfaceID mDNSInterface_Any = { 0 }; -mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = { (mDNSInterfaceID)~0 }; +// Note that mDNSInterfaceMark is the same value as mDNSInterface_LocalOnly, but they are used in different contexts +mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; #define UnicastDNSPortAsNumber 53 #define MulticastDNSPortAsNumber 5353 @@ -1116,9 +1280,11 @@ mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0 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 } }; +mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; +mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; +mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; +mDNSexport const mDNSOpaque16 UpdateReqFlags= { { kDNSFlag0_OP_Update | kDNSFlag0_QR_Query, 0 } }; +mDNSexport const mDNSOpaque16 UpdateRespFlags={ { kDNSFlag0_OP_Update | kDNSFlag0_QR_Response, 0 } }; #define zeroDomainNamePtr ((domainname*)"") // Any records bigger than this are considered 'large' records @@ -1128,17 +1294,16 @@ static const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_ #define kDefaultTTLforShared (2*3600) #define kMaxUpdateCredits 10 +#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 50) static const char *const mDNS_DomainTypeNames[] = { - "_browse._dns-sd._udp.local.", - "_default._browse._dns-sd._udp.local.", - "_register._dns-sd._udp.local.", - "_default._register._dns-sd._udp.local." + "_browse._dns-sd._udp.", + "_default._browse._dns-sd._udp.", + "_register._dns-sd._udp.", + "_default._register._dns-sd._udp." }; -#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC).c, (DST).c, DomainNameLength(&(SRC))) - // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -1157,13 +1322,15 @@ static const struct mDNSprintf_format char sign; // +, - or space unsigned int fieldWidth; unsigned int precision; - } mDNSprintf_format_default; + } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) { mDNSu32 nwritten = 0; int c; + if (buflen == 0) return(0); buflen--; // Pre-reserve one space in the buffer for the terminating nul + if (buflen == 0) goto exit; for (c = *fmt; c != 0; c = *++fmt) { @@ -1275,17 +1442,14 @@ mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } else { - unsigned short *w = (unsigned short *)a; s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end if (F.altForm) { mDNSAddr *ip = (mDNSAddr*)a; - a = (unsigned char *)&ip->ip.v4; - w = (unsigned short *)&ip->ip.v6; switch (ip->type) { - case mDNSAddrType_IPv4: F.precision = 4; break; - case mDNSAddrType_IPv6: F.precision = 16; break; + case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; + case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; default: F.precision = 0; break; } } @@ -1295,8 +1459,10 @@ mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt 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; + case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), + "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", + a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], + a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); 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; } @@ -1334,7 +1500,35 @@ mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt if (!s) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } else switch (F.altForm) { - case 0: { char *a=s; i=0; while(*a++) i++; break; } // C string + case 0: i=0; + if (!F.havePrecision) // C string + while(s[i]) i++; + else + { + while ((i < F.precision) && s[i]) i++; + // Make sure we don't truncate in the middle of a UTF-8 character + // If last character we got was any kind of UTF-8 multi-byte character, + // then see if we have to back up. + // This is not as easy as the similar checks below, because + // here we can't assume it's safe to examine the *next* byte, so we + // have to confine ourselves to working only backwards in the string. + j = i; // Record where we got to + // Now, back up until we find first non-continuation-char + while (i>0 && (s[i-1] & 0xC0) == 0x80) i--; + // Now s[i-1] is the first non-continuation-char + // and (j-i) is the number of continuation-chars we found + if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char + { + i--; // Tentatively eliminate this start-char as well + // Now (j-i) is the number of characters we're considering eliminating. + // To be legal UTF-8, the start-char must contain (j-i) one-bits, + // followed by a zero bit. If we shift it right by (7-(j-i)) bits + // (with sign extension) then the result has to be 0xFE. + // If this is right, then we reinstate the tentatively eliminated bytes. + if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j; + } + } + break; case 1: i = (unsigned char) *s++; break; // Pascal string case 2: { // DNS label-sequence name unsigned char *a = (unsigned char *)s; @@ -1352,7 +1546,8 @@ mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt break; } } - if (F.havePrecision && i > F.precision) // Make sure we don't truncate in the middle of a UTF-8 character + // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below) + if (F.havePrecision && i > F.precision) { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } break; @@ -1376,7 +1571,11 @@ mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt 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 + // Make sure we don't truncate in the middle of a UTF-8 character. + // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the allowed output. If s[i] is a + // UTF-8 continuation character, then we've cut a unicode character in half, so back up 'i' until s[i] is no longer a UTF-8 continuation + // character. (if the input was proprly formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). + if (i > buflen - nwritten) { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } for (j=0; jMsgBuffer; - 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); - } - -mDNSexport 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) - { - const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); - if (next) return(next->InterfaceID); else return(mDNSNULL); - } - #define InitialQuestionInterval (mDNSPlatformOneSecond/2) #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0) @@ -1537,730 +1625,125 @@ mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - -#pragma mark - Domain Name Utility Functions +#pragma mark - Resource Record Utility Functions #endif -#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) - { - int i; - const int len = *a++; +#define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA) - if (len > MAX_DOMAIN_LABEL) - { debugf("Malformed label (too long)"); return(mDNSfalse); } +#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)) ) - if (len != *b++) return(mDNSfalse); - for (i=0; iresrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID))) -mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2) - { - const mDNSu8 * a = d1->c; - const mDNSu8 * b = d2->c; - const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid +#define RRUniqueOrKnownUnique(RR) ((RR)->RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeKnownUnique)) - while (*a || *b) - { - if (a + 1 + *a >= max) - { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse); } - if (!SameDomainLabel(a, b)) return(mDNSfalse); - a += 1 + *a; - b += 1 + *b; - } - - return(mDNStrue); - } +#define DefaultProbeCountForTypeUnique ((mDNSu8)3) +#define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) -// 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)); - } +// 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) -// 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 -// (length, three data bytes, length, three more data bytes, final zero). -// In the case where a parent domain name is provided, and the given name is a child -// of that parent, CompressedDomainNameLength returns the length of the prefix portion -// 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 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((mDNSu16)(src - name->c + 2)); - src += 1 + *src; - if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); - } - return((mDNSu16)(src - name->c + 1)); - } +// 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) -// 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 -// 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 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 - - 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 - } +#define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \ + (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \ + (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0) -// 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) - { - 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 (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label... - { - mDNSu8 c = (mDNSu8)*cstr++; // Read the character - if (c == '\\') // If escape character, check next character - { - c = (mDNSu8)*cstr++; // Assume we'll just take the next character - if (mdnsIsDigit(cstr[-1]) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1])) - { // If three decimal digits, - int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal - int v1 = cstr[ 0] - '0'; - int v2 = cstr[ 1] - '0'; - int val = v0 * 100 + v1 * 10 + v2; - if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it - } - } - *ptr++ = c; // Write the character - } - 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 - } +#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)) - *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 - } +#define MaxUnansweredQueries 4 -// 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) +// 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) { - 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); + 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)); } -mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append) +// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if the +// authoratative record is in the probing state. We always send probes with the wildcard type kDNSQType_ANY, +// 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) { - 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]) - { - 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); + 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)); } -// 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) +// 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) { - 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 + 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)); } -// 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) +// 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) { - name->c[0] = 0; // Make an empty domain name - return(AppendDNSNameString(name, cstr)); // And then add this string to it + // 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. + // If the requester's indicated TTL is at least half the real TTL, + // then we can suppress our answer this time. + // If the requester's indicated TTL is greater than the TTL we believe, + // then that's okay, and we don't need to do anything about it. + // (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(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2); } -mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) +mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr) { - 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 - while (src < end) // While we have characters in the label + if (rr->resrec.RecordType == kDNSRecordTypeUnique) { - mDNSu8 c = *src++; - if (esc) - { - 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); - c = (mDNSu8)('0' + (c ) % 10); - } - } - *ptr++ = (char)c; // Copy the character - } - *ptr = 0; // Null-terminate the string - return(ptr); // and return - } - -// Note, to guarantee that there will be no possible overrun, cstr must be at least 1005 bytes -// The longest legal domain name is 255 bytes, in the form of three 64-byte labels, one 62-byte label, -// and the null root label. -// If every label character has to be escaped as a four-byte escape sequence, the maximum textual -// ascii display of this is 63*4 + 63*4 + 63*4 + 61*4 = 1000 label characters, -// plus four dots and the null at the end of the C string = 1005 -mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) - { - const mDNSu8 *src = name->c; // Domain name we're reading - const mDNSu8 *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(mDNSNULL); - ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); - if (!ptr) return(mDNSNULL); - src += 1 + *src; - *ptr++ = '.'; // Write the dot after the label - } - - *ptr++ = 0; // Null-terminate the string - return(ptr); // and return - } - -// 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) - { - const mDNSu8 * src = &UTF8Name[1]; - const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0]; - mDNSu8 * ptr = &hostlabel->c[1]; - const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL; - while (src < end) - { - // Delete apostrophes from source name - if (src[0] == '\'') { src++; continue; } // Standard straight single quote - if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99) - { src += 3; continue; } // Unicode curly apostrophe - if (ptr < lim) - { - if (mdnsValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src; - else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-'; - } - src++; - } - while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks - hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]); - } - -mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, - const domainlabel *name, const domainname *type, const domainname *const domain) - { - int i, len; - mDNSu8 *dst = fqdn->c; - 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 && type) - { - const mDNSu8 *s0 = type->c; - if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63) - { - const mDNSu8 * s1 = s0 + 1 + s0[0]; - if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63) - { - const mDNSu8 *s2 = s1 + 1 + s1[0]; - if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels - { - static const mDNSu8 SubTypeLabel[5] = "\x04_sub"; - src = s0; // Copy the first label - len = *src; - for (i=0; i <= len; i++) *dst++ = *src++; - for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i]; - type = (domainname *)s1; - - // Special support for queries done by older versions of "Rendezvous Browser" - // For these queries, we retract the "._sub" we just added between the subtype and the main type - if (SameDomainName((domainname*)s0, (domainname*)"\x09_services\x07_dns-sd\x04_udp") || - SameDomainName((domainname*)s0, (domainname*)"\x09_services\x05_mdns\x04_udp")) - dst -= sizeof(SubTypeLabel); - } - } - } - } - - if (name && name->c[0]) - { - src = name->c; // Put the service name into the domain name - len = *src; - 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 < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, (domainname*)"\x05" "local"))) - { - errormsg="Application protocol name must be underscore plus 1-14 characters. See "; - goto fail; - } - if (src[1] != '_') { errormsg="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="Application protocol name must contain only letters, digits, and hyphens"; goto fail; } - for (i=0; i<=len; i++) *dst++ = *src++; - - len = *src; - 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="Transport protocol name must be _udp or _tcp"; goto fail; } - for (i=0; i<=len; i++) *dst++ = *src++; - - if (*src) { errormsg="Service type must have only two labels"; goto fail; } - - *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, - domainlabel *const name, domainname *const type, domainname *const domain) - { - int i, len; - const mDNSu8 *src = fqdn->c; - const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME; - mDNSu8 *dst; - - dst = name->c; // Extract the service name from the domain name - len = *src; - if (len >= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - - dst = type->c; // Extract the service type from the domain name - len = *src; - if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - - len = *src; - if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - *dst++ = 0; // Put the null root label on the end of the service type - - dst = domain->c; // Extract the service domain from the domain name - while (*src) - { - len = *src; - if (len >= 0x40) - { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse); } - if (src + 1 + len + 1 >= max) - { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse); } - for (i=0; i<=len; i++) *dst++ = *src++; - } - *dst++ = 0; // Put the null root label on the end - - return(mDNStrue); - } - -// 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) - { - 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]--; } - - // 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)") - - // 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 (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; } - else { name->c[++name->c[0]] = '-'; } - - while (divisor) - { - name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor); - 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 COMPILER_LIKES_PRAGMA_MARK -#pragma mark - -#pragma mark - Resource Record Utility Functions -#endif - -#define RRIsAddressType(RR) ((RR)->resrec.rrtype == kDNSType_A || (RR)->resrec.rrtype == kDNSType_AAAA) -#define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA) - -#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)->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) - -// 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->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->rdata->u.name, &r2->rdata->u.name)); - - 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->rdata->u.data, r2->rdata->u.data, r1->rdlength)); - } - } - -mDNSlocal mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) - { - 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->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)); - } - -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) - { - 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); - } - -// 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) { 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)); - } - -// 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 (!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)); - } - -// 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 (!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)); - } - -// 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 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. - // If the requester's indicated TTL is at least half the real TTL, - // then we can suppress our answer this time. - // If the requester's indicated TTL is greater than the TTL we believe, - // then that's okay, and we don't need to do anything about it. - // (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(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2); - } - -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(rd->ip)); - case kDNSType_CNAME:// Same as PTR - 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)); - 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->rdlength); - } - } - -mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr) - { - if (rr->resrec.RecordType == kDNSRecordTypeUnique) - { - if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) - m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); + //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, GetRRDisplayString(m, rr)); + if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) + m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); } else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr)) { @@ -2269,10 +1752,6 @@ mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const r } } -#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) { // To allow us to aggregate probes when a group of services are registered together, @@ -2317,21 +1796,7 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) SetNextAnnounceProbeTime(m, rr); } -mDNSlocal void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength) - { - 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; - } +#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS) mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) { @@ -2379,49 +1844,6 @@ mDNSlocal void CompleteProbing(mDNS *const m, AuthRecord *const rr) } } -#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 - } - } - // 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)) @@ -2435,9 +1857,15 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) AuthRecord **l = &m->LocalOnlyRecords; #if TEST_LOCALONLY_FOR_EVERYTHING - rr->resrec.InterfaceID = (mDNSInterfaceID)~0; + rr->resrec.InterfaceID = mDNSInterface_LocalOnly; #endif + // If the client has specified an explicit InterfaceID, + // then we do a multicast registration on that interface, even for unicast domains. + if (rr->resrec.InterfaceID || IsLocalDomain(&rr->resrec.name)) + rr->uDNS_info.id = zeroID; + else return uDNS_RegisterRecord(m, rr); + while (*p && *p != rr) p=&(*p)->next; while (*d && *d != rr) d=&(*d)->next; while (*l && *l != rr) l=&(*l)->next; @@ -2466,7 +1894,7 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } // If this resource record is referencing a specific interface, make sure it exists - if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != ((mDNSInterfaceID)~0)) + if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly) { NetworkInterfaceInfo *intf; for (intf = m->HostInterfaces; intf; intf = intf->next) @@ -2489,6 +1917,8 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // rr->Context = already set in mDNS_SetupResourceRecord // rr->RecordType = already set in mDNS_SetupResourceRecord // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client + // Make sure target is not uninitialized data, or we may crash writing debugging log messages + if (rr->HostTarget && target) target->c[0] = 0; // Field Group 2: Transient state for Authoritative Records rr->Acknowledged = mDNSfalse; @@ -2504,7 +1934,7 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); - InitializeLastAPTime(m, rr); + if (!rr->HostTarget) InitializeLastAPTime(m, rr); // rr->AnnounceUntil = Set for us in InitializeLastAPTime() // rr->LastAPTime = Set for us in InitializeLastAPTime() // rr->LastMCTime = Set for us in InitializeLastAPTime() @@ -2524,10 +1954,7 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // 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 - } + SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime(); else { rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); @@ -2545,7 +1972,7 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->resrec.rdatahash = RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u); rr->resrec.rdnamehash = target ? DomainNameHashValue(target) : 0; - if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0)) + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) { debugf("Adding %p %##s (%s) to LocalOnly list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); *l = rr; @@ -2603,10 +2030,21 @@ mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) 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); + if (m->NumFailedProbes >= 10) + { + m->SuppressProbes = (m->timenow + mDNSPlatformOneSecond * 5) | 1; + LogMsg("Excessive name conflicts (%d) for %##s (%s); rate limiting in effect", + m->NumFailedProbes, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + } + } + +mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr) + { + 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 } // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal @@ -2620,7 +2058,11 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, { 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; + + if (!rr->resrec.InterfaceID && !IsLocalDomain(&rr->resrec.name) && rr->uDNS_info.id.NotAnInteger) + return uDNS_DeregisterRecord(m, rr); + + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) p = &m->LocalOnlyRecords; while (*p && *p != rr) p=&(*p)->next; if (*p) @@ -2690,565 +2132,60 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, rr->resrec.RecordType = kDNSRecordTypeDeregistering; rr->resrec.rroriginalttl = 0; rr->ImmedAnswer = mDNSInterfaceMark; - if (rr->resrec.InterfaceID == ((mDNSInterfaceID)~0)) + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) m->DiscardLocalOnlyRecords = mDNStrue; else { 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->NewLocalOnlyRecords == rr) m->NewLocalOnlyRecords = rr->next; - rr->next = mDNSNULL; - - if (RecordType == kDNSRecordTypeUnregistered) - 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->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - else - { - 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->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->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 - } - - // 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) - { - 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 COMPILER_LIKES_PRAGMA_MARK -#pragma mark - -#pragma mark - -#pragma mark - DNS Message Creation Functions -#endif - -mDNSlocal void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags) - { - h->id = id; - h->flags = flags; - h->numQuestions = 0; - h->numAnswers = 0; - h->numAuthorities = 0; - h->numAdditionals = 0; - } - -mDNSlocal const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname) - { - const mDNSu8 *result = end - *domname - 1; - - if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label - - // This loop examines each possible starting position in packet, starting end of the packet and working backwards - while (result >= base) - { - // If the length byte and first character of the label match, then check further to see - // if this location in the packet will yield a useful name compression pointer. - if (result[0] == domname[0] && result[1] == domname[1]) - { - const mDNSu8 *name = domname; - const mDNSu8 *targ = result; - while (targ + *name < end) - { - // First see if this label matches - int i; - const mDNSu8 *pointertarget; - for (i=0; i <= *name; i++) if (targ[i] != name[i]) break; - if (i <= *name) break; // If label did not match, bail out - targ += 1 + *name; // Else, did match, so advance target pointer - name += 1 + *name; // and proceed to check next label - if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match! - if (*name == 0) break; // If no more labels to match, we failed, so bail out - - // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches - if (targ[0] < 0x40) continue; // If length value, continue to check next label - if (targ[0] < 0xC0) break; // If 40-BF, not valid - if (targ+1 >= end) break; // Second byte not present! - pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1]; - if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet - if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte - targ = pointertarget; - } - } - result--; // We failed to match at this search position, so back up the tentative result pointer and try again - } - return(mDNSNULL); - } - -// Put a string of dot-separated labels as length-prefixed labels -// domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't) -// msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers) -// end points to the end of the message so far -// ptr points to where we want to put the name -// limit points to one byte past the end of the buffer that we must not overrun -// domainname is the name to put -mDNSlocal mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, - mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name) - { - const mDNSu8 *const base = (const mDNSu8 *)msg; - const mDNSu8 * np = name->c; - const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid - const mDNSu8 * pointer = mDNSNULL; - const mDNSu8 *const searchlimit = ptr; - - 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) - { 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 - { - mDNSu16 offset = (mDNSu16)(pointer - base); - *ptr++ = (mDNSu8)(0xC0 | (offset >> 8)); - *ptr++ = (mDNSu8)( offset ); - return(ptr); - } - else // Else copy one label and try again - { - int i; - mDNSu8 len = *np++; - if (ptr + 1 + len >= limit) return(mDNSNULL); - *ptr++ = len; - for (i=0; irrtype) - { - case kDNSType_A: if (rr->rdlength != 4) - { - debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); - return(mDNSNULL); - } - if (ptr + 4 > limit) return(mDNSNULL); - *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, &rr->rdata->u.name)); - - 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_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)); - - 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); - } - } - -mDNSlocal mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl) - { - mDNSu8 *endofrdata; - 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, - // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet - if (msg->h.numAnswers || msg->h.numAuthorities || msg->h.numAdditionals) - limit = msg->data + NormalMaxDNSMessageData; - - if (rr->RecordType == kDNSRecordTypeUnregistered) - { - LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name.c, DNSTypeName(rr->rrtype)); - return(ptr); - } - - 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); - ptr[1] = (mDNSu8)(rr->rrtype ); - ptr[2] = (mDNSu8)(rr->rrclass >> 8); - ptr[3] = (mDNSu8)(rr->rrclass ); - 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 = (mDNSu16)(endofrdata - ptr - 10); - ptr[8] = (mDNSu8)(actualLength >> 8); - ptr[9] = (mDNSu8)(actualLength ); - - (*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 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->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)++; - return(ptr + 10); - } -#endif - -mDNSlocal mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, - const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass) - { - ptr = putDomainNameAsLabels(msg, ptr, limit, name); - if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL - ptr[0] = (mDNSu8)(rrtype >> 8); - ptr[1] = (mDNSu8)(rrtype ); - ptr[2] = (mDNSu8)(rrclass >> 8); - ptr[3] = (mDNSu8)(rrclass ); - msg->h.numQuestions++; - return(ptr+4); - } - -// *************************************************************************** -#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) - { - mDNSu16 total = 0; - - if (ptr < (mDNSu8*)msg || ptr >= end) - { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } - - while (1) // Read sequence of labels - { - const mDNSu8 len = *ptr++; // Read length of this label - if (len == 0) return(ptr); // If length is zero, that means this name is complete - switch (len & 0xC0) - { - case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label - { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } - if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label - { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); } - ptr += len; - total += 1 + len; - break; - - case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL); - case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL); - case 0xC0: return(ptr+1); - } - } - } - -// Routine to fetch an FQDN from the DNS message, following compression pointers if necessary. -mDNSlocal const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, - domainname *const name) - { - const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers - mDNSu8 *np = name->c; // Name pointer - const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer - - if (ptr < (mDNSu8*)msg || ptr >= end) - { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } - - *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) - - while (1) // Read sequence of labels - { - const mDNSu8 len = *ptr++; // Read length of this label - if (len == 0) break; // If length is zero, that means this name is complete - switch (len & 0xC0) - { - int i; - mDNSu16 offset; - - case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label - { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } - if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label - { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); } - *np++ = len; - for (i=0; ic); - return(mDNSNULL); - - case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL); - - case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++); - if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers - ptr = (mDNSu8 *)msg + offset; - if (ptr < (mDNSu8*)msg || ptr >= end) - { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); } - if (*ptr & 0xC0) - { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); } - break; - } - } - - if (nextbyte) return(nextbyte); - else return(ptr); - } - -mDNSlocal const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) - { - mDNSu16 pktrdlength; - - ptr = skipDomainName(msg, ptr, end); - if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); } - - if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } - pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); - ptr += 10; - if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } - - return(ptr + pktrdlength); - } - -#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; - rr->resrec.RecordType = RecordType; - - 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->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->resrec.RecordType |= kDNSRecordTypePacketUniqueMask; - ptr += 10; - if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } - - if (RDataStorage) - rr->resrec.rdata = RDataStorage; - else - { - rr->resrec.rdata = (RData*)&rr->rdatastorage; - rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); - } - - switch (rr->resrec.rrtype) - { - 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:// 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_NULL: //Same as TXT - case kDNSType_HINFO://Same as TXT - case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength) - { - debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)", - DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); - return(mDNSNULL); - } - rr->resrec.rdlength = pktrdlength; - mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength); - break; - - case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6)); - break; - - 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 (%s) size (%d) exceeds storage (%d)", - rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); - return(mDNSNULL); - } - 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->resrec.rdlength = pktrdlength; - mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength); - break; - } - - rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name); - SetNewRData(&rr->resrec, mDNSNULL, 0); - - return(ptr + pktrdlength); - } - -mDNSlocal const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) - { - ptr = skipDomainName(msg, ptr, end); - if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); } - if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } - return(ptr+4); - } + } + } + 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->NewLocalOnlyRecords == rr) m->NewLocalOnlyRecords = rr->next; + rr->next = mDNSNULL; -mDNSlocal const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, - DNSQuestion *question) - { - 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->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); - } + if (RecordType == kDNSRecordTypeUnregistered) + 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->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + else + { + verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeUnregistered; + } -mDNSlocal const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end) - { - int i; - const mDNSu8 *ptr = msg->data; - for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end); - return(ptr); - } + 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->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); -mDNSlocal const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end) - { - int i; - const mDNSu8 *ptr = LocateAnswers(msg, end); - for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end); - return(ptr); + // If we have an update queued up which never executed, give the client a chance to free that memory + if (rr->NewRData) CompleteRDataUpdate(m, rr); // Update our rdata, clear the NewRData pointer, and return memory to the client + + // 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) + { + 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); } // *************************************************************************** @@ -3258,38 +2195,6 @@ mDNSlocal const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDN #pragma mark - Packet Sending Functions #endif -mDNSlocal mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, mDNSIPPort srcport, const mDNSAddr *dst, mDNSIPPort dstport) - { - mStatus status; - mDNSu16 numQuestions = msg->h.numQuestions; - mDNSu16 numAnswers = msg->h.numAnswers; - mDNSu16 numAuthorities = msg->h.numAuthorities; - mDNSu16 numAdditionals = msg->h.numAdditionals; - - // Put all the integer values in IETF byte-order (MSB first, LSB second) - mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; - *ptr++ = (mDNSu8)(numQuestions >> 8); - *ptr++ = (mDNSu8)(numQuestions ); - *ptr++ = (mDNSu8)(numAnswers >> 8); - *ptr++ = (mDNSu8)(numAnswers ); - *ptr++ = (mDNSu8)(numAuthorities >> 8); - *ptr++ = (mDNSu8)(numAuthorities ); - *ptr++ = (mDNSu8)(numAdditionals >> 8); - *ptr++ = (mDNSu8)(numAdditionals ); - - // Send the packet on the wire - 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; - msg->h.numAnswers = numAnswers; - msg->h.numAuthorities = numAuthorities; - msg->h.numAdditionals = numAdditionals; - - return(status); - } - mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr) { // Setting AnnounceCount to InitialAnnounceCount signals mDNS_Deregister_internal() @@ -3316,6 +2221,12 @@ mDNSlocal void DiscardDeregistrations(mDNS *const m) } } +mDNSlocal void GrantUpdateCredit(AuthRecord *rr) + { + if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; + else rr->NextUpdateCredit = (rr->NextUpdateCredit + kUpdateCreditRefreshInterval) | 1; + } + mDNSlocal mDNSBool HaveSentEntireRRSet(const mDNS *const m, const AuthRecord *const rr, mDNSInterfaceID InterfaceID) { // Try to find another member of this set that we're still planning to send on this interface @@ -3357,11 +2268,7 @@ mDNSlocal void SendResponses(mDNS *const m) // 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) { - if (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) - { - if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0; - else rr->NextUpdateCredit = (m->timenow + mDNSPlatformOneSecond * 60) | 1; - } + while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr)) { rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces @@ -3380,17 +2287,20 @@ mDNSlocal void SendResponses(mDNS *const m) 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) + // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals + // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID, + // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen) + // then all that means is that it won't get sent -- which would not be the end of the world. 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) ... + if (RRTypeIsAddressType(r2->resrec.rrtype) && // 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 + r2->ImmedAdditional = r2->resrec.InterfaceID; // ... 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 @@ -3438,12 +2348,13 @@ mDNSlocal void SendResponses(mDNS *const m) } else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface: { - rr->SendRNow = rr->ImmedAnswer; // Just respond on that interface + 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); + //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, GetRRDisplayString(m, rr)); } // *** @@ -3482,7 +2393,7 @@ mDNSlocal void SendResponses(mDNS *const m) { 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. + // See if we should send a courtesy "goodbye" for 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) @@ -3537,7 +2448,16 @@ mDNSlocal void SendResponses(mDNS *const m) 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); - if (newptr) responseptr = newptr; + if (newptr) + { + responseptr = newptr; + // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface. + // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise, + // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get + // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches. + rr->LastMCTime = m->timenow; + rr->LastMCInterface = intf->InterfaceID; + } rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state } } @@ -3549,8 +2469,8 @@ mDNSlocal void SendResponses(mDNS *const m) 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 (intf->IPv4Available) mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &response, responseptr, intf->InterfaceID, &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; } @@ -3578,14 +2498,10 @@ mDNSlocal void SendResponses(mDNS *const m) 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->SendRNow) + { LogMsg("SendResponses: No active interface to send: %s", GetRRDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } + + if (rr->NewRData) CompleteRDataUpdate(m,rr); // Update our rdata, clear the NewRData pointer, and return memory to the client if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) CompleteDeregistration(m, rr); @@ -3643,6 +2559,12 @@ mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr) mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) { + if (!rr->resrec.InterfaceID && !IsLocalDomain(&rr->resrec.name)) + { + LogMsg("mDNS_Reconfirm_internal: Not implemented for unicast DNS"); + return mStatus_UnsupportedErr; + } + if (interval < kMinimumReconfirmTime) interval = kMinimumReconfirmTime; if (interval > 0x10000000) // Make sure interval doesn't overflow when we multiply by four below @@ -3669,7 +2591,7 @@ mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) { - mDNSBool ucast = q->LargeAnswers || q->ThisQInterval <= InitialQuestionInterval*2; + mDNSBool ucast = (q->LargeAnswers || q->ThisQInterval <= InitialQuestionInterval*2) && m->CanReceiveUnicast; 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)); @@ -3695,8 +2617,7 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer 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 + rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0) // and it is less than half-way to expiry { *ka = rr; // Link this record into our known answer chain ka = &rr->NextInKAList; @@ -3882,13 +2803,32 @@ mDNSlocal void SendQueries(mDNS *const m) { q = rr->CRActiveQuestion; ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID); - if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID; + if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If unicast query, mark it + else 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 unicast queries need to be sent + for (q = m->Questions; q; q=q->next) + if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow))) + { + DNSMessage query; + mDNSu8 *qptr = query.data; + const mDNSu8 *const limit = query.data + sizeof(query.data); + InitializeDNSMessage(&query.h, q->TargetQID, QueryFlags); + qptr = putQuestion(&query, qptr, limit, &q->qname, q->qtype, q->qclass); + mDNSSendDNSMessage(m, &query, qptr, mDNSInterface_Any, &q->Target, q->TargetPort); + q->ThisQInterval *= 2; + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswers = 0; + q->SendQNow = mDNSNULL; + m->ExpectUnicastResponse = m->timenow; + } + // 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)) + if (!q->Target.type && TimeToSendThisQuestion(q, m->timenow)) { q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces if (maxExistingQuestionInterval < q->ThisQInterval) @@ -3900,7 +2840,8 @@ mDNSlocal void SendQueries(mDNS *const m) // (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 (q->SendQNow || + (!q->Target.type && 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 @@ -4021,7 +2962,7 @@ mDNSlocal void SendQueries(mDNS *const m) for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->SendRNow == intf->InterfaceID) { - mDNSBool ucast = rr->ProbeCount >= DefaultProbeCountForTypeUnique-1; + mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicast; 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)); @@ -4085,8 +3026,8 @@ mDNSlocal void SendQueries(mDNS *const m) 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 (intf->IPv4Available) mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &query, queryptr, intf->InterfaceID, &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; } @@ -4103,6 +3044,14 @@ mDNSlocal void SendQueries(mDNS *const m) intf = next; } } + + // Final sanity check for debugging purposes + { + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->SendRNow) + { LogMsg("SendQueries: No active interface to send: %s", GetRRDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } + } } // *************************************************************************** @@ -4294,7 +3243,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) 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 + // 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) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set"); @@ -4431,7 +3380,11 @@ mDNSlocal CacheRecord *GetFreeCacheRR(mDNS *const m, mDNSu16 RDLength) debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu", m->rrcache_size, m->rrcache_active); else + { + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback m->MainCallback(m, mStatus_GrowCache); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } } // If we still have no free records, recycle all the records we can. @@ -4558,6 +3511,7 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) if (m->NewLocalOnlyRecords) return(m->timenow); if (m->DiscardLocalOnlyRecords) return(m->timenow); if (m->SuppressSending) return(m->SuppressSending); + if (e - m->uDNS_info.nextevent > 0) e = m->uDNS_info.nextevent; if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; @@ -4590,6 +3544,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) { mDNS_Lock(m); // Must grab lock before trying to read m->timenow + if (m->timenow - m->NextScheduledEvent >= 0) { int i; @@ -4681,6 +3636,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) // 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). + uDNS_Execute(m); mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value return(m->NextScheduledEvent); } @@ -4703,10 +3659,11 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) mDNS_Lock(m); m->SleepState = sleepstate; - LogMsg("mDNSResponder %s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow); + LogMsg("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow); if (sleepstate) { + uDNS_SuspendLLQs(m); // Mark all the records we need to deregister and send them for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->AnnounceCount < InitialAnnounceCount) @@ -4719,7 +3676,9 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) mDNSu32 slot; CacheRecord *cr; - // 1. Retrigger all our questions + uDNS_RestartLLQs(m); + + // 1. Retrigger all our questions for (q = m->Questions; q; q=q->next) // Scan our list of questions if (ActiveQuestion(q)) { @@ -5105,7 +4064,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // 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->Target.type && 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)) @@ -5136,10 +4095,10 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // For SRV records, automatically add the Address record(s) for the target host if (rr->resrec.rrtype == kDNSType_SRV) for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records - if (RRIsAddressType(rr2) && // For all address records (A/AAAA) ... + if (RRTypeIsAddressType(rr2->resrec.rrtype) && // 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 + rr->resrec.rdnamehash == rr2->resrec.namehash && // ... whose name is the name of the SRV target + SameDomainName(&rr->resrec.rdata->u.srv.target, &rr2->resrec.name)) AddRecordToResponseList(&nrp, rr2, rr); } @@ -5174,7 +4133,13 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr; } - if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) rr->ImmedAnswer = mDNSNULL; + if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) + { + rr->ImmedAnswer = mDNSNULL; +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, GetRRDisplayString(m, rr)); +#endif + } } } @@ -5238,17 +4203,19 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con if (SendMulticastResponse) { +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + rr->ImmedAnswerMarkTime = m->timenow; +#endif + m->NextScheduledResponse = m->timenow; // If we're already planning to send this on another interface, just send it on all interfaces if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID) { 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 { 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; @@ -5265,7 +4232,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms else delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms - } + } } else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0) { @@ -5283,6 +4250,11 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50)) { +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + mDNSs32 oldss = m->SuppressSending; + if (oldss && delayresponse) + LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50); +#endif // Pick a random delay: // We start with the base delay chosen above (typically either 1 second or 20 seconds), // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds). @@ -5298,10 +4270,14 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // or 400-500ms in the case of multi-packet known-answer lists. m->SuppressSending = m->timenow + (delayresponse + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*5) + 49) / 50; if (m->SuppressSending == 0) m->SuppressSending = 1; +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + if (oldss && delayresponse) + LogMsg("Set SuppressSending to %5ld", m->SuppressSending - m->timenow); +#endif } // *** - // *** 8. If query is from a legacy client, generate a unicast response too + // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too // *** if (HaveUnicastAnswer) responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); @@ -5395,64 +4371,42 @@ exit: return(responseptr); } -mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) - { - NetworkInterfaceInfo *intf; - - if (addr->type == mDNSAddrType_IPv4) - { - if (addr->ip.v4.b[0] == 169 && addr->ip.v4.b[1] == 254) return(mDNStrue); - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID) - if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) - return(mDNStrue); - } - - if (addr->type == mDNSAddrType_IPv6) - { - if (addr->ip.v6.b[0] == 0xFE && addr->ip.v6.b[1] == 0x80) return(mDNStrue); - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID) - if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) && - (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) && - (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) && - (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0)) - return(mDNStrue); - } - - return(mDNSfalse); - } - mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) { - if (mDNSAddrIsDNSMulticast(dstaddr) || AddressIsLocalSubnet(m, InterfaceID, srcaddr)) + DNSMessage response; + mDNSu8 *responseend = mDNSNULL; + + if (!InterfaceID) { - DNSMessage response; - const mDNSu8 *responseend = mDNSNULL; - - 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, + LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%.8X with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), 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, InterfaceID, - (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), &response); + return; + } - if (responseend) // If responseend is non-null, that means we built a unicast response packet - { - 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); - } + verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%.8X with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", + srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), 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, InterfaceID, + (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), &response); + + if (responseend) // If responseend is non-null, that means we built a unicast response packet + { + 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, mDNSVal16(srcport), InterfaceID, srcaddr->type); + mDNSSendDNSMessage(m, &response, responseend, InterfaceID, srcaddr, srcport); } } @@ -5460,9 +4414,11 @@ 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 mDNSAddr *srcaddr, const mDNSAddr *dstaddr, - const mDNSInterfaceID InterfaceID) + const DNSMessage *const response, const mDNSu8 *end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID, mDNSu8 ttl) { + static mDNSu32 NumPktsAccepted = 0, NumPktsIgnored = 0; int i; const mDNSu8 *ptr = LocateAnswers(response, end); // We ignore questions (if any) in a DNS response packet CacheRecord *CacheFlushRecords = mDNSNULL; @@ -5475,25 +4431,31 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, (void)srcaddr; // Currently used only for display in debugging message - verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", - srcaddr, dstaddr, InterfaceID, + 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"); - // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us - if (!mDNSAddrIsDNSMulticast(dstaddr)) - if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2)) - { - debugf("** Ignored apparent spoof mDNS Response from %#-15a to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", - srcaddr, dstaddr, 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; - } + // 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*2))) + { + mDNSBool ignoredlots = (++NumPktsIgnored > NumPktsAccepted + 10); + if (ignoredlots || NumPktsIgnored <= 10) + LogMsg("Ignored apparent spoof mDNS Response with TTL %d from %#-15a:%-5d to %#-15a:%-5d on %p with %2d Q %2d Ans %2d Auth %2d Add", + ttl, srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, + response->h.numQuestions, response->h.numAnswers, response->h.numAuthorities, response->h.numAdditionals); + if (ignoredlots) + LogMsg("WARNING: Have ignored %lu packets out of %lu; this may indicate an error in the platform support layer.", + NumPktsIgnored, NumPktsIgnored + NumPktsAccepted); + return; + } + NumPktsAccepted++; for (i = 0; i < totalrecords && ptr && ptr < end; i++) { @@ -5696,13 +4658,12 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, 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 unusedttl) + 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)unusedttl; + const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; + const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + const mDNSu8 UpdateR = kDNSFlag0_OP_Update | kDNSFlag0_QR_Response; + 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; @@ -5710,17 +4671,25 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS 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 (!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; } + + if (dstaddr->type == mDNSAddrType_IPv4 && dstaddr->ip.v4.NotAnInteger != AllDNSLinkGroup.NotAnInteger && + (QR_OP == StdR || QR_OP == UpdateR ) && msg->h.id.NotAnInteger) + { + uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, ttl); + return; + } mDNS_Lock(m); 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); - else debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]); + else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, ttl); + else LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)", + msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID); // Packet reception often causes a change to the task list: // 1. Inbound queries can cause us to need to send responses @@ -5737,6 +4706,8 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS #pragma mark - Searcher Functions #endif +#define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger) + mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question) { DNSQuestion *q; @@ -5746,6 +4717,7 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest // further in the list. 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, + SameQTarget(q, question) && // and same unicast/multicast target settings q->qtype == question->qtype && // type, q->qclass == question->qclass && // class, q->qnamehash == question->qnamehash && @@ -5771,11 +4743,34 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const } } +#define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \ + ((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger)) + mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) { #if TEST_LOCALONLY_FOR_EVERYTHING - question->InterfaceID = (mDNSInterfaceID)~0; + question->InterfaceID = mDNSInterface_LocalOnly; #endif + + if (question->Target.type && !ValidQuestionTarget(question)) + { + LogMsg("Warning! Target.type = %d port = %u (Client forgot to initialize before calling mDNS_StartQuery?)", + question->Target.type, mDNSVal16(question->TargetPort)); + question->Target.type = mDNSAddrType_None; + } + + // If the client has specified an explicit InterfaceID, + // then we do a multicast query on that interface, even for unicast domains. + if (question->InterfaceID || IsLocalDomain(&question->qname)) + question->uDNS_info.id = zeroID; + else return uDNS_StartQuery(m, question); + + // The special interface ID "-2" means + // "do this query via multicast on all interfaces, even if it's apparently a unicast domain" + // After it's served its purpose by preventing a unicast query above, we now set it to mDNSInterface_Any. + if (question->InterfaceID == mDNSInterface_ForceMCast) + question->InterfaceID = mDNSInterface_Any; + if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated return(mStatus_NoCache); else @@ -5783,7 +4778,7 @@ mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const que 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; + if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; while (*q && *q != question) q=&(*q)->next; if (*q) @@ -5794,7 +4789,7 @@ mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const que } // If this question is referencing a specific interface, make sure it exists - if (question->InterfaceID && question->InterfaceID != ((mDNSInterfaceID)~0)) + if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly) { NetworkInterfaceInfo *intf; for (intf = m->HostInterfaces; intf; intf = intf->next) @@ -5802,7 +4797,7 @@ mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const que if (!intf) { debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID); - return(mStatus_BadReferenceErr); + return(mStatus_BadInterfaceErr); } } @@ -5843,7 +4838,7 @@ mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const que question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question, question->DuplicateOf); *q = question; - if (question->InterfaceID == ((mDNSInterfaceID)~0)) + if (question->InterfaceID == mDNSInterface_LocalOnly) { if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; } @@ -5861,7 +4856,10 @@ mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const ques { CacheRecord *rr; DNSQuestion **q = &m->Questions; - if (question->InterfaceID == ((mDNSInterfaceID)~0)) q = &m->LocalOnlyQuestions; + + if (IsActiveUnicastQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question); + + if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions; while (*q && *q != question) q=&(*q)->next; if (*q) *q = (*q)->next; else @@ -5958,14 +4956,27 @@ mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, const domainname *const srv, const domainname *const domain, 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; + question->InterfaceID = InterfaceID; + question->Target = zeroAddr; + 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)); + + // If the client has specified an explicit InterfaceID, + // then we do a multicast query on that interface, even for unicast domains. + if (question->InterfaceID || IsLocalDomain(&question->qname)) + { + question->LongLived = mDNSfalse; + question->uDNS_info.id = zeroID; + return(mDNS_StartQuery(m, question)); + } + else + { + question->LongLived = mDNStrue; + return uDNS_StartQuery(m, question); + } } mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) @@ -6020,7 +5031,7 @@ mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const R 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]); + mDNSVal16(answer->rdata->u.srv.port)); query->ServiceInfoQueryCallback(m, query); } // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's @@ -6036,6 +5047,7 @@ mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const R query->GotTXT = mDNStrue; query->info->TXTlen = answer->rdlength; + query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero mDNSPlatformMemCopy(answer->rdata->u.txt.c, query->info->TXTinfo, answer->rdlength); verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); @@ -6102,32 +5114,36 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, mStatus status; mDNS_Lock(m); - query->qSRV.ThisQInterval = -1; // This question not yet in the question list + query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question query->qSRV.InterfaceID = info->InterfaceID; + query->qSRV.Target = zeroAddr; 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.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question query->qTXT.InterfaceID = info->InterfaceID; + query->qTXT.Target = zeroAddr; 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.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question query->qAv4.InterfaceID = info->InterfaceID; + query->qAv4.Target = zeroAddr; 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.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question query->qAv6.InterfaceID = info->InterfaceID; + query->qAv6.Target = zeroAddr; query->qAv6.qname.c[0] = 0; query->qAv6.qtype = kDNSType_AAAA; query->qAv6.qclass = kDNSClass_IN; @@ -6160,27 +5176,31 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query) { 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->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qAv4); - if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &query->qAv6); + if (query->qSRV.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qSRV, &m->uDNS_info)) + mDNS_StopQuery_internal(m, &query->qSRV); + if (query->qTXT.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qTXT, &m->uDNS_info)) + mDNS_StopQuery_internal(m, &query->qTXT); + if (query->qAv4.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qAv4, &m->uDNS_info)) + mDNS_StopQuery_internal(m, &query->qAv4); + if (query->qAv6.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qAv6, &m->uDNS_info)) + mDNS_StopQuery_internal(m, &query->qAv6); mDNS_Unlock(m); } -mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, +mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) { - MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType]); question->InterfaceID = InterfaceID; + question->Target = zeroAddr; question->qtype = kDNSType_PTR; question->qclass = kDNSClass_IN; question->QuestionCallback = Callback; question->QuestionContext = Context; - - // No sense doing this until we actually support unicast query/update - //return(mDNS_StartQuery(m, question)); - (void)m; // Unused - return(mStatus_NoError); + if (DomainType > mDNS_DomainTypeRegistrationDefault) return(mStatus_BadParamErr); + if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); + if (!dom) dom = (const domainname *)"\x5local"; + if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr); + return(mDNS_StartQuery(m, question)); } // *************************************************************************** @@ -6246,6 +5266,13 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback) { + + if (!rr->resrec.InterfaceID && !IsLocalDomain(&rr->resrec.name)) + { + LogMsg("mDNS_Update: Not implemented for unicast DNS"); + return mStatus_UnsupportedErr; + } + 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); } @@ -6263,26 +5290,43 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt 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) + + if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)) + CompleteRDataUpdate(m, rr); + else { - 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); + domainlabel name; + domainname type, domain; + DeconstructServiceName(&rr->resrec.name, &name, &type, &domain); + if (rr->AnnounceCount < ReannounceCount) + rr->AnnounceCount = ReannounceCount; + // iChat often does suprious record updates where no data has changed. For the _presence service type, using + // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful + // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data + // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us. + // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement. + if (SameDomainLabel(type.c, (mDNSu8*)"\x6_ichat")) rr->AnnounceCount = 1; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + InitializeLastAPTime(m, rr); + while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr); + if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--; + if (!rr->NextUpdateCredit) rr->NextUpdateCredit = (m->timenow + kUpdateCreditRefreshInterval) | 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 second%s", rr->resrec.name.c, delay, delay > 1 ? "s" : ""); + } + rr->resrec.rroriginalttl = newttl; } - rr->resrec.rroriginalttl = newttl; + mDNS_Unlock(m); return(mStatus_NoError); } @@ -6299,7 +5343,7 @@ mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) return(status); } -mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); +mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result); mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) { @@ -6309,13 +5353,15 @@ mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) return(intf); } -mDNSlocal void mDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) +mDNSlocal void 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); + + // Send dynamic update for non-linklocal IPv4 Addresses + uDNS_AdvertiseInterface(m, set); + mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kDefaultTTLforUnique, kDNSRecordTypeUnique, mDNS_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); @@ -6371,10 +5417,11 @@ mDNSlocal void mDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) } } -mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) +mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) { NetworkInterfaceInfo *intf; - // If we still have address records referring to this one, update them + + // 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) @@ -6382,7 +5429,7 @@ mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *se 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 + // When doing the mDNS_Close processing, we first call 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(). @@ -6391,48 +5438,68 @@ mDNSlocal void mDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *se if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); } -mDNSexport void mDNS_GenerateFQDN(mDNS *const m) +mDNSlocal void GenerateFQDN(mDNS *const m, const char *domain, mDNSBool local) { domainname newname; mDNS_Lock(m); 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)) + if (!AppendDomainLabel(&newname, &m->hostlabel)) LogMsg("ERROR: GenerateFQDN: Cannot create hostname"); + if (!AppendDNSNameString(&newname, domain)) LogMsg("ERROR: GenerateFQDN: Cannot create hostname"); + + if ((local && !SameDomainName(&m->hostname, &newname)) || + (!local && !SameDomainName(&m->uDNS_info.hostname, &newname))) { NetworkInterfaceInfo *intf; AuthRecord *rr; - m->hostname = newname; + if (local) m->hostname = newname; + else m->uDNS_info.hostname = newname; + //!!!KRS for unicast, we can do the deadvertisements/new adverts in the same update message // 1. Stop advertising our address records on all interfaces for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) mDNS_DeadvertiseInterface(m, intf); + if (intf->Advertise) + local ? DeadvertiseInterface(m, intf) : uDNS_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); + if (intf->Advertise) + local ? AdvertiseInterface(m, intf) : uDNS_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); + if (local) + { + 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); + } + else uDNS_UpdateServiceTargets(m); } + mDNS_Unlock(m); + } - mDNS_Unlock(m); +mDNSexport void mDNS_GenerateFQDN(mDNS *const m) + { + GenerateFQDN(m, "local.", mDNStrue); } -mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +mDNSexport void mDNS_GenerateGlobalFQDN(mDNS *const m) { - (void)rr; // Unused parameter + if (!m->uDNS_info.NameRegDomain[0]) return; + GenerateFQDN(m, m->uDNS_info.NameRegDomain, mDNSfalse); + } +mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + (void)rr; // Unused parameter + #if MDNS_DEBUGMSGS { 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); + debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), msg, result); } #endif @@ -6440,24 +5507,45 @@ mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus res { // Notify the client that the host name is successfully registered if (m->MainCallback) + { + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback m->MainCallback(m, result); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } } else if (result == mStatus_NameConflict) { domainlabel oldlabel = m->hostlabel; - + mDNSBool local = (IsLocalDomain(&rr->resrec.name) || rr->resrec.InterfaceID); // 1. First give the client callback a chance to pick a new name if (m->MainCallback) + { + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback m->MainCallback(m, mStatus_NameConflict); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + } // 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); + if (!local) + { + //!!!KRS clean this up + char regdomain[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString_unescaped((domainname *)(rr->resrec.name.c + rr->resrec.name.c[0] + 1), regdomain); + mDNS_GenerateGlobalFQDN(m); + LogMsg("Host Name %s.%s already in use; new name %s.%s selected for this host", oldlabel.c, regdomain, m->hostlabel.c, regdomain); + } + else + { + mDNS_GenerateFQDN(m); + LogMsg("Link-Local Host Name %#s.local. already in use; new name %#s.local. selected for this host.", oldlabel.c, m->hostlabel.c); + } } + else LogMsg("mDNS_HostNameCallback: Unknown error %d for registration of record %s", result, rr->resrec.name.c); } mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) @@ -6468,8 +5556,8 @@ mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *act 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; + if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue; + if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue; } } @@ -6481,8 +5569,8 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s // 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); + set->IPv4Available = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx); + set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx); while (*p) { @@ -6498,8 +5586,8 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s { 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; + if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue; + if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue; } p=&(*p)->next; @@ -6512,7 +5600,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s 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, @@ -6549,7 +5637,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s } if (set->Advertise) - mDNS_AdvertiseInterface(m, set); + AdvertiseInterface(m, set); mDNS_Unlock(m); return(mStatus_NoError); @@ -6630,8 +5718,10 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se // If we were advertising on this interface, deregister those address and reverse-lookup records now if (set->Advertise) - mDNS_DeadvertiseInterface(m, set); - + { + DeadvertiseInterface(m, set); + uDNS_DeadvertiseInterface(m, set); + } // 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. @@ -6676,7 +5766,7 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu if (result == mStatus_MemFree) { - // If the PTR record or any of the subtype PTR records are still in the process of deregistering, + // 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; @@ -6695,7 +5785,7 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu // Note: // Name is first label of domain name (any dots in the name are actual dots, not label separators) -// Type is service type (e.g. "_printer._tcp.") +// Type is service type (e.g. "_ipp._tcp.") // 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.) @@ -6724,7 +5814,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *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.resrec.rdata->MaxRDLength < txtlen) @@ -6753,12 +5843,9 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, // already set the first label of the record name to the subtype being registered for (i=0; iSubTypes[i].resrec.name); - st.c[1+st.c[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService()) - AppendDomainName(&st, type); + 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, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr); + 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; @@ -6771,7 +5858,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, // 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]) AssignDomainName(sr->RR_SRV.resrec.rdata->u.srv.target, sr->Host); - else sr->RR_SRV.HostTarget = mDNStrue; + else { sr->RR_SRV.HostTarget = mDNStrue; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; } // 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 @@ -6784,6 +5871,11 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, } sr->RR_TXT.DependentOn = &sr->RR_SRV; + // If the client has specified an explicit InterfaceID, + // then we do a multicast registration on that interface, even for unicast domains. + if (!InterfaceID && !IsLocalDomain(&sr->RR_SRV.resrec.name)) + return uDNS_RegisterService(m, sr); + mDNS_Lock(m); err = mDNS_Register_internal(m, &sr->RR_SRV); if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); @@ -6795,8 +5887,9 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV); for (i=0; iSubTypes[i]); if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR); - mDNS_Unlock(m); + mDNS_Unlock(m); + if (err) mDNS_DeregisterService(m, sr); return(err); } @@ -6804,8 +5897,17 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl) { - mStatus result = mStatus_UnknownErr; - ExtraResourceRecord **e = &sr->Extras; + ExtraResourceRecord **e; + mStatus status; + + if (!sr->RR_SRV.resrec.InterfaceID && !IsLocalDomain(&sr->RR_SRV.resrec.name)) + { + LogMsg("mDNS_AddRecordToService: Not implemented for unicast DNS"); + return mStatus_UnsupportedErr; + } + + mDNS_Lock(m); + e = &sr->Extras; while (*e) e = &(*e)->next; // If TTL is unspecified, make it the same as the service's TXT and SRV default @@ -6818,42 +5920,59 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name.c); - result = mDNS_Register(m, &extra->r); - if (!result) *e = extra; - return result; + status = mDNS_Register_internal(m, &extra->r); + if (status == mStatus_NoError) *e = extra; + mDNS_Unlock(m); + return(status); } mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra) { - ExtraResourceRecord **e = &sr->Extras; + ExtraResourceRecord **e; + mStatus status; + + if (!sr->RR_SRV.resrec.InterfaceID && !IsLocalDomain(&sr->RR_SRV.resrec.name)) + { + LogMsg("mDNS_AddRecordToService: Not implemented for unicast DNS"); + return mStatus_UnsupportedErr; + } + + mDNS_Lock(m); + e = &sr->Extras; while (*e && *e != extra) e = &(*e)->next; if (!*e) { debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name.c); - return(mStatus_BadReferenceErr); + status = mStatus_BadReferenceErr; } - - debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name.c); - - *e = (*e)->next; - return(mDNS_Deregister(m, &extra->r)); + else + { + debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name.c); + *e = (*e)->next; + status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal); + } + mDNS_Unlock(m); + return(status); } mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname) { - domainlabel name; + // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines + // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally. + domainlabel name1, name2; domainname type, domain; domainname *host = mDNSNULL; ExtraResourceRecord *extras = sr->Extras; mStatus err; - DeconstructServiceName(&sr->RR_SRV.resrec.name, &name, &type, &domain); + DeconstructServiceName(&sr->RR_SRV.resrec.name, &name1, &type, &domain); if (!newname) { - IncrementLabelSuffix(&name, mDNStrue); - newname = &name; + name2 = name1; + IncrementLabelSuffix(&name2, mDNStrue); + newname = &name2; } - debugf("Reregistering as %#s", newname->c); + LogMsg("Service \"%##s\" renamed to \"%#s\"", sr->RR_SRV.resrec.name.c, newname->c); if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host; err = mDNS_RegisterService(m, sr, newname, &type, &domain, @@ -6879,6 +5998,9 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. mDNSexport mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr) { + if (!sr->RR_SRV.resrec.InterfaceID && !IsLocalDomain(&sr->RR_SRV.resrec.name)) + return uDNS_DeregisterService(m, sr); + if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) { debugf("Service set for %##s already deregistered", sr->RR_PTR.resrec.name.c); @@ -6963,7 +6085,7 @@ mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, #pragma mark - Startup and Shutdown #endif -mDNSexport void mDNS_GrowCache(mDNS *const m, CacheRecord *storage, mDNSu32 numrecords) +mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheRecord *storage, mDNSu32 numrecords) { if (storage && numrecords) { @@ -6975,6 +6097,13 @@ mDNSexport void mDNS_GrowCache(mDNS *const m, CacheRecord *storage, mDNSu32 numr } } +mDNSexport void mDNS_GrowCache(mDNS *const m, CacheRecord *storage, mDNSu32 numrecords) + { + mDNS_Lock(m); + mDNS_GrowCache_internal(m, storage, numrecords); + mDNS_Unlock(m); + } + mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, CacheRecord *rrcachestorage, mDNSu32 rrcachesize, mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) @@ -6988,6 +6117,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->p = p; m->KnownBugs = 0; + m->CanReceiveUnicast = mDNSfalse; // Assume we can't receive unicasts, unless platform layer tells us otherwise m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; m->mDNSPlatformStatus = mStatus_Waiting; m->MainCallback = Callback; @@ -7036,7 +6166,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->rrcache_used[slot] = 0; } - mDNS_GrowCache(m, rrcachestorage, rrcachesize); + mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize); // Fields below only required for mDNS Responder... m->hostlabel.c[0] = 0; @@ -7055,6 +6185,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->NumFailedProbes = 0; m->SuppressProbes = 0; + uDNS_Init(m); result = mDNSPlatformInit(m); return(result); @@ -7064,7 +6195,13 @@ mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result) { m->mDNSPlatformStatus = result; if (m->MainCallback) + { + mDNS_Lock(m); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback m->MainCallback(m, mStatus_NoError); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + mDNS_Unlock(m); + } } mDNSexport void mDNS_Close(mDNS *const m) @@ -7074,7 +6211,7 @@ mDNSexport void mDNS_Close(mDNS *const m) mDNSu32 slot; NetworkInterfaceInfo *intf; mDNS_Lock(m); - + m->mDNS_shutdown = mDNStrue; rrcache_totalused = m->rrcache_totalused; @@ -7099,7 +6236,7 @@ mDNSexport void mDNS_Close(mDNS *const m) for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->Advertise) - mDNS_DeadvertiseInterface(m, intf); + DeadvertiseInterface(m, intf); // Make sure there are nothing but deregistering records remaining in the list if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set"); @@ -7115,6 +6252,8 @@ mDNSexport void mDNS_Close(mDNS *const m) } } + uDNS_SuspendLLQs(m); + if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records"); else debugf("mDNS_Close: No deregistering records remain"); diff --git a/mDNSCore/mDNSClientAPI.h b/mDNSCore/mDNSClientAPI.h index 4381c67..df404f7 100755 --- a/mDNSCore/mDNSClientAPI.h +++ b/mDNSCore/mDNSClientAPI.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,37 +25,218 @@ Change History (most recent first): $Log: mDNSClientAPI.h,v $ -Revision 1.114.2.10 2005/01/28 05:02:05 cheshire - SUPan: Replace IP TTL 255 check with local-subnet check +Revision 1.177 2004/06/05 00:04:26 cheshire +: wide-area domains should be returned in reg. domain enumeration + +Revision 1.176 2004/06/04 08:58:29 ksekar +: Keychain integration for secure dynamic update + +Revision 1.175 2004/06/04 00:15:06 cheshire +Move misplaced brackets + +Revision 1.174 2004/06/03 23:30:16 cheshire +Remove extraneous blank lines and white space + +Revision 1.173 2004/06/03 03:09:58 ksekar +: Garbage Collection for Dynamic Updates + +Revision 1.172 2004/06/01 23:46:50 ksekar +: DynDNS: dynamically look up LLQ/Update ports + +Revision 1.171 2004/05/28 23:42:37 ksekar +: Feature: DNS server->client notification on record changes (#7805) -Revision 1.114.2.9 2004/04/22 03:17:35 cheshire +Revision 1.170 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.169 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.168 2004/05/12 22:03:09 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.167 2004/04/22 04:07:01 cheshire +Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it + +Revision 1.166 2004/04/22 03:15:56 cheshire Fix use of "struct __attribute__((__packed__))" so it only applies on GCC >= 2.9 -Revision 1.114.2.8 2004/03/30 06:55:37 cheshire -Gave name to anonymous struct, to avoid errors on certain compilers. -(Thanks to ramaprasad.kr@hp.com for reporting this.) +Revision 1.165 2004/04/22 03:05:28 cheshire +kDNSClass_ANY should be kDNSQClass_ANY + +Revision 1.164 2004/04/21 02:55:03 cheshire +Update comments describing 'InterfaceActive' field + +Revision 1.163 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.162 2004/04/15 00:51:28 bradley +Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. +Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. + +Revision 1.161 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.160 2004/04/09 17:40:26 cheshire +Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field + +Revision 1.159 2004/04/09 16:37:15 cheshire +Suggestion from Bob Bradley: +Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers + +Revision 1.158 2004/04/02 19:38:33 cheshire +Update comment about typical RR TTLs + +Revision 1.157 2004/04/02 19:35:53 cheshire +Add clarifying comments about legal mDNSInterfaceID values + +Revision 1.156 2004/04/02 19:19:48 cheshire +Add code to do optional logging of multi-packet KA list time intervals + +Revision 1.155 2004/03/24 00:29:45 ksekar +Make it safe to call StopQuery in a unicast question callback + +Revision 1.154 2004/03/20 01:05:49 cheshire +Test __LP64__ and __ILP64__ to compile properly on a wider range of 64-bit architectures + +Revision 1.153 2004/03/13 01:57:33 ksekar +: DynDNS: Dynamic update of service records -Revision 1.114.2.7 2004/03/09 02:31:27 cheshire +Revision 1.152 2004/03/09 02:27:16 cheshire Remove erroneous underscore in 'packed_struct' (makes no difference now, but might in future) -Revision 1.114.2.6 2004/03/02 02:55:25 cheshire +Revision 1.151 2004/03/02 03:21:56 cheshire Properly support "_services._dns-sd._udp" meta-queries -Revision 1.114.2.5 2004/02/18 23:35:17 cheshire -: Hard code domain enumeration functions to return ".local" only -Also make mDNS_StopGetDomains() a no-op too, so that we don't get warning messages in syslog +Revision 1.150 2004/02/21 02:06:24 cheshire +Can't use anonymous unions -- they're non-standard and don't work on all compilers + +Revision 1.149 2004/02/06 23:04:19 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.148 2004/02/03 19:47:36 ksekar +Added an asyncronous state machine mechanism to uDNS.c, including +calls to find the parent zone for a domain name. Changes include code +in repository previously dissabled via "#if 0 //incomplete". Codepath +is currently unused, and will be called to create update records, etc. + +Revision 1.147 2004/02/03 18:57:35 cheshire +Update comment for "IsLocalDomain()" + +Revision 1.146 2004/01/30 02:20:24 bradley +Map inline to __inline when building with Microsoft C compilers since they do not support C99 inline. + +Revision 1.145 2004/01/29 02:59:17 ksekar +Unicast DNS: Changed from a resource record oriented question/response +matching to packet based matching. New callback architecture allows +collections of records in a response to be processed differently +depending on the nature of the request, and allows the same structure +to be used for internal and client-driven queries with different processing needs. + +Revision 1.144 2004/01/28 20:20:45 ksekar +Unified ActiveQueries and ActiveInternalQueries lists, using a flag to +demux them. Check-in includes work-in-progress code, #ifdef'd out. + +Revision 1.143 2004/01/28 03:41:00 cheshire +: Need ability to do targeted queries as well as multicast queries + +Revision 1.142 2004/01/28 02:30:07 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.141 2004/01/27 20:15:22 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.140 2004/01/24 23:37:08 cheshire +At Kiren's suggestion, made functions to convert mDNSOpaque16s to/from integer values + +Revision 1.139 2004/01/24 08:46:26 bradley +Added InterfaceID<->Index platform interfaces since they are now used by all platforms for the DNS-SD APIs. + +Revision 1.138 2004/01/24 04:59:15 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.137 2004/01/24 03:40:56 cheshire +Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSClientAPI.h so clients can use it + +Revision 1.136 2004/01/24 03:38:27 cheshire +Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport" + +Revision 1.135 2004/01/23 23:23:15 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.134 2004/01/22 03:54:11 cheshire +Create special meta-interface 'mDNSInterface_ForceMCast' (-2), +which means "do this query via multicast, even if it's apparently a unicast domain" + +Revision 1.133 2004/01/22 03:48:41 cheshire +Make sure uDNS client doesn't accidentally use query ID zero + +Revision 1.132 2004/01/22 03:43:08 cheshire +Export constants like mDNSInterface_LocalOnly so that the client layers can use them + +Revision 1.131 2004/01/21 21:53:18 cheshire +: Don't try to receive unicast responses if we're not the first to bind to the UDP port + +Revision 1.130 2003/12/14 05:05:29 cheshire +Add comments explaining mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize + +Revision 1.129 2003/12/13 03:05:27 ksekar +: DynDNS: Unicast query of service records + +Revision 1.128 2003/12/01 21:44:23 cheshire +Add mStatus_BadInterfaceErr = -65552 for consistency with dns_sd.h + +Revision 1.127 2003/12/01 18:26:37 cheshire +Also pack the OpaqueXX union types. Otherwise, on some systems, mDNSOpaque16 is four bytes! + +Revision 1.126 2003/12/01 18:23:48 cheshire +: Scalar size problem in mDNS code on some 64-bit architectures + +Revision 1.125 2003/11/22 00:18:27 cheshire +Add compile-time asserts to verify correct sizes of mDNSu32, mDNSOpaque16, etc. -Revision 1.114.2.4 2004/01/28 23:29:20 cheshire -Fix structure packing (only affects third-party Darwin developers) +Revision 1.124 2003/11/20 22:59:54 cheshire +Changed runtime checks in mDNS.c to be compile-time checks in mDNSClientAPI.h +Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. -Revision 1.114.2.3 2003/12/05 00:03:34 cheshire - Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256 +Revision 1.123 2003/11/20 22:53:01 cheshire +Add comment about MAX_ESCAPED_DOMAIN_LABEL -Revision 1.114.2.2 2003/12/04 23:30:00 cheshire -Add "#define MAX_ESCAPED_DOMAIN_NAME 1005", needed for Posix folder to build +Revision 1.122 2003/11/20 20:49:53 cheshire +Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures -Revision 1.114.2.1 2003/12/03 11:07:58 cheshire -: Stop and start of a service uses old ip address (with old port number) +Revision 1.121 2003/11/20 05:01:38 cheshire +Update comments; add explanation of Advertise/DontAdvertiseLocalAddresses + +Revision 1.120 2003/11/14 20:59:08 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.119 2003/11/14 19:47:52 cheshire +Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString + +Revision 1.118 2003/11/14 19:18:34 cheshire +Move AssignDomainName macro to mDNSClientAPI.h to that client layers can use it too + +Revision 1.117 2003/11/08 23:32:24 cheshire +Gave name to anonymous struct, to avoid errors on certain compilers. +(Thanks to ramaprasad.kr@hp.com for reporting this.) + +Revision 1.116 2003/11/07 03:32:56 cheshire + mDNSResponder delivers answers in inconsistent order +This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes +purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is +to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR() +can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place. + +Revision 1.115 2003/09/23 00:53:54 cheshire +NumFailedProbes should be unsigned Revision 1.114 2003/08/29 19:44:15 cheshire Traffic reduction: Eliminate synchronized QUs when a new service appears @@ -166,7 +349,7 @@ 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 #: : Feature: New Rendezvous APIs (#7875) (mDNSResponder component) +: 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 @@ -323,12 +506,11 @@ Revision 1.43 2003/04/25 01:45:56 cheshire Revision 1.42 2003/04/15 20:58:31 jgraessl -Bug #: 3229014 -Added a hash to lookup records in the cache. + 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. @@ -353,25 +535,25 @@ Revision 1.37 2003/03/14 21:34:11 cheshire 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! + 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 + 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 + 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 + 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 + 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 @@ -386,7 +568,7 @@ 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 + mDNSResponder sometimes fails to find the correct results Add 'Active' flag for interfaces Revision 1.28 2003/01/28 01:35:56 cheshire @@ -394,9 +576,9 @@ 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 + computer name changes not handled properly + service name changes are not properly handled + announcements sent in pairs, failing chattiness test Revision 1.26 2002/12/23 22:13:28 jgraessl @@ -407,7 +589,7 @@ 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-existence +Added mDNS_RegisterNoSuchService() function for assertion of non-existance of a particular named service Revision 1.23 2002/09/19 21:25:34 cheshire @@ -430,7 +612,7 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release #include // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration #include "mDNSDebug.h" -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -506,7 +688,9 @@ typedef enum // From RFC 1035 kDNSType_TXT, // 16 Arbitrary text string kDNSType_AAAA = 28, // 28 IPv6 address - kDNSType_SRV = 33, // 33 Service record + kDNSType_SRV = 33, // 33 Service record + kDNSType_OPT = 41, // EDNS0 OPT record + kDNSType_TSIG = 250, // 250 Transaction Signature kDNSQType_ANY = 255 // Not a DNS type, but a DNS query type, meaning "all types" } DNS_TypeValues; @@ -523,9 +707,21 @@ typedef signed char mDNSs8; typedef unsigned char mDNSu8; typedef signed short mDNSs16; typedef unsigned short mDNSu16; -#if _LP64 + +// says +// __LP64__ _LP64 +// These macros are defined, with value 1, if (and only if) the compilation is +// for a target where long int and pointer both use 64-bits and int uses 32-bit. +// says +// Macro Name __LP64__ Value 1 +// A quick Google search for "defined(__LP64__)" OR "#ifdef __LP64__" gives 2590 hits and +// a search for "#if __LP64__" gives only 12, so I think we'll go with the majority and use defined() +#if defined(_LP64) || defined(__LP64__) typedef signed int mDNSs32; typedef unsigned int mDNSu32; +#elif defined(_ILP64) || defined(__ILP64__) +typedef signed int32 mDNSs32; +typedef unsigned int32 mDNSu32; #else typedef signed long mDNSs32; typedef unsigned long mDNSu32; @@ -548,7 +744,7 @@ typedef packedunion { mDNSu8 b[4]; mDNSu32 NotAnInteger; } mDNSOpaque32; typedef packedunion { 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 mDNSv4Addr; // An IP address is a four-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 @@ -595,13 +791,20 @@ enum // = -65550, mStatus_Incompatible = -65551, mStatus_BadInterfaceErr = -65552, - - // -65553 - -65789 currently unused + mStatus_Refused = -65553, + mStatus_NoSuchRecord = -65554, + mStatus_NoAuth = -65555, + // -65556 - -65789 currently unused // Non-error values: mStatus_GrowCache = -65790, mStatus_ConfigChanged = -65791, - mStatus_MemFree = -65792 // 0xFFFE FF00 + mStatus_MemFree = -65792, // 0xFFFE FF00 + + // tcp connection status + mStatus_ConnectionPending = -65793, + mStatus_ConnectionFailed = -65794, + mStatus_ConnectionEstablished = -65795 }; typedef mDNSs32 mStatus; @@ -631,6 +834,36 @@ typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string #define MAX_ESCAPED_DOMAIN_LABEL 254 #define MAX_ESCAPED_DOMAIN_NAME 1005 +// *************************************************************************** +#if 0 +#pragma mark - DNS Message structures +#endif + +#define mDNS_numZones numQuestions +#define mDNS_numPrereqs numAnswers +#define mDNS_numUpdates numAuthorities + +typedef packedstruct + { + mDNSOpaque16 id; + mDNSOpaque16 flags; + mDNSu16 numQuestions; + mDNSu16 numAnswers; + mDNSu16 numAuthorities; + mDNSu16 numAdditionals; + } DNSMessageHeader; + +// 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 +// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total +#define AbsoluteMaxDNSMessageData 8940 +#define NormalMaxDNSMessageData 1440 +typedef packedstruct + { + DNSMessageHeader h; // Note: Size 12 bytes + mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 + } DNSMessage; + // *************************************************************************** #if 0 #pragma mark - Resource Record structures @@ -653,7 +886,7 @@ typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string // -- 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 // -- 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) +// -- These RRs typically have low TTLs (e.g. a few minutes) // -- 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 @@ -698,9 +931,10 @@ enum 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 - + // For Dynamic Update records, Known Unique means the record must already exist on the server. kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), @@ -713,8 +947,27 @@ enum kDNSRecordTypePacketUniqueMask = 0x20 // True for PacketAddUnique and PacketAnsUnique }; -typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; -typedef packedstruct { mDNSu16 preference; domainname exchange; } rdataMX; +typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; +typedef packedstruct { mDNSu16 preference; domainname exchange; } rdataMX; +typedef packedstruct { domainname mname; domainname rname; mDNSOpaque32 serial; mDNSOpaque32 refresh; + mDNSOpaque32 retry; mDNSOpaque32 expire; mDNSOpaque32 min; } rdataSOA; + +typedef packedstruct + { + mDNSu16 vers; + mDNSu16 llqOp; + mDNSu16 err; + mDNSu8 id[8]; + mDNSu32 lease; + } LLQOptData; + +// NOTE: rdataOpt format may be repeated an arbitrary number of times in a single resource record +typedef packedstruct + { + mDNSu16 opt; + mDNSu16 optlen; + union { LLQOptData llq; mDNSu32 lease; } OptData; + } rdataOpt; // 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) @@ -738,10 +991,12 @@ typedef union mDNSu8 data[StandardAuthRDSize]; mDNSv4Addr ip; // For 'A' record mDNSv6Addr ipv6; // For 'AAAA' record - domainname name; // For PTR and CNAME records + domainname name; // For PTR, NS, and CNAME records UTF8str255 txt; // For TXT record rdataSRV srv; // For SRV record rdataMX mx; // For MX record + rdataSOA soa; // For SOA record + rdataOpt opt; // For eDNS0 opt record } RDataBody; typedef struct @@ -758,7 +1013,7 @@ typedef struct DNSQuestion_struct DNSQuestion; typedef struct mDNS_struct mDNS; typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; -// Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +// 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: @@ -774,7 +1029,7 @@ struct ResourceRecord_struct // 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; + domainname name; mDNSu16 rrtype; mDNSu16 rrclass; mDNSu32 rroriginalttl; // In seconds @@ -786,6 +1041,34 @@ struct ResourceRecord_struct RData *rdata; // Pointer to storage for this rdata }; +enum + { + regState_FetchingZoneData = 1, // getting info - update not sent + regState_Pending = 2, // update sent, reply not received + regState_Registered = 3, // update sent, reply received + regState_DeregPending = 4, // dereg sent, reply not received + regState_DeregDeferred = 5, // dereg requested while in Pending state - send dereg AFTER registration is confirmed + regState_Cancelled = 6, // update not sent, reg. cancelled by client + regState_TargetChange = 7, // host name change update pending + regState_Unregistered = 8, // not in any list + regState_Refresh = 9 // outstanding refresh message + }; + +typedef mDNSu16 regState_t; + +typedef struct + { + regState_t state; + mDNSOpaque16 id; + domainname zone; // the zone that is updated + mDNSAddr ns; // primary name server for the record's zone !!!KRS not technically correct to cache longer than TTL + mDNSIPPort port; // port on which server accepts dynamic updates + mDNSBool add; // !!!KRS this should really be an enumerated state + struct uDNS_AuthInfo *AuthInfo; // authentication info (may be null) + mDNSBool lease; // dynamic update contains (should contain) lease option + mDNSs32 expire; // expiration of lease (-1 for static) + } uDNS_RegInfo; + struct AuthRecord_struct { // For examples of how to set up this structure for use in mDNS_Register(), @@ -795,8 +1078,9 @@ struct AuthRecord_struct AuthRecord *next; // Next in list; first element of structure for efficiency reasons ResourceRecord resrec; + uDNS_RegInfo uDNS_info; - // Persistent metadata for Authoritative Records + // 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 @@ -811,6 +1095,9 @@ struct AuthRecord_struct 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) +#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES + mDNSs32 ImmedAnswerMarkTime; +#endif 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 @@ -867,16 +1154,28 @@ typedef struct typedef struct NetworkInterfaceInfo_struct NetworkInterfaceInfo; +typedef struct + { + AuthRecord RR_A; + mDNSBool registered; // True if a name for the interface is globally registered + domainname regname; // the name registered to the update server + } uDNS_NetworkInterfaceInfo; + +// A NetworkInterfaceInfo_struct serves two purposes: +// 1. It holds the address, PTR and HINFO records to advertise a given IP address on a given physical interface +// 2. It tells mDNSCore which physical interfaces are available; each physical interface has its own unique InterfaceID. +// Since there may be multiple IP addresses on a single physical interface, +// there may be multiple NetworkInterfaceInfo_structs with the same InterfaceID. +// In this case, to avoid sending the same packet n times, when there's more than one +// struct with the same InterfaceID, mDNSCore picks one member of the set to be the +// active representative of the set; all others have the 'InterfaceActive' flag unset. + struct NetworkInterfaceInfo_struct { // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. NetworkInterfaceInfo *next; - 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 InterfaceActive; // Set if interface is sending & receiving packets (see comment above) mDNSBool IPv4Available; // If InterfaceActive, set if v4 available on this InterfaceID mDNSBool IPv6Available; // If InterfaceActive, set if v6 available on this InterfaceID @@ -885,13 +1184,13 @@ struct NetworkInterfaceInfo_struct AuthRecord RR_PTR; // PTR (reverse lookup) record AuthRecord RR_HINFO; + uDNS_NetworkInterfaceInfo uDNS_info; + // Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface() - mDNSInterfaceID InterfaceID; - mDNSAddr ip; - mDNSAddr mask; - char ifname[16]; - 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 + mDNSInterfaceID InterfaceID; // Identifies physical interface; MUST NOT be 0, -1, or -2 + mDNSAddr ip; // The IPv4 or IPv6 address to advertise + mDNSBool Advertise; // False if you are only searching on this interface + mDNSBool McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ? }; typedef struct ExtraResourceRecord_struct ExtraResourceRecord; @@ -904,7 +1203,7 @@ struct ExtraResourceRecord_struct // 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() +// 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 @@ -912,7 +1211,9 @@ struct ServiceRecordSet_struct // 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; + ServiceRecordSet *next; + uDNS_RegInfo uDNS_info; + mDNSServiceCallback *ServiceCallback; void *ServiceContext; ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration mDNSu32 NumSubTypes; @@ -945,7 +1246,78 @@ typedef struct mDNSs32 Type; // v4 or v6? } DupSuppressInfo; -// Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() +typedef enum + { + LLQ_UnInit = 0, + LLQ_GetZoneInfo = 1, + LLQ_InitialRequest = 2, + LLQ_SecondaryRequest = 3, + LLQ_Established = 4, + LLQ_Refresh = 5, + LLQ_Retry = 6, + LLQ_Suspended = 7, + // safe to re-start LLQ before this point + LLQ_Static = 16, + LLQ_Poll = 17, + LLQ_Error = 18, + LLQ_Cancelled = 19 + } LLQ_State; + +typedef struct + { + LLQ_State state; + mDNSAddr servAddr; + mDNSIPPort servPort; + DNSQuestion *question; + mDNSu32 origLease; // seconds (relative) + mDNSs32 retry; // ticks (absolute) + mDNSs32 expire; // ticks (absolute) + mDNSs16 ntries; + mDNSu8 id[8]; + mDNSBool deriveRemovesOnResume; + } LLQ_Info; + +// LLQ constants +#define kDNSOpt_LLQ 1 //!!!KRS +#define kDNSOpt_Lease 2 //!!!KRS +#define kLLQ_Vers 0 // prerelease +#define kLLQ_DefLease 7200 // 2 hours +#define kUpdate_DefLease 7200 +#define kLLQ_MAX_TRIES 3 // retry an operation 3 times max +#define kLLQ_INIT_RESEND 2 // resend an un-ack'd packet after 2 seconds, then double for each additional +#define kLLQ_DEF_RETRY 1800 // retry a failed operation after 30 minutes +// LLQ Operation Codes +#define kLLQ_Setup 1 +#define kLLQ_Refresh 2 + +#define LLQ_OPT_SIZE (2 * sizeof(mDNSu16)) + sizeof(LLQOptData) +#define LEASE_OPT_SIZE (2 * sizeof(mDNSu16)) + sizeof(mDNSs32) + +// LLQ Errror Codes +enum + { + LLQErr_NoError = 0, + LLQErr_ServFull = 1, + LLQErr_Static = 2, + LLQErr_FormErr = 3, + LLQErr_NoSuchLLQ = 4, + LLQErr_BadVers = 5, + LLQErr_UnknownErr = 6 + }; + +typedef void (*InternalResponseHndlr)(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *internalContext); +typedef struct + { + mDNSOpaque16 id; + mDNSs32 timestamp; + mDNSBool internal; + InternalResponseHndlr responseCallback; // NULL if internal field is false + LLQ_Info *llq; // NULL for 1-shot queries + CacheRecord *knownAnswers; + void *context; + } uDNS_QuestionInfo; + +// 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 { @@ -967,14 +1339,20 @@ struct DNSQuestion_struct 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 + uDNS_QuestionInfo uDNS_info; // 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 + mDNSInterfaceID InterfaceID; // Non-zero if you want to issue queries only on a single specific IP interface + mDNSAddr Target; // Non-zero if you want to direct queries to a specific unicast target address + mDNSIPPort TargetPort; // Must be set if Target is set + mDNSOpaque16 TargetQID; // Must be set if Target is set domainname qname; mDNSu16 qtype; mDNSu16 qclass; mDNSQuestionCallback *QuestionCallback; void *QuestionContext; + mDNSBool LongLived; // Set by client for calls to mDNS_StartQuery to indicate LLQs to unicast layer. + // Set by mDNS.c in mDNS_StartBrowse. }; typedef struct @@ -989,7 +1367,7 @@ typedef struct 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() +// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute() typedef struct ServiceInfoQuery_struct ServiceInfoQuery; typedef void mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query); struct ServiceInfoQuery_struct @@ -1027,6 +1405,29 @@ enum mDNS_KnownBug_PhantomInterfaces = 1 }; +typedef struct + { + mDNSs32 nextevent; + DNSQuestion *ActiveQueries; //!!!KRS this should be a hashtable (hash on messageID) + DNSQuestion *CurrentQuery; // pointer to ActiveQueries list being examined in a loop. Functions that remove + // elements from the ActiveQueries list must update this pointer (if non-NULL) as necessary. + //!!!KRS do the same for registration lists + ServiceRecordSet *ServiceRegistrations; + AuthRecord *RecordRegistrations; + mDNSu16 NextMessageID; + mDNSAddr Servers[32]; //!!!KRS this should be a dynamically allocated linked list + domainname hostname; // global name for dynamic registration of address records + char NameRegDomain[MAX_ESCAPED_DOMAIN_NAME]; + // domain in which above hostname is registered + // currently set by the platform layer at startup + // do not set if services / address records are not to be globally registered to an update server + // !!!KRS this must go away once we can learn the reg domain from the network or prefs + char ServiceRegDomain[MAX_ESCAPED_DOMAIN_NAME]; + // if set, all services that don't explicitly specify a domain upon registration will be + // registered in this domain. if not set, .local will be used by default + struct uDNS_AuthInfo *AuthInfoList; // list of domains required authentication for updates. !!!KRS this shoudl be a hashtable + } uDNS_GlobalInfo; + struct mDNS_struct { // Internal state fields. These hold the main internal state of mDNSCore; @@ -1036,6 +1437,7 @@ struct mDNS_struct mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size mDNSu32 KnownBugs; + mDNSBool CanReceiveUnicast; mDNSBool AdvertiseLocalAddresses; mStatus mDNSPlatformStatus; mDNSCallback *MainCallback; @@ -1070,10 +1472,10 @@ struct mDNS_struct 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 *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly 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_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; CacheRecord *rrcache_free; @@ -1085,18 +1487,21 @@ struct mDNS_struct 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 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 *LocalOnlyRecords; // Local records registered with InterfaceID set to mDNSInterface_LocalOnly 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 ProbeFailTime; - mDNSs32 NumFailedProbes; + mDNSu32 NumFailedProbes; mDNSs32 SuppressProbes; + + // unicast-specific data + uDNS_GlobalInfo uDNS_info; }; // *************************************************************************** @@ -1109,7 +1514,11 @@ extern const mDNSv4Addr zeroIPAddr; extern const mDNSv6Addr zerov6Addr; extern const mDNSv4Addr onesIPv4Addr; extern const mDNSv6Addr onesIPv6Addr; -extern const mDNSInterfaceID mDNSInterface_Any; +extern const mDNSAddr zeroAddr; + +extern const mDNSInterfaceID mDNSInterface_Any; // Zero +extern const mDNSInterfaceID mDNSInterface_LocalOnly; // (mDNSInterfaceID)-1; +extern const mDNSInterfaceID mDNSInterface_ForceMCast; // (mDNSInterfaceID)-2; extern const mDNSIPPort UnicastDNSPort; extern const mDNSIPPort MulticastDNSPort; @@ -1119,14 +1528,79 @@ extern const mDNSv6Addr AllDNSLinkGroupv6; extern const mDNSAddr AllDNSLinkGroup_v4; extern const mDNSAddr AllDNSLinkGroup_v6; +extern const mDNSOpaque16 zeroID; +extern const mDNSOpaque16 QueryFlags; +extern const mDNSOpaque16 ResponseFlags; +extern const mDNSOpaque16 UpdateReqFlags; +extern const mDNSOpaque16 UpdateRespFlags; + +// *************************************************************************** +#if 0 +#pragma mark - Inline functions +#endif + +#if (defined(_MSC_VER)) + #define mDNSinline static __inline +#elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) + #define mDNSinline static inline +#endif + +// If we're not doing inline functions, then this header needs to have the extern declarations +#if !defined(mDNSinline) +extern mDNSu16 mDNSVal16(mDNSOpaque16 x); +extern mDNSu32 mDNSVal32(mDNSOpaque32 x); +extern mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v); +extern mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v); +#endif + +// If we're compiling the particular C file that instantiates our inlines, then we +// define "mDNSinline" (to empty string) so that we generate code in the following section +#if (!defined(mDNSinline) && mDNS_InstantiateInlines) +#define mDNSinline +#endif + +#ifdef mDNSinline + +mDNSinline mDNSu16 mDNSVal16(mDNSOpaque16 x) { return((mDNSu16)((mDNSu16)x.b[0] << 8 | (mDNSu16)x.b[1])); } +mDNSinline mDNSu32 mDNSVal32(mDNSOpaque32 x) { return((mDNSu32)((mDNSu32)x.b[0] << 24 | (mDNSu32)x.b[1] << 16 | (mDNSu32)x.b[2] << 8 | (mDNSu32)x.b[3])); } + +mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v) + { + mDNSOpaque16 x; + x.b[0] = (mDNSu8)(v >> 8); + x.b[1] = (mDNSu8)(v & 0xFF); + return(x); + } + +mDNSinline mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v) + { + mDNSOpaque32 x; + x.b[0] = (mDNSu8) (v >> 24) ; + x.b[1] = (mDNSu8)((v >> 16) & 0xFF); + x.b[2] = (mDNSu8)((v >> 8 ) & 0xFF); + x.b[3] = (mDNSu8)((v ) & 0xFF); + return x; + } + +#endif + // *************************************************************************** #if 0 #pragma mark - Main Client Functions #endif -// 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. -// Most clients use mDNS_Init_AdvertiseLocalAddresses. This causes mDNSCore to automatically +// Every client should call mDNS_Init, passing in storage for the mDNS object and the mDNS_PlatformSupport object. +// +// Clients that are only advertising services should use mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize. +// Clients that plan to perform queries (mDNS_StartQuery, mDNS_StartBrowse, mDNS_StartResolveService, etc.) +// need to provide storage for the resource record cache, or the query calls will return 'mStatus_NoCache'. +// The rrcachestorage parameter is the address of memory for the resource record cache, and +// the rrcachesize parameter is the number of entries in the CacheRecord array passed in. +// (i.e. the size of the cache memory needs to be sizeof(CacheRecord) * rrcachesize). +// OS X 10.3 Panther uses an initial cache size of 64 entries, and then mDNSCore sends an +// mStatus_GrowCache message if it needs more. +// +// Most clients should use mDNS_Init_AdvertiseLocalAddresses. This causes mDNSCore to automatically // create the correct address records for all the hosts interfaces. If you plan to advertise // services being offered by the local machine, this is almost always what you want. // There are two cases where you might use mDNS_Init_DontAdvertiseLocalAddresses: @@ -1176,7 +1650,7 @@ 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, + const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback); extern mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr); @@ -1250,11 +1724,9 @@ typedef enum mDNS_DomainTypeRegistrationDefault = 3 } mDNS_DomainType; -extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); -// In the Panther mDNSResponder we don't do unicast queries yet, so there's no point trying to do domain enumeration -// mDNS_GetDomains() and mDNS_StopGetDomains() are set to be no-ops so that clients don't try to do browse/register operations that will fail -//#define mDNS_StopGetDomains mDNS_StopQuery -#define mDNS_StopGetDomains(m,q) ((void)(m),(void)(q)) +extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, + const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); +#define mDNS_StopGetDomains mDNS_StopQuery extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname); #define mDNS_StopAdvertiseDomains mDNS_Deregister @@ -1277,6 +1749,7 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainT // Comparison functions extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b); extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2); +extern mDNSBool IsLocalDomain(const domainname *d); // returns true for domains that by default should be looked up using link-local multicast // 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) @@ -1337,13 +1810,77 @@ extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel 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 mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id); 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 mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); +// *************************************************************************** +#if 0 +#pragma mark - Authentication Support +#endif + +#define HMAC_LEN 64 +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c +#define MD5_LEN 16 + +// padded keys for inned/outer hash rounds +typedef struct + { + mDNSu8 ipad[HMAC_LEN]; + mDNSu8 opad[HMAC_LEN]; + } HMAC_Key; + +// Internal data structure to maintain authentication information for an update domain +typedef struct uDNS_AuthInfo + { + domainname zone; + domainname keyname; + HMAC_Key key; + struct uDNS_AuthInfo *next; + } uDNS_AuthInfo; + +// Platform Support for computing MD5 +// mDNSPlatformUTC returns the time, in seconds, since Jan 1st 1970 UTC and is required for generating TSIG records + +extern mDNSs32 mDNSPlatformUTC(void); + +// Client Calls +// mDNS_UpdateDomainRequiresAuthentication tells the core to authenticate (via TSIG with an HMAC_MD5 hash) +// when dynamically updating a given zone (and its subdomains). The key used in authentication must be in +// domain name format. The shared secret must be a base64 encoded string with the base64 parameter set to +// true, or binary data with the base64 parameter set to false. The length is the size of the secret in +// bytes. (A minimum size of 16 bytes (128 bits) is recommended for an MD5 hash as per RFC 2485). +// The This routine is normally called once for each secure domain at startup, though it can be called at any time. + +// mDNS_ClearAuthenticationList clears from the core's internal structures all domains previously passed to +// mDNS_UpdateDomainRequiresAuthentication. + +extern mStatus mDNS_UpdateDomainRequiresAuthentication(mDNS *m, domainname *zone, domainname *key, + mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64); + +extern void mDNS_ClearAuthenticationList(mDNS *m); + +// Routines called by the core, exported by DNSDigest.c + +// Convert a base64 encoded key into a binary byte stream +extern mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize); + +// Convert an arbitrary binary key (of any length) into an HMAC key (stored in AuthInfo struct) +extern void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, mDNSu8 *key, mDNSu32 len); + +// sign a DNS message. The message must be compete, with all values in network byte order. end points to the end +// of the message, and is modified by this routine. numAdditionals is a pointer to the number of additional +// records in HOST byte order, which is incremented upon successful completion of this routine. The function returns +// the new end pointer on success, and NULL on failure. +extern mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info); + +// MD5 hash function used by the core for signing TSIG records (impemented in DNSDigest.c) +extern mStatus DNSDigest_MD5(const DNSMessage *msg, mDNSu32 msglen, mDNSOpaque16 *digest); + // *************************************************************************** #if 0 #pragma mark - PlatformSupport interface @@ -1354,34 +1891,13 @@ extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); // The definitions are placed here because sometimes clients do use these calls indirectly, via other supported client operations. // For example, AssignDomainName is a macro defined using mDNSPlatformMemCopy() -typedef packedstruct - { - mDNSOpaque16 id; - mDNSOpaque16 flags; - mDNSu16 numQuestions; - mDNSu16 numAnswers; - mDNSu16 numAuthorities; - mDNSu16 numAdditionals; - } DNSMessageHeader; - -// 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 -// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total -#define AbsoluteMaxDNSMessageData 8940 -#define NormalMaxDNSMessageData 1440 -typedef packedstruct - { - DNSMessageHeader h; // Note: Size 12 bytes - mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 - } DNSMessage; - // Every platform support module must provide the following functions. // mDNSPlatformInit() typically opens a communication endpoint, and starts listening for mDNS packets. // When Setup is complete, the platform support layer calls mDNSCoreInitComplete(). // mDNSPlatformSendUDP() sends one UDP packet // When a packet is received, the PlatformSupport code calls mDNSCoreReceive() // mDNSPlatformClose() tidies up on exit -// Note: mDNSPlatformMemAllocate/mDNSPlatformMemFree are only required for handling oversized resource records. +// Note: mDNSPlatformMemAllocate/mDNSPlatformMemFree are only required for handling oversized resource records and unicast DNS. // 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 @@ -1390,7 +1906,7 @@ typedef packedstruct 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, - mDNSInterfaceID InterfaceID, mDNSIPPort srcport, const mDNSAddr *dst, mDNSIPPort dstport); +mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport); extern void mDNSPlatformLock (const mDNS *const m); extern void mDNSPlatformUnlock (const mDNS *const m); @@ -1404,11 +1920,65 @@ extern void * mDNSPlatformMemAllocate (mDNSu32 len); extern void mDNSPlatformMemFree (void *mem); extern mStatus mDNSPlatformTimeInit (mDNSs32 *timenow); +// Platform support modules should provide the following functions to map between opaque interface IDs +// and interface indexes in order to support the DNS-SD API. If your target platform does not support +// multiple interfaces and/or does not support the DNS-SD API, these functions can be empty. +extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index); +extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id); + +// Every platform support module must provide the following functions if it is to support unicast DNS +// and Dynamic Update. +// All TCP socket operations implemented by the platform layer MUST NOT BLOCK. +// mDNSPlatformTCPConnect initiates a TCP connection with a peer, adding the socket descriptor to the +// main event loop. The return value indicates whether the connection succeeded, failed, or is pending +// (i.e. the call would block.) On return, the descriptor parameter is set to point to the connected socket. +// The TCPConnectionCallback is subsequently invoked when the connection +// completes (in which case the ConnectionEstablished parameter is true), or data is available for +// reading on the socket (indicated by the ConnectionEstablished parameter being false.) If the connection +// asyncronously fails, the TCPConnectionCallback should be invoked as usual, with the error being +// returned in subsequent calls to PlatformReadTCP or PlatformWriteTCP. (This allows for platforms +// with limited asyncronous error detection capabilities.) PlatformReadTCP and PlatformWriteTCP must +// return the number of bytes read/written, 0 if the call would block, and -1 if an error. +// PlatformTCPCloseConnection must close the connection to the peer and remove the descriptor from the +// event loop. CloseConnectin may be called at any time, including in a ConnectionCallback. + +typedef void (*TCPConnectionCallback)(int sd, void *context, mDNSBool ConnectionEstablished); +extern mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context, int *descriptor); +extern void mDNSPlatformTCPCloseConnection(int sd); +extern int mDNSPlatformReadTCP(int sd, void *buf, int buflen); +extern int mDNSPlatformWriteTCP(int sd, const char *msg, int len); + +// Platforms that support unicast browsing and dynamic update registration for clients who do not specify a domain +// in browse/registration calls must implement these routines to get the "default" browse/registration list. +// The Get() functions must return a linked list of DNameListElem structs, allocated via mDNSPlatformMemAllocate. +// Platforms may implement the Get() calls via the mDNS_CopyDNameList() helper routine. +// Callers should free lists obtained via the Get() calls with th mDNS_FreeDNameList routine, provided by the core. + +typedef struct DNameListElem + { + domainname name; + struct DNameListElem *next; + } DNameListElem; + +extern DNameListElem *mDNSPlatformGetSearchDomainList(void); +extern DNameListElem *mDNSPlatformGetRegDomainList(void); + +// Helper functions provided by the core +extern DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig); +extern void mDNS_FreeDNameList(DNameListElem *list); + // 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 change of the dot-local host name. // +// !!!KRS +// mDNS_GenerateGlobalFQDN() is called to register a domain name via Dynamic DNS update. It should be +// called on startup (after acquiring an IP address and DNS server) and on each change to the machine name +// or registration domain. The domain parameter is the domain in which the address record is to be registered, +// e.g. "mycompany.com". The full name is formed by appending this domain to the machine's host label. +// // 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 @@ -1418,6 +1988,16 @@ extern mStatus mDNSPlatformTimeInit (mDNSs32 *timenow); // -- Name-to-address records (A/AAAA) // -- Address-to-name records (PTR) // -- Host information (HINFO) +// IMPORTANT: The specified mDNSInterfaceID MUST NOT be 0, -1, or -2; these values have special meaning +// +// mDNS_SetDynamicRegistrationDomain is used to enable dynamic update registrations of address records +// in the specified domain. +// +// mDNS_RegisterDNS() is used by the platform support layer to provide the core with the addresses of +// available domain name servers for unicast queries/updates. RegisterDNS() should be called once for +// each name server, typically at startup, or when a new name server becomes available. DeregiterDNS() +// must be called whenever a registered name server becomes unavailable. DeregisterDNSList deregisters +// all registered servers. mDNS_DNSRegistered() returns true if one or more servers are registered in the core. // // mDNSCoreInitComplete() is called when the platform support layer is finished. // Typically this is at the end of mDNSPlatformInit(), but may be later @@ -1430,14 +2010,21 @@ extern mStatus mDNSPlatformTimeInit (mDNSs32 *timenow); // not lightweight second-by-second CPU power management modes.) extern void mDNS_GenerateFQDN(mDNS *const m); +extern void mDNS_GenerateGlobalFQDN(mDNS *const m); extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set); extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set); +extern void mDNS_RegisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr); +extern void mDNS_DeregisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr); +extern void mDNS_DeregisterDNSList(mDNS *const m); +extern mDNSBool mDNS_DNSRegistered(mDNS *const m); extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); extern 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); extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); +extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); + // *************************************************************************** #if 0 #pragma mark - Compile-Time assertion checks @@ -1469,7 +2056,7 @@ struct mDNS_CompileTimeAssertionChecks // *************************************************************************** -#ifdef __cplusplus +#ifdef __cplusplus } #endif diff --git a/mDNSCore/mDNSDebug.h b/mDNSCore/mDNSDebug.h index 65b6daf..bcb0851 100755 --- a/mDNSCore/mDNSDebug.h +++ b/mDNSCore/mDNSDebug.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,33 @@ Change History (most recent first): $Log: mDNSDebug.h,v $ +Revision 1.23 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.22 2004/04/22 04:27:42 cheshire +Spacing tidyup + +Revision 1.21 2004/04/14 23:21:41 ksekar +Removed accidental checkin of MALLOC_DEBUGING flag in 1.20 + +Revision 1.20 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.19 2004/03/15 18:57:59 cheshire +Undo last checkin that accidentally made verbose debugging the default for all targets + +Revision 1.18 2004/03/13 01:57:33 ksekar +: DynDNS: Dynamic update of service records + +Revision 1.17 2004/01/28 21:14:23 cheshire +Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode) + +Revision 1.16 2003/12/09 01:30:06 rpantos +Fix usage of ARGS... macros to build properly on Windows. + +Revision 1.15 2003/12/08 20:55:26 rpantos +Move some definitions here from mDNSMacOSX.h. + Revision 1.14 2003/08/12 19:56:24 cheshire Update to APSL 2.0 @@ -56,6 +85,8 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release // 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 +// (If you edit the file here to turn on MDNS_DEBUGMSGS while you're debugging some code, be careful +// not to accidentally check-in that change by mistake when you check in your other changes.) //#define MDNS_DEBUGMSGS 2 @@ -80,9 +111,9 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release #define debugf debugf_ 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__ ) ) + #if (defined(__GNUC__)) #define debugf( ARGS... ) ((void)0) - #elif( defined( __MWERKS__ ) ) + #elif (defined(__MWERKS__)) #define debugf( ... ) #else #define debugf 1 ? ((void)0) : (void) @@ -93,9 +124,9 @@ extern void debugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); #define verbosedebugf verbosedebugf_ extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); #else - #if( defined( __GNUC__ ) ) + #if (defined(__GNUC__)) #define verbosedebugf( ARGS... ) ((void)0) - #elif( defined( __MWERKS__ ) ) + #elif (defined(__MWERKS__)) #define verbosedebugf( ... ) #else #define verbosedebugf 1 ? ((void)0) : (void) @@ -103,7 +134,42 @@ extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1 #endif // LogMsg is used even in shipping code, to write truly serious error messages to syslog (or equivalent) +extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog extern void LogMsg(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +extern void LogMsgIdent(const char *ident, const char *format, ...); +extern void LogMsgNoIdent(const char *format, ...); + +// 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 + #if (defined( __GNUC__ )) + #define LogMalloc(ARGS...) ((void)0) + #elif (defined( __MWERKS__ )) + #define LogMalloc( ... ) + #else + #define LogMalloc 1 ? ((void)0) : (void) + #endif +#endif + +#define LogAllOperations 0 + +#if LogAllOperations +#define LogOperation LogMsg +#else +#define LogOperation debugf +#endif #ifdef __cplusplus } diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h deleted file mode 100755 index 3b53374..0000000 --- a/mDNSCore/mDNSEmbeddedAPI.h +++ /dev/null @@ -1,1470 +0,0 @@ -/* - * 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: mDNSEmbeddedAPI.h,v $ -Revision 1.114.2.9 2004/04/22 03:17:35 cheshire -Fix use of "struct __attribute__((__packed__))" so it only applies on GCC >= 2.9 - -Revision 1.114.2.8 2004/03/30 06:55:37 cheshire -Gave name to anonymous struct, to avoid errors on certain compilers. -(Thanks to ramaprasad.kr@hp.com for reporting this.) - -Revision 1.114.2.7 2004/03/09 02:31:27 cheshire -Remove erroneous underscore in 'packed_struct' (makes no difference now, but might in future) - -Revision 1.114.2.6 2004/03/02 02:55:25 cheshire - Properly support "_services._dns-sd._udp" meta-queries - -Revision 1.114.2.5 2004/02/18 23:35:17 cheshire -: Hard code domain enumeration functions to return ".local" only -Also make mDNS_StopGetDomains() a no-op too, so that we don't get warning messages in syslog - -Revision 1.114.2.4 2004/01/28 23:29:20 cheshire -Fix structure packing (only affects third-party Darwin developers) - -Revision 1.114.2.3 2003/12/05 00:03:34 cheshire - Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256 - -Revision 1.114.2.2 2003/12/04 23:30:00 cheshire -Add "#define MAX_ESCAPED_DOMAIN_NAME 1005", needed for Posix folder to build - -Revision 1.114.2.1 2003/12/03 11:07:58 cheshire -: Stop and start of a service uses old ip address (with old port number) - -Revision 1.114 2003/08/29 19:44:15 cheshire - Traffic reduction: Eliminate synchronized QUs when a new service appears -1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries - that already have at least one unique answer in the cache -2. For these queries, go straight to QM, skipping QU - -Revision 1.113 2003/08/21 19:31:58 cheshire -Cosmetic: Swap order of fields - -Revision 1.112 2003/08/21 19:27:36 cheshire - Traffic reduction: No need to announce record for longer than TTL - -Revision 1.111 2003/08/21 02:21:50 cheshire - Efficiency: Reduce repeated queries - -Revision 1.110 2003/08/20 23:39:31 cheshire - Review syslog messages, and remove as appropriate - -Revision 1.109 2003/08/19 22:24:10 cheshire -Comment change - -Revision 1.108 2003/08/19 22:20:00 cheshire - Don't use IPv6 on interfaces that have a routable IPv4 address configured -More minor refinements - -Revision 1.107 2003/08/19 06:48:25 cheshire - Guard against excessive record updates -Each record starts with 10 UpdateCredits. -Every update consumes one UpdateCredit. -UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10. -As the number of UpdateCredits declines, the number of announcements is similarly scaled back. -When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount. - -Revision 1.106 2003/08/19 04:49:28 cheshire - Interaction between v4, v6 and dual-stack hosts not working quite right -1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6. -2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface. -3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface. - -Revision 1.105 2003/08/19 02:33:37 cheshire -Update comments - -Revision 1.104 2003/08/19 02:31:11 cheshire - mDNSResponder overenthusiastic with final expiration queries -Final expiration queries now only mark the question for sending on the particular interface -pertaining to the record that's expiring. - -Revision 1.103 2003/08/18 19:05:44 cheshire - UpdateRecord not working right -Added "newrdlength" field to hold new length of updated rdata - -Revision 1.102 2003/08/16 03:39:00 cheshire - InterfaceID -1 indicates "local only" - -Revision 1.101 2003/08/15 20:16:02 cheshire - mDNSResponder takes too much RPRVT -We want to avoid touching the rdata pages, so we don't page them in. -1. RDLength was stored with the rdata, which meant touching the page just to find the length. - Moved this from the RData to the ResourceRecord object. -2. To avoid unnecessarily touching the rdata just to compare it, - compute a hash of the rdata and store the hash in the ResourceRecord object. - -Revision 1.100 2003/08/14 19:29:04 cheshire - Include cache records in SIGINFO output -Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them - -Revision 1.99 2003/08/14 02:17:05 cheshire - Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord - -Revision 1.98 2003/08/12 19:56:23 cheshire -Update to APSL 2.0 - -Revision 1.97 2003/08/12 14:59:27 cheshire - Rate-limiting blocks some legitimate responses -When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine -whether to suppress the response, also check LastMCInterface to see if it matches. - -Revision 1.96 2003/08/12 13:57:04 cheshire - Improve cache performance -Changed the number of hash table slots from 37 to 499 - -Revision 1.95 2003/08/09 00:55:02 cheshire - mDNSResponder is taking 20-30% of the CPU -Don't scan the whole cache after every packet. - -Revision 1.94 2003/08/09 00:35:29 cheshire - -Revision 1.93 2003/08/08 18:55:48 cheshire - Guard against time going backwards - -Revision 1.92 2003/08/08 18:36:04 cheshire - Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug - -Revision 1.91 2003/08/06 21:33:39 cheshire -Fix compiler warnings on PocketPC 2003 (Windows CE) - -Revision 1.90 2003/08/06 20:30:17 cheshire -Add structure definition for rdataMX (not currently used, but good to have it for completeness) - -Revision 1.89 2003/08/06 18:58:19 cheshire -Update comments - -Revision 1.88 2003/07/24 23:45:44 cheshire -To eliminate compiler warnings, changed definition of mDNSBool from -"unsigned char" to "int", since "int" is in fact truly the type that C uses -for the result of comparison operators (a: Feature: New Rendezvous APIs (#7875) (mDNSResponder component) -Added error type for incompatibility between daemon and client versions - -Revision 1.85 2003/07/19 03:23:13 cheshire - mDNSResponder needs to receive and cache larger records - -Revision 1.84 2003/07/18 23:52:12 cheshire -To improve consistency of field naming, global search-and-replace: -NextProbeTime -> NextScheduledProbe -NextResponseTime -> NextScheduledResponse - -Revision 1.83 2003/07/18 00:29:59 cheshire - Remove mDNSResponder version from packet header and use HINFO record instead - -Revision 1.82 2003/07/17 17:35:04 cheshire - Rate-limit responses, to guard against packet flooding - -Revision 1.81 2003/07/16 05:01:36 cheshire -Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for - Need to implement "unicast response" request, using top bit of qclass - -Revision 1.80 2003/07/15 01:55:12 cheshire - Need to implement service registration with subtypes - -Revision 1.79 2003/07/13 02:28:00 cheshire - SendResponses didn't all its responses -Delete all references to RRInterfaceActive -- it's now superfluous - -Revision 1.78 2003/07/13 01:47:53 cheshire -Fix one error and one warning in the Windows build - -Revision 1.77 2003/07/11 01:32:38 cheshire -Syntactic cleanup (no change to funcationality): Now that we only have one host name, -rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A". - -Revision 1.76 2003/07/11 01:28:00 cheshire - No more local.arpa - -Revision 1.75 2003/07/02 21:19:45 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.74 2003/07/02 02:41:23 cheshire - mDNSResponder needs to start with a smaller cache and then grow it as needed - -Revision 1.73 2003/06/10 04:24:39 cheshire - React when we observe other people query unsuccessfully for a record that's in our cache -Some additional refinements: -Don't try to do this for unicast-response queries -better tracking of Qs and KAs in multi-packet KA lists - -Revision 1.72 2003/06/10 01:46:27 cheshire -Add better comments explaining how these data structures are intended to be used from the client layer - -Revision 1.71 2003/06/07 06:45:05 cheshire - No need for multiple machines to all be sending the same queries - -Revision 1.70 2003/06/07 04:50:53 cheshire - React when we observe other people query unsuccessfully for a record that's in our cache - -Revision 1.69 2003/06/07 04:22:17 cheshire -Add MsgBuffer for error log and debug messages - -Revision 1.68 2003/06/07 01:46:38 cheshire - When query produces zero results, call mDNS_Reconfirm() on any antecedent records - -Revision 1.67 2003/06/07 01:22:14 cheshire - mDNSResponder needs an mDNS_Reconfirm() function - -Revision 1.66 2003/06/07 00:59:43 cheshire - Need some randomness to spread queries on the network - -Revision 1.65 2003/06/06 21:41:11 cheshire -For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines - -Revision 1.64 2003/06/06 21:38:55 cheshire -Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we -already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.) - -Revision 1.63 2003/06/06 17:20:14 cheshire -For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass -(Global search-and-replace; no functional change to code execution.) - -Revision 1.62 2003/06/04 01:25:33 cheshire - Cannot perform multi-packet known-answer suppression messages -Display time interval between first and subsequent queries - -Revision 1.61 2003/06/03 05:02:16 cheshire - Duplicate registrations not handled as efficiently as they should be - -Revision 1.60 2003/05/31 00:09:49 cheshire - Add ability to discover what services are on a network - -Revision 1.59 2003/05/29 06:11:35 cheshire -: Report if there appear to be too many "Resolve" callbacks - -Revision 1.58 2003/05/29 05:48:06 cheshire -Minor fix for when generating printf warnings: mDNS_snprintf arguments are now 3,4 - -Revision 1.57 2003/05/26 03:21:27 cheshire -Tidy up address structure naming: -mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) -mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 -mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 - -Revision 1.56 2003/05/26 03:01:27 cheshire - sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead - -Revision 1.55 2003/05/26 00:47:30 cheshire -Comment clarification - -Revision 1.54 2003/05/24 16:39:48 cheshire - SendResponses also needs to handle multihoming better - -Revision 1.53 2003/05/23 02:15:37 cheshire -Fixed misleading use of the term "duplicate suppression" where it should have -said "known answer suppression". (Duplicate answer suppression is something -different, and duplicate question suppression is yet another thing, so the use -of the completely vague term "duplicate suppression" was particularly bad.) - -Revision 1.52 2003/05/22 02:29:22 cheshire - SendQueries needs to handle multihoming better -Complete rewrite of SendQueries. Works much better now :-) - -Revision 1.51 2003/05/21 20:14:55 cheshire -Fix comments and warnings - -Revision 1.50 2003/05/14 07:08:36 cheshire - mDNSResponder should be smarter about reconfigurations -Previously, when there was any network configuration change, mDNSResponder -would tear down the entire list of active interfaces and start again. -That was very disruptive, and caused the entire cache to be flushed, -and caused lots of extra network traffic. Now it only removes interfaces -that have really gone, and only adds new ones that weren't there before. - -Revision 1.49 2003/05/07 01:49:36 cheshire -Remove "const" in ConstructServiceName prototype - -Revision 1.48 2003/05/07 00:18:44 cheshire -Fix typo: "kDNSQClass_Mask" should be "kDNSClass_Mask" - -Revision 1.47 2003/05/06 00:00:46 cheshire - Rationalize naming of domainname manipulation functions - -Revision 1.46 2003/04/30 20:39:09 cheshire -Add comment - -Revision 1.45 2003/04/29 00:40:50 cheshire -Fix compiler warnings - -Revision 1.44 2003/04/26 02:41:56 cheshire - Change timenow from a local variable to a structure member - -Revision 1.43 2003/04/25 01:45:56 cheshire - mDNS_RegisterNoSuchService needs to include a host name - -Revision 1.42 2003/04/15 20:58:31 jgraessl - -Bug #: 3229014 -Added a hash to lookup records in the cache. - -Revision 1.41 2003/04/15 18:09:13 jgraessl - -Bug #: 3228892 -Reviewed by: Stuart Cheshire -Added code to keep track of when the next cache item will expire so we can -call TidyRRCache only when necessary. - -Revision 1.40 2003/03/29 01:55:19 cheshire - mDNSResponder sometimes suffers false self-conflicts when it sees its own packets -Solution: Major cleanup of packet timing and conflict handling rules - -Revision 1.39 2003/03/27 03:30:55 cheshire - Name conflicts not handled properly, resulting in memory corruption, and eventual crash -Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback -Fixes: -1. Make mDNS_DeregisterInterface() safe to call from a callback -2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead - (it never really needed to deregister the interface at all) - -Revision 1.38 2003/03/15 04:40:36 cheshire -Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" - -Revision 1.37 2003/03/14 21:34:11 cheshire - Can't setup and print to Lexmark PS printers via Airport Extreme -Increase size of cache rdata from 512 to 768 - -Revision 1.36 2003/03/05 03:38:35 cheshire -Bug #: 3185731 Bogus error message in console: died or deallocated, but no record of client can be found! -Fixed by leaving client in list after conflict, until client explicitly deallocates - -Revision 1.35 2003/02/21 02:47:54 cheshire -Bug #: 3099194 mDNSResponder needs performance improvements -Several places in the code were calling CacheRRActive(), which searched the entire -question list every time, to see if this cache resource record answers any question. -Instead, we now have a field "CRActiveQuestion" in the resource record structure - -Revision 1.34 2003/02/21 01:54:08 cheshire -Bug #: 3099194 mDNSResponder needs performance improvements -Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") - -Revision 1.33 2003/02/20 06:48:32 cheshire -Bug #: 3169535 Xserve RAID needs to do interface-specific registrations -Reviewed by: Josh Graessley, Bob Bradley - -Revision 1.32 2003/01/31 03:35:59 cheshire -Bug #: 3147097 mDNSResponder sometimes fails to find the correct results -When there were *two* active questions in the list, they were incorrectly -finding *each other* and *both* being marked as duplicates of another question - -Revision 1.31 2003/01/29 02:46:37 cheshire -Fix for IPv6: -A physical interface is identified solely by its InterfaceID (not by IP and type). -On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts. -In cases where the requested outbound protocol (v4 or v6) is not supported on -that InterfaceID, the platform support layer should simply discard that packet. - -Revision 1.30 2003/01/29 01:47:08 cheshire -Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity - -Revision 1.29 2003/01/28 05:23:43 cheshire -Bug #: 3147097 mDNSResponder sometimes fails to find the correct results -Add 'Active' flag for interfaces - -Revision 1.28 2003/01/28 01:35:56 cheshire -Revise comment about ThisQInterval to reflect new semantics - -Revision 1.27 2003/01/13 23:49:42 jgraessl -Merged changes for the following fixes in to top of tree: -3086540 computer name changes not handled properly -3124348 service name changes are not properly handled -3124352 announcements sent in pairs, failing chattiness test - -Revision 1.26 2002/12/23 22:13:28 jgraessl - -Reviewed by: Stuart Cheshire -Initial IPv6 support for mDNSResponder. - -Revision 1.25 2002/09/21 20:44:49 zarzycki -Added APSL info - -Revision 1.24 2002/09/19 23:47:35 cheshire -Added mDNS_RegisterNoSuchService() function for assertion of non-existence -of a particular named service - -Revision 1.23 2002/09/19 21:25:34 cheshire -mDNS_snprintf() doesn't need to be in a separate file - -Revision 1.22 2002/09/19 04:20:43 cheshire -Remove high-ascii characters that confuse some systems - -Revision 1.21 2002/09/17 01:06:35 cheshire -Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init() - -Revision 1.20 2002/09/16 18:41:41 cheshire -Merge in license terms from Quinn's copy, in preparation for Darwin release - -*/ - -#ifndef __mDNSClientAPI_h -#define __mDNSClientAPI_h - -#include // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration -#include "mDNSDebug.h" - -#ifdef __cplusplus - extern "C" { -#endif - -// *************************************************************************** -// Function scope indicators - -// If you see "mDNSlocal" before a function name in a C file, it means the function is not callable outside this file -#ifndef mDNSlocal -#define mDNSlocal static -#endif -// If you see "mDNSexport" before a symbol in a C file, it means the symbol is exported for use by clients -// For every "mDNSexport" in a C file, there needs to be a corresponding "extern" declaration in some header file -// (When a C file #includes a header file, the "extern" declarations tell the compiler: -// "This symbol exists -- but not necessarily in this C file.") -#ifndef mDNSexport -#define mDNSexport -#endif - -// *************************************************************************** -// Structure packing macro - -// If we're not using GNUC, it's not fatal. -// Most compilers naturally pack the on-the-wire structures correctly anyway, so a plain "struct" is usually fine. -// In the event that structures are not packed correctly, mDNS_Init() will detect this and report an error, so the -// developer will know what's wrong, and can investigate what needs to be done on that compiler to provide proper packing. -#ifndef packedstruct - #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) - #define packedstruct struct __attribute__((__packed__)) - #define packedunion union __attribute__((__packed__)) - #else - #define packedstruct struct - #define packedunion union - #endif -#endif - -// *************************************************************************** -#if 0 -#pragma mark - DNS Resource Record class and type constants -#endif - -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] - - 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 - { - kDNSType_A = 1, // 1 Address - kDNSType_NS, // 2 Name Server - kDNSType_MD, // 3 Mail Destination - kDNSType_MF, // 4 Mail Forwarder - kDNSType_CNAME, // 5 Canonical Name - kDNSType_SOA, // 6 Start of Authority - kDNSType_MB, // 7 Mailbox - kDNSType_MG, // 8 Mail Group - kDNSType_MR, // 9 Mail Rename - kDNSType_NULL, // 10 NULL RR - kDNSType_WKS, // 11 Well-known-service - kDNSType_PTR, // 12 Domain name pointer - kDNSType_HINFO, // 13 Host information - 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 - - kDNSQType_ANY = 255 // Not a DNS type, but a DNS query type, meaning "all types" - } DNS_TypeValues; - -// *************************************************************************** -#if 0 -#pragma mark - Simple types -#endif - -// 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 int mDNSBool; -typedef signed char mDNSs8; -typedef unsigned char mDNSu8; -typedef signed short mDNSs16; -typedef unsigned short mDNSu16; -#if _LP64 -typedef signed int mDNSs32; -typedef unsigned int mDNSu32; -#else -typedef signed long mDNSs32; -typedef unsigned long mDNSu32; -#endif - -// 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 mDNSInterfaceID_dummystruct { 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, 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 packedunion { mDNSu8 b[2]; mDNSu16 NotAnInteger; } mDNSOpaque16; -typedef packedunion { mDNSu8 b[4]; mDNSu32 NotAnInteger; } mDNSOpaque32; -typedef packedunion { 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 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 }; - -#define mDNSNULL 0L - -enum - { - mStatus_Waiting = 1, - mStatus_NoError = 0, - - // mDNS return values are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537) - // The top end of the range (FFFE FFFF) is used for error codes; - // the bottom end of the range (FFFE FF00) is used for non-error values; - - // Error codes: - mStatus_UnknownErr = -65537, // 0xFFFE FFFF - mStatus_NoSuchNameErr = -65538, - mStatus_NoMemoryErr = -65539, - mStatus_BadParamErr = -65540, - mStatus_BadReferenceErr = -65541, - mStatus_BadStateErr = -65542, - mStatus_BadFlagsErr = -65543, - mStatus_UnsupportedErr = -65544, - mStatus_NotInitializedErr = -65545, - mStatus_NoCache = -65546, - mStatus_AlreadyRegistered = -65547, - mStatus_NameConflict = -65548, - mStatus_Invalid = -65549, - // = -65550, - mStatus_Incompatible = -65551, - mStatus_BadInterfaceErr = -65552, - - // -65553 - -65789 currently unused - - // Non-error values: - mStatus_GrowCache = -65790, - 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 - -// The longest legal textual form of a DNS name is 1005 bytes, including the C-string terminating NULL at the end. -// Explanation: -// When a native domainname object is converted to printable textual form using ConvertDomainNameToCString(), -// non-printing characters are represented in the conventional DNS way, as '\ddd', where ddd is a three-digit decimal number. -// The longest legal domain name is 255 bytes, in the form of four labels as shown below: -// Length byte, 63 data bytes, length byte, 63 data bytes, length byte, 63 data bytes, length byte, 61 data bytes, zero byte. -// Each label is encoded textually as characters followed by a trailing dot. -// If every character has to be represented as a four-byte escape sequence, then this makes the maximum textual form four labels -// plus the C-string terminating NULL as shown below: -// 63*4+1 + 63*4+1 + 63*4+1 + 61*4+1 + 1 = 1005. -// Note that MAX_ESCAPED_DOMAIN_LABEL is not normally used: If you're only decoding a single label, escaping is usually not required. -// It is for domain names, where dots are used as label separators, that proper escaping is vital. -#define MAX_ESCAPED_DOMAIN_LABEL 254 -#define MAX_ESCAPED_DOMAIN_NAME 1005 - -// *************************************************************************** -#if 0 -#pragma mark - Resource Record structures -#endif - -// 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 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 -// -- 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 -// -- 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 -// 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 = 0x02, // Will become a kDNSRecordTypeVerified when probing is complete - - 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 - - kDNSRecordTypeUniqueMask = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), - kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique), - - 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 packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV; -typedef packedstruct { 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[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)) - 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 DNSQuestion_struct DNSQuestion; -typedef struct mDNS_struct mDNS; -typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; - -// 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); - -struct ResourceRecord_struct - { - 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 - 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; - - 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; - 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 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 - { - // 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 - AuthRecord RR_ADV; // e.g. _services._dns-sd._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 - }; - -// *************************************************************************** -#if 0 -#pragma mark - Question structures -#endif - -// 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; - mDNSu32 qnamehash; - mDNSs32 LastQTime; // Last scheduled transmission of this Q on *all* applicable interfaces - mDNSs32 ThisQInterval; // LastQTime + ThisQInterval is the next scheduled transmission 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; - 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 - { - // 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 mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query); -struct ServiceInfoQuery_struct - { - // 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; - }; - -// *************************************************************************** -#if 0 -#pragma mark - Main mDNS object, used to hold all the mDNS state -#endif - -typedef void mDNSCallback(mDNS *const m, mStatus result); - -#define CACHE_HASH_SLOTS 499 - -enum - { - mDNS_KnownBug_PhantomInterfaces = 1 - }; - -struct mDNS_struct - { - // 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 *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 *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; - CacheRecord *rrcache_free; - CacheRecord *rrcache_hash[CACHE_HASH_SLOTS]; - CacheRecord **rrcache_tail[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 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 ProbeFailTime; - mDNSs32 NumFailedProbes; - mDNSs32 SuppressProbes; - }; - -// *************************************************************************** -#if 0 -#pragma mark - Useful Static Constants -#endif - -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 -#pragma mark - Main Client Functions -#endif - -// 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. -// Most clients use mDNS_Init_AdvertiseLocalAddresses. This causes mDNSCore to automatically -// create the correct address records for all the hosts interfaces. If you plan to advertise -// services being offered by the local machine, this is almost always what you want. -// There are two cases where you might use mDNS_Init_DontAdvertiseLocalAddresses: -// 1. A client-only device, that browses for services but doesn't advertise any of its own. -// 2. A proxy-registration service, that advertises services being offered by other machines, and takes -// the appropriate steps to manually create the correct address records for those other machines. -// In principle, a proxy-like registration service could manually create address records for its own machine too, -// but this would be pointless extra effort when using mDNS_Init_AdvertiseLocalAddresses does that for you. -// -// 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 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 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 -// -// Care should be taken on multi-threaded or interrupt-driven environments. -// The main mDNS routines call mDNSPlatformLock() on entry and mDNSPlatformUnlock() on exit; -// each platform layer needs to implement these appropriately for its respective platform. -// For example, if the support code on a particular platform implements timer callbacks at interrupt time, then -// mDNSPlatformLock/Unlock need to disable interrupts or do similar concurrency control to ensure that the mDNS -// code is not entered by an interrupt-time timer callback while in the middle of processing a client call. - -extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p, - CacheRecord *rrcachestorage, mDNSu32 rrcachesize, - mDNSBool AdvertiseLocalAddresses, - mDNSCallback *Callback, void *Context); -// See notes above on use of NoCache/ZeroCacheSize -#define mDNS_Init_NoCache mDNSNULL -#define mDNS_Init_ZeroCacheSize 0 -// See notes above on use of Advertise/DontAdvertiseLocalAddresses -#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 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 mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr); - -extern mStatus mDNS_StartQuery(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_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, -// to find the IP address, port number, and demultiplexing information for a given named service. -// As with mDNS_StartQuery, it executes asynchronously, and calls the ServiceInfoQueryCallback when the answer is -// found. After the service is resolved, the client should call mDNS_StopResolveService to complete the transaction. -// The client can also call mDNS_StopResolveService at any time to abort the transaction. -// -// mDNS_GetBrowseDomains is a special case of the mDNS_StartQuery call, where the resulting answers -// are a list of PTR records indicating (in the rdata) domains that are recommended for browsing. -// After getting the list of domains to browse, call mDNS_StopQuery to end the search. -// mDNS_GetDefaultBrowseDomain returns the name of the domain that should be highlighted by default. -// -// mDNS_GetRegistrationDomains and mDNS_GetDefaultRegistrationDomain are the equivalent calls to get the list -// 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(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, - mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context); - -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, - 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 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 mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); -#define mDNS_StopBrowse mDNS_StopQuery - -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 - { - mDNS_DomainTypeBrowse = 0, - mDNS_DomainTypeBrowseDefault = 1, - mDNS_DomainTypeRegistration = 2, - mDNS_DomainTypeRegistrationDefault = 3 - } mDNS_DomainType; - -extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context); -// In the Panther mDNSResponder we don't do unicast queries yet, so there's no point trying to do domain enumeration -// mDNS_GetDomains() and mDNS_StopGetDomains() are set to be no-ops so that clients don't try to do browse/register operations that will fail -//#define mDNS_StopGetDomains mDNS_StopQuery -#define mDNS_StopGetDomains(m,q) ((void)(m),(void)(q)) -extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname); -#define mDNS_StopAdvertiseDomains mDNS_Deregister - -// *************************************************************************** -#if 0 -#pragma mark - DNS name utility functions -#endif - -// In order to expose the full capabilities of the DNS protocol (which allows any arbitrary eight-bit values -// in domain name labels, including unlikely characters like ascii nulls and even dots) all the mDNS APIs -// 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. - -// Assignment -// A simple C structure assignment of a domainname can cause a protection fault by accessing unmapped memory, -// because that object is defined to be 256 bytes long, but not all domainname objects are truly the full size. -// This macro uses mDNSPlatformMemCopy() to make sure it only touches the actual bytes that are valid. -#define AssignDomainName(DST, SRC) mDNSPlatformMemCopy((SRC).c, (DST).c, DomainNameLength(&(SRC))) - -// Comparison functions -extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b); -extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2); - -// 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 -// IMPORTANT: -// When using ConvertDomainLabelToCString, the target buffer must be MAX_ESCAPED_DOMAIN_LABEL (254) bytes long -// to guarantee there will be no buffer overrun. It is only safe to use a buffer shorter than this in rare cases -// where the label is known to be constrained somehow (for example, if the label is known to be either "_tcp" or "_udp"). -// Similarly, when using ConvertDomainNameToCString, the target buffer must be MAX_ESCAPED_DOMAIN_NAME (1005) bytes long. -// See definitions of MAX_ESCAPED_DOMAIN_LABEL and MAX_ESCAPED_DOMAIN_NAME for more detailed explanation. -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); - -// 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)) - -// *************************************************************************** -#if 0 -#pragma mark - Other utility functions -#endif - -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); - -// *************************************************************************** -#if 0 -#pragma mark - PlatformSupport interface -#endif - -// This section defines the interface to the Platform Support layer. -// Normal client code should not use any of types defined here, or directly call any of the functions defined here. -// The definitions are placed here because sometimes clients do use these calls indirectly, via other supported client operations. -// For example, AssignDomainName is a macro defined using mDNSPlatformMemCopy() - -typedef packedstruct - { - mDNSOpaque16 id; - mDNSOpaque16 flags; - mDNSu16 numQuestions; - mDNSu16 numAnswers; - mDNSu16 numAuthorities; - mDNSu16 numAdditionals; - } DNSMessageHeader; - -// 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 -// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total -#define AbsoluteMaxDNSMessageData 8940 -#define NormalMaxDNSMessageData 1440 -typedef packedstruct - { - DNSMessageHeader h; // Note: Size 12 bytes - mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 - } DNSMessage; - -// Every platform support module must provide the following functions. -// mDNSPlatformInit() typically opens a communication endpoint, and starts listening for mDNS packets. -// When Setup is complete, the platform support layer calls mDNSCoreInitComplete(). -// mDNSPlatformSendUDP() sends one UDP packet -// When a packet is received, the PlatformSupport code calls mDNSCoreReceive() -// mDNSPlatformClose() tidies up on exit -// 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, - mDNSInterfaceID InterfaceID, mDNSIPPort srcport, const mDNSAddr *dst, mDNSIPPort dstport); - -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 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 change of the dot-local host name. -// -// 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, - 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); - -// *************************************************************************** -#if 0 -#pragma mark - Compile-Time assertion checks -#endif - -// Some C compiler cleverness. We can make the compiler check certain things for -// us, and report compile-time errors if anything is wrong. The usual way to do -// this would be to use a run-time "if" statement, but then you don't find out -// what's wrong until you run the software. This way, if the assertion condition -// is false, the array size is negative, and the complier complains immediately. - -struct mDNS_CompileTimeAssertionChecks - { - // Check that the compiler generated our on-the-wire packet format structure definitions - // properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries. - char assert0[(sizeof(rdataSRV) == 262 ) ? 1 : -1]; - char assert1[(sizeof(DNSMessageHeader) == 12 ) ? 1 : -1]; - char assert2[(sizeof(DNSMessage) == 12+AbsoluteMaxDNSMessageData) ? 1 : -1]; - char assert3[(sizeof(mDNSs8) == 1 ) ? 1 : -1]; - char assert4[(sizeof(mDNSu8) == 1 ) ? 1 : -1]; - char assert5[(sizeof(mDNSs16) == 2 ) ? 1 : -1]; - char assert6[(sizeof(mDNSu16) == 2 ) ? 1 : -1]; - char assert7[(sizeof(mDNSs32) == 4 ) ? 1 : -1]; - char assert8[(sizeof(mDNSu32) == 4 ) ? 1 : -1]; - char assert9[(sizeof(mDNSOpaque16) == 2 ) ? 1 : -1]; - char assertA[(sizeof(mDNSOpaque32) == 4 ) ? 1 : -1]; - char assertB[(sizeof(mDNSOpaque128) == 16 ) ? 1 : -1]; - }; - -// *************************************************************************** - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/mDNSCore/mDNSPlatformFunctions.h b/mDNSCore/mDNSPlatformFunctions.h deleted file mode 100755 index 8f07d59..0000000 --- a/mDNSCore/mDNSPlatformFunctions.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.2.1 2003/12/05 00:03:34 cheshire - Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256 - -Revision 1.22 2003/08/18 22:53:37 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() - -Revision 1.21 2003/08/15 20:16:57 cheshire -Update comment for mDNSResponder takes too much RPRVT - -Revision 1.20 2003/08/12 19:56:24 cheshire -Update to APSL 2.0 - -Revision 1.19 2003/08/05 22:20:15 cheshire - Need to check IP TTL on responses - -Revision 1.18 2003/07/22 23:57:20 cheshire -Move platform-layer function prototypes from mDNSClientAPI.h to mDNSPlatformFunctions.h where they belong - -Revision 1.17 2003/07/19 03:15:15 cheshire -Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, -and add the obvious trivial implementations to each platform support layer - -Revision 1.16 2003/07/02 21:19:46 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.15 2003/05/23 22:39:45 cheshire - Need to adjust maximum packet size for IPv6 - -Revision 1.14 2003/04/28 21:54:57 cheshire -Fix compiler warning - -Revision 1.13 2003/03/15 04:40:36 cheshire -Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" - -Revision 1.12 2003/02/21 01:54:08 cheshire -Bug #: 3099194 mDNSResponder needs performance improvements -Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") - -Revision 1.11 2002/12/23 22:13:29 jgraessl - -Reviewed by: Stuart Cheshire -Initial IPv6 support for mDNSResponder. - -Revision 1.10 2002/09/21 20:44:49 zarzycki -Added APSL info - -Revision 1.9 2002/09/19 04:20:43 cheshire -Remove high-ascii characters that confuse some systems - -Revision 1.8 2002/09/16 23:12:14 cheshire -Minor code tidying - -Revision 1.7 2002/09/16 18:41:42 cheshire -Merge in license terms from Quinn's copy, in preparation for Darwin release - -*/ - -// Note: All moved to mDNSClientAPI.h diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c new file mode 100755 index 0000000..90fd363 --- /dev/null +++ b/mDNSCore/uDNS.c @@ -0,0 +1,3001 @@ +/* + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: uDNS.c,v $ +Revision 1.54 2004/06/22 02:10:53 ksekar +: Lighthouse failure causes packet flood to DNS + +Revision 1.53 2004/06/17 20:49:09 ksekar +: Tiger8A148: repeated crash of mDNSResponder while location cycling + +Revision 1.52 2004/06/17 01:13:11 ksekar +: polling interval too short + +Revision 1.51 2004/06/10 04:36:44 cheshire +Fix compiler warning + +Revision 1.50 2004/06/10 00:55:13 ksekar +: crash on network reconnect + +Revision 1.49 2004/06/10 00:10:50 ksekar +: Infinite Loop in uDNS_Execute() + +Revision 1.48 2004/06/09 20:03:37 ksekar +: Incorrect copying of resource record in deregistration + +Revision 1.47 2004/06/09 03:48:28 ksekar +: nameserver address fails with prod. Lighthouse server + +Revision 1.46 2004/06/09 01:44:30 ksekar + reworked Cache Record copy code + +Revision 1.45 2004/06/08 18:54:47 ksekar +: mDNSResponder leaks after exploring in Printer Setup Utility + +Revision 1.44 2004/06/05 00:33:51 cheshire +: Check for incorrect time comparisons + +Revision 1.43 2004/06/05 00:14:44 cheshire +Fix signed/unsigned and other compiler warnings + +Revision 1.42 2004/06/04 22:36:16 ksekar +Properly set u->nextevent in uDNS_Execute + +Revision 1.41 2004/06/04 08:58:29 ksekar +: Keychain integration for secure dynamic update + +Revision 1.40 2004/06/03 03:09:58 ksekar +: Garbage Collection for Dynamic Updates + +Revision 1.39 2004/06/01 23:46:50 ksekar +: DynDNS: dynamically look up LLQ/Update ports + +Revision 1.38 2004/05/31 22:19:44 ksekar +: Feature: DNS server->client notification on +record changes (#7805) - revert to polling mode on setup errors + +Revision 1.37 2004/05/28 23:42:37 ksekar +: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.36 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.35 2004/05/07 23:01:04 ksekar +Cleaned up list traversal in deriveGoodbyes - removed unnecessary +conditional assignment. + +Revision 1.34 2004/05/05 18:26:12 ksekar +Periodically re-transmit questions if the send() fails. Include +internal questions in retransmission. + +Revision 1.33 2004/05/05 17:40:06 ksekar +Removed prerequisite from deregistration update - it does not work for +shared records, and is unnecessary until we have more sophisticated +name conflict management. + +Revision 1.32 2004/05/05 17:32:18 ksekar +Prevent registration of loopback interface caused by removal of +Multicast flag in interface structure. + +Revision 1.31 2004/05/05 17:05:02 ksekar +Use LargeCacheRecord structs when pulling records off packets + +Revision 1.30 2004/04/16 21:33:27 ksekar +Fixed bug in processing GetZoneData responses that do not use BIND formatting. + +Revision 1.29 2004/04/15 20:03:13 ksekar +Clarified log message when pulling bad resource records off packet. + +Revision 1.28 2004/04/15 00:51:28 bradley +Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers. +Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers. + +Revision 1.27 2004/04/14 23:09:28 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.26 2004/04/14 19:36:05 ksekar +Fixed memory corruption error in deriveGoodbyes. + +Revision 1.25 2004/04/14 04:07:11 ksekar +Fixed crash in IsActiveUnicastQuery(). Removed redundant checks in routine. + +Revision 1.24 2004/04/08 09:41:40 bradley +Added const to AuthRecord in deadvertiseIfCallback to match callback typedef. + +Revision 1.23 2004/03/24 00:29:45 ksekar +Make it safe to call StopQuery in a unicast question callback + +Revision 1.22 2004/03/19 10:11:09 bradley +Added AuthRecord * cast from umalloc for C++ builds. + +Revision 1.21 2004/03/15 02:03:45 bradley +Added const to params where needed to match prototypes. Changed SetNewRData calls to use 0 instead +of -1 for unused size to fix warning. Disable assignment within conditional warnings with Visual C++. + +Revision 1.20 2004/03/13 02:07:26 ksekar +: DynDNS: Dynamic update of service records + +Revision 1.19 2004/03/13 01:57:33 ksekar +: DynDNS: Dynamic update of service records + +Revision 1.18 2004/02/21 08:34:15 bradley +Added casts from void * to specific type for C++ builds. Changed void * l-value cast +r-value cast to fix problems with VC++ builds. Removed empty switch to fix VC++ error. + +Revision 1.17 2004/02/21 02:06:24 cheshire +Can't use anonymous unions -- they're non-standard and don't work on all compilers + +Revision 1.16 2004/02/12 01:51:45 cheshire +Don't try to send uDNS queries unless we have at least one uDNS server available + +Revision 1.15 2004/02/10 03:02:46 cheshire +Fix compiler warning + +Revision 1.14 2004/02/06 23:04:19 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.13 2004/02/03 22:15:01 ksekar +Fixed nameToAddr error check: don't abort state machine on nxdomain error. + +Revision 1.12 2004/02/03 19:47:36 ksekar +Added an asyncronous state machine mechanism to uDNS.c, including +calls to find the parent zone for a domain name. Changes include code +in repository previously dissabled via "#if 0 //incomplete". Codepath +is currently unused, and will be called to create update records, etc. + +Revision 1.11 2004/01/30 02:12:30 ksekar +Changed uDNS_ReceiveMsg() to correctly return void. + +Revision 1.10 2004/01/29 02:59:17 ksekar +Unicast DNS: Changed from a resource record oriented question/response +matching to packet based matching. New callback architecture allows +collections of records in a response to be processed differently +depending on the nature of the request, and allows the same structure +to be used for internal and client-driven queries with different processing needs. + +Revision 1.9 2004/01/28 20:20:45 ksekar +Unified ActiveQueries and ActiveInternalQueries lists, using a flag to +demux them. Check-in includes work-in-progress code, #ifdef'd out. + +Revision 1.8 2004/01/28 02:30:07 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.7 2004/01/27 20:15:22 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.6 2004/01/24 23:47:17 cheshire +Use mDNSOpaque16fromIntVal() instead of shifting and masking + +Revision 1.5 2004/01/24 04:59:15 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.4 2004/01/24 04:19:26 cheshire +Restore overwritten checkin 1.2 + +Revision 1.3 2004/01/23 23:23:15 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.2 2004/01/22 03:48:41 cheshire +Make sure uDNS client doesn't accidentally use query ID zero + +Revision 1.1 2003/12/13 03:05:27 ksekar +: DynDNS: Unicast query of service records + + */ + +#include "uDNS.h" + +#if(defined(_MSC_VER)) + // 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 + +#ifndef NULL +#define NULL mDNSNULL +#endif // NULL + + +#define ustrcpy(d,s) mDNSPlatformStrCopy(s,d) // use strcpy(2) param ordering +#define ustrlen(s) mDNSPlatformStrLen(s) +#define umalloc(x) mDNSPlatformMemAllocate(x) // short hands for common routines +#define ufree(x) mDNSPlatformMemFree(x) +#define ubzero(x,y) mDNSPlatformMemZero(x,y) +#define umemcpy(x, y, l) mDNSPlatformMemCopy(y, x, l) // uses memcpy(2) arg ordering + + +// Asyncronous operation types + +typedef enum + { + zoneDataResult + // other async. operation names go here + } AsyncOpResultType; + +typedef struct + { + domainname zoneName; + mDNSAddr primaryAddr; + mDNSu16 zoneClass; + mDNSIPPort llqPort; + mDNSIPPort updatePort; + } zoneData_t; + +// other async. result struct defs go here + +typedef struct + { + AsyncOpResultType type; + zoneData_t zoneData; + // other async result structs go here + } AsyncOpResult; + +typedef void AsyncOpCallback(mStatus err, mDNS *const m, void *info, const AsyncOpResult *result); + + +// Private Function Prototypes +// Note: In general, functions are ordered such that they do not require forward declarations. +// However, prototypes are used where cyclic call graphs exist (e.g. foo calls bar, and bar calls +// foo), or when they aid in the grouping or readability of code (e.g. state machine code that is easier +// read top-to-bottom.) + +mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m); +mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort, + AsyncOpCallback callback, void *callbackInfo); +mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID); +mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr); +mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs); + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - General Utility Functions +#endif + +mDNSlocal mDNSOpaque16 newMessageID(uDNS_GlobalInfo *u) + { + // if NextMessageID is 0 (ininitialized) or 0xffff (reserved for TCP packets) reset to 1 + if (!u->NextMessageID || u->NextMessageID == (mDNSu16)~0) u->NextMessageID = 1; + return mDNSOpaque16fromIntVal(u->NextMessageID++); + } + +// unlink an AuthRecord from a linked list +mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr) + { + AuthRecord *rptr, *prev = NULL; + + for (rptr = *list; rptr; rptr = rptr->next) + { + if (rptr == rr) + { + if (prev) prev->next = rptr->next; + else *list = rptr->next; + rptr->next = NULL; + return mStatus_NoError; + } + prev = rptr; + } + LogMsg("ERROR: unlinkAR - no such active record"); + return mStatus_UnknownErr; + } + +mDNSlocal void LinkActiveQuestion(uDNS_GlobalInfo *u, DNSQuestion *q) + { + if (IsActiveUnicastQuery(q, u)) + { LogMsg("LinkActiveQuestion - %s (%d) already in list!", q->qname.c, q->qtype); return; } + + q->next = u->ActiveQueries; + u->ActiveQueries = q; + } + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Name Server List Management +#endif + +mDNSexport void mDNS_RegisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr) + { + //!!!KRS do this dynamically! + uDNS_GlobalInfo *u = &m->uDNS_info; + int i; + + if (!dnsAddr->NotAnInteger) + { + LogMsg("ERROR: attempt to register DNS with IP address 0"); + return; + } + + for (i = 0; i < 32; i++) + { + if (!u->Servers[i].ip.v4.NotAnInteger) + { + u->Servers[i].ip.v4.NotAnInteger = dnsAddr->NotAnInteger; + u->Servers[i].type = mDNSAddrType_IPv4; + return; + } + if (u->Servers[i].ip.v4.NotAnInteger == dnsAddr->NotAnInteger) + { + LogMsg("ERROR: mDNS_RegisterDNS - DNS already registered"); + return; + } + } + if (i == 32) { LogMsg("ERROR: mDNS_RegisterDNS - too many registered servers"); } + + } + +mDNSexport void mDNS_DeregisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + int i; + + if (!dnsAddr->NotAnInteger) + { + LogMsg("ERROR: attempt to deregister DNS with IP address 0"); + return; + } + + for (i = 0; i < 32; i++) + { + + if (u->Servers[i].ip.v4.NotAnInteger == dnsAddr->NotAnInteger) + { + u->Servers[i].ip.v4.NotAnInteger = 0; + return; + } + } + if (i == 32) { LogMsg("ERROR: mDNS_DeregisterDNS - no such DNS registered"); } + } + +mDNSexport void mDNS_DeregisterDNSList(mDNS *const m) + { + ubzero(m->uDNS_info.Servers, 32 * sizeof(mDNSAddr)); + } + +mDNSexport mDNSBool mDNS_DNSRegistered(mDNS *const m) + { + int i; + + for (i = 0; i < 32; i++) if (m->uDNS_info.Servers[i].ip.v4.NotAnInteger) return mDNStrue; + return mDNSfalse; + } + + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - authorization management +#endif + + +mDNSexport mStatus mDNS_UpdateDomainRequiresAuthentication(mDNS *m, domainname *zone, domainname *key, + mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64) + { + uDNS_AuthInfo *info; + mDNSu8 keybuf[1024]; + mDNSs32 keylen; + + info = (uDNS_AuthInfo*)umalloc(sizeof(uDNS_AuthInfo) + ssLen); + if (!info) { LogMsg("ERROR: umalloc"); return mStatus_NoMemoryErr; } + ubzero(info, sizeof(uDNS_AuthInfo)); + ustrcpy(info->zone.c, zone->c); + ustrcpy(info->keyname.c, key->c); + + if (base64) + { + keylen = DNSDigest_Base64ToBin((const char*)sharedSecret, keybuf, 1024); + if (keylen < 0) + { + LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication - could not convert shared secret from base64"); + ufree(info); + return mStatus_UnknownErr; + } + DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen); + } + else DNSDigest_ConstructHMACKey(info, sharedSecret, ssLen); + + // link into list + // !!!KRS this should be a hashtable since we must check if updates are required on each registration + info->next = m->uDNS_info.AuthInfoList; + m->uDNS_info.AuthInfoList = info; + return mStatus_NoError; + } + +mDNSexport void mDNS_ClearAuthenticationList(mDNS *m) + { + uDNS_AuthInfo *fptr, *ptr = m->uDNS_info.AuthInfoList; + + while (ptr) + { + fptr = ptr; + ptr = ptr->next; + ufree(fptr); + } + m->uDNS_info.AuthInfoList = NULL; + } + +mDNSlocal uDNS_AuthInfo *GetAuthInfoForZone(const uDNS_GlobalInfo *u, const domainname *zone) + { + uDNS_AuthInfo *ptr; + domainname *z; + mDNSu32 zoneLen, ptrZoneLen; + + zoneLen = ustrlen(zone->c); + for (ptr = u->AuthInfoList; ptr; ptr = ptr->next) + { + z = &ptr->zone; + ptrZoneLen = ustrlen(z->c); + if (zoneLen < ptrZoneLen) continue; + // return info if zone ends in info->zone + if (mDNSPlatformMemSame(z->c, zone->c + (zoneLen - ptrZoneLen), ptrZoneLen)) return ptr; + } + return NULL; + } + + + + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - host name and interface management +#endif + + +mDNSlocal void hostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + // note that the rr is already unlinked if result is non-zero + + if (result == mStatus_MemFree) return; + if (result == mStatus_NameConflict && rr->resrec.RecordType == kDNSRecordTypeUnique) + { + // if we get a name conflict, make sure our name/addr isn't already registered by re-registering + rr->resrec.RecordType = kDNSRecordTypeKnownUnique; + uDNS_RegisterRecord(m, rr); + return; + } + + if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) + // we've already tried to re-register. reset RecordType before returning RR to client + { + if (result == mStatus_NoSuchRecord) // name is advertised for some other address + result = mStatus_NameConflict; + rr->resrec.RecordType = kDNSRecordTypeUnique; + } + + if (!result) rr->resrec.RecordType = kDNSRecordTypeVerified; + if (result) + ((NetworkInterfaceInfo *)(rr->RecordContext))->uDNS_info.registered = mDNSfalse; + mDNS_HostNameCallback(m, rr, result); + } + + +mDNSlocal void deadvertiseIfCallback(mDNS *const m, AuthRecord *const rr, mStatus err) + { + (void)m; // unused + + if (err == mStatus_MemFree) ufree(rr); + else LogMsg("deadvertiseIfCallback - error %s for record %s", err, rr->resrec.name.c); + } + + mDNSexport void uDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) + { + AuthRecord *copy; + AuthRecord *rr = &set->uDNS_info.RR_A; + + // NOTE: for compatibility w/ mDNS architecture, we make a copy of the address record before sending a + // goodbye, since mDNS does not send goodbyes for address records and expects the memory to be immediately + // freed + + if (set->uDNS_info.registered) + { + // copy resource record + copy = (AuthRecord*)umalloc(sizeof(AuthRecord)); // allocate storage + if (!copy) { LogMsg("ERROR: Malloc"); return; } + umemcpy(copy, rr, sizeof(AuthRecord)); // copy all fields + copy->resrec.rdata = ©->rdatastorage; // set rdata pointer + if (rr->resrec.rdata != &rr->rdatastorage) + { LogMsg("ERROR: uDNS_DeadvertiseInterface - expected local rdata storage. Aborting deregistration"); return; } + + // link copy into list + copy->next = m->uDNS_info.RecordRegistrations; + m->uDNS_info.RecordRegistrations = copy; + copy->RecordCallback = deadvertiseIfCallback; + + // unlink the original + unlinkAR(&m->uDNS_info.RecordRegistrations, rr); + rr->uDNS_info.state = regState_Unregistered; + set->uDNS_info.registered = mDNSfalse; + uDNS_DeregisterRecord(m, copy); + } + else debugf("uDNS_DeadvertiseInterface - interface not registered"); + return; + } + +mDNSexport void uDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) + { + mDNSu8 *ip = set->ip.ip.v4.b; + AuthRecord *a = &set->uDNS_info.RR_A; + a->RecordContext = set; + if (set->ip.type != mDNSAddrType_IPv4 // non-v4 + || (ip[0] == 169 && ip[1] == 254) // link-local + || (ip[0] == 127 && ip[1] == 0 && ip[2] == 0 && ip[3] == 1)) // loopback + return; + + if (set->uDNS_info.registered && SameDomainName(&m->uDNS_info.hostname, &set->uDNS_info.regname)) + return; // already registered + + if (!m->uDNS_info.hostname.c[0]) + { + // no hostname available + set->uDNS_info.registered = mDNSfalse; + return; + } + + set->uDNS_info.registered = mDNStrue; + ustrcpy(set->uDNS_info.regname.c, m->uDNS_info.hostname.c); + //!!!KRS temp ttl 1 + mDNS_SetupResourceRecord(a, mDNSNULL, 0, kDNSType_A, 1, kDNSRecordTypeShared /*Unique*/, hostnameCallback, set); //!!!KRS + + ustrcpy(a->resrec.name.c, m->uDNS_info.hostname.c); + a->resrec.rdata->u.ip = set->ip.ip.v4; + LogMsg("uDNS_AdvertiseInterface: advertising %s", m->uDNS_info.hostname.c); + + uDNS_RegisterRecord(m, a); + } + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Incoming Message Processing +#endif + +mDNSlocal mDNSBool sameResourceRecord(ResourceRecord *r1, ResourceRecord *r2) + { + return (r1->namehash == r2->namehash && + r1->rrtype == r2->rrtype && + SameDomainName(&r1->name, &r2->name) && + SameRData(r1, r2)); + } + +mDNSlocal mDNSBool kaListContainsAnswer(DNSQuestion *question, CacheRecord *rr) + { + CacheRecord *ptr; + + for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next) + if (sameResourceRecord(&ptr->resrec, &rr->resrec)) return mDNStrue; + + return mDNSfalse; + } + + +mDNSlocal void removeKnownAnswer(DNSQuestion *question, CacheRecord *rr) + { + CacheRecord *ptr, *prev = NULL; + + for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next) + { + if (sameResourceRecord(&ptr->resrec, &rr->resrec)) + { + if (prev) prev->next = ptr->next; + else question->uDNS_info.knownAnswers = ptr->next; + ufree(ptr); + return; + } + prev = ptr; + } + LogMsg("removeKnownAnswer() called for record not in KA list"); + } + + +mDNSlocal void addKnownAnswer(DNSQuestion *question, const CacheRecord *rr) + { + CacheRecord *newCR = NULL; + mDNSu32 size; + + size = sizeof(CacheRecord) + rr->resrec.rdlength - InlineCacheRDSize; + newCR = (CacheRecord *)umalloc(size); + if (!newCR) { LogMsg("ERROR: addKnownAnswer - malloc"); return; } + umemcpy(newCR, rr, size); + newCR->resrec.rdata = (RData*)&newCR->rdatastorage; + newCR->resrec.rdata->MaxRDLength = rr->resrec.rdlength; + newCR->next = question->uDNS_info.knownAnswers; + question->uDNS_info.knownAnswers = newCR; + } + +mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question) + { + const mDNSu8 *ptr; + int i; + CacheRecord *fptr, *ka, *cr, *answers = NULL, *prev = NULL; + LargeCacheRecord *lcr; + + if (question != m->uDNS_info.CurrentQuery) { LogMsg("ERROR: deriveGoodbyes called without CurrentQuery set!"); return; } + + ptr = LocateAnswers(msg, end); + if (!ptr) goto pkt_error; + + if (!msg->h.numAnswers) + { + // delete the whole KA list + ka = question->uDNS_info.knownAnswers; + while (ka) + { + debugf("deriving goodbye for %s", ka->resrec.name.c); + question->QuestionCallback(m, question, &ka->resrec, mDNSfalse); + if (question != m->uDNS_info.CurrentQuery) + { + debugf("deriveGoodbyes - question removed via callback. returning."); + return; + } + fptr = ka; + ka = ka->next; + ufree(fptr); + } + question->uDNS_info.knownAnswers = NULL; + return; + } + + // make a list of all the new answers + for (i = 0; i < msg->h.numAnswers; i++) + { + lcr = (LargeCacheRecord *)umalloc(sizeof(LargeCacheRecord)); + if (!lcr) goto malloc_error; + ubzero(lcr, sizeof(LargeCacheRecord)); + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAns, lcr); + if (!ptr) goto pkt_error; + cr = &lcr->r; + if (ResourceRecordAnswersQuestion(&cr->resrec, question)) + { + cr->next = answers; + answers = cr; + } + else ufree(cr); + } + + // make sure every known answer is in the answer list + ka = question->uDNS_info.knownAnswers; + while (ka) + { + for (cr = answers; cr; cr = cr->next) + { if (sameResourceRecord(&ka->resrec, &cr->resrec)) break; } + if (!cr) + { + // record is in KA list but not answer list - remove from KA list + if (prev) prev->next = ka->next; + else question->uDNS_info.knownAnswers = ka->next; + debugf("deriving goodbye for %s", ka->resrec.name.c); + question->QuestionCallback(m, question, &ka->resrec, mDNSfalse); + if (question != m->uDNS_info.CurrentQuery) + { + debugf("deriveGoodbyes - question removed via callback. returning."); + return; + } + fptr = ka; + ka = ka->next; + ufree(fptr); + } + else + { + prev = ka; + ka = ka->next; + } + } + + // free temp answers list + cr = answers; + while (cr) { fptr = cr; cr = cr->next; ufree(fptr); } + + return; + + pkt_error: + LogMsg("ERROR: deriveGoodbyes - received malformed response to query for %s (%d)", + question->qname.c, question->qtype); + return; + + malloc_error: + LogMsg("ERROR: Malloc"); + } + +mDNSlocal void pktResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, mDNSBool llq) + { + const mDNSu8 *ptr; + int i; + LargeCacheRecord lcr; + CacheRecord *cr = &lcr.r; + mDNSBool goodbye, inKAList; + LLQ_Info *llqInfo = question->uDNS_info.llq; + + if (question != m->uDNS_info.CurrentQuery) + { LogMsg("ERROR: pktResponseHdnlr called without CurrentQuery ptr set!"); return; } + + ptr = LocateAnswers(msg, end); + if (!ptr) goto pkt_error; + + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) goto pkt_error; + if (ResourceRecordAnswersQuestion(&cr->resrec, question)) + { + goodbye = llq ? ((mDNSs32)cr->resrec.rroriginalttl == -1) : mDNSfalse; + inKAList = kaListContainsAnswer(question, cr); + + if ((goodbye && !inKAList) || (!goodbye && inKAList)) continue; // list up to date + if (!inKAList) addKnownAnswer(question, cr); + if (goodbye) removeKnownAnswer(question, cr); + question->QuestionCallback(m, question, &cr->resrec, !goodbye); + if (question != m->uDNS_info.CurrentQuery) + { + debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning"); + return; + } + } + else + { + LogMsg("unexpected answer: %s", cr->resrec.name.c); + } + } + if (llq && (llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume)) + { deriveGoodbyes(m, msg, end,question); llqInfo->deriveRemovesOnResume = mDNSfalse; } + //!!!KRS should we derive goodbyes for non-LLQs? + + return; + + pkt_error: + LogMsg("ERROR: pktResponseHndlr - received malformed response to query for %s (%d)", + question->qname.c, question->qtype); + return; + } + +mDNSlocal void simpleResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context) + { + (void)context; // unused + pktResponseHndlr(m, msg, end, question, mDNSfalse); + } + +mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context) + { + (void)context; // unused + pktResponseHndlr(m, msg, end, question, mDNStrue); + } + + + +mDNSlocal void unlinkSRS(uDNS_GlobalInfo *u, ServiceRecordSet *srs) + { + ServiceRecordSet *ptr, *prev = NULL; + + for (ptr = u->ServiceRegistrations; ptr; ptr = ptr->next) + { + if (ptr == srs) + { + if (prev) prev->next = ptr->next; + else u->ServiceRegistrations = ptr->next; + ptr->next = NULL; + return; + } + prev = ptr; + } + LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list"); + } + + +mDNSlocal mStatus checkUpdateResult(domainname *name, mDNSu8 rcode, const DNSMessage *msg) + { + (void)msg; // currently unused, needed for TSIG errors + if (!rcode) return mStatus_NoError; + else if (rcode == kDNSFlag1_RC_YXDomain) + { + LogMsg("Name in use: %s", name->c); + return mStatus_NameConflict; + } + else if (rcode == kDNSFlag1_RC_Refused) + { + LogMsg("Update %s refused", name->c); + return mStatus_Refused; + } + else if (rcode == kDNSFlag1_RC_NXRRSet) + { + LogMsg("Reregister refusted (NXRRSET): %s", name->c); + return mStatus_NoSuchRecord; + } + else if (rcode == kDNSFlag1_RC_NotAuth) + { + LogMsg("Permission denied (NOAUTH): %s", name->c); + return mStatus_NoAuth; + } + else if (rcode == kDNSFlag1_RC_FmtErr) + { + LogMsg("Format Error: %s", name->c); + return mStatus_UnknownErr; + //!!!KRS need to parse message for TSIG errors + } + else + { + LogMsg("Update %s failed with rcode %d", name->c, rcode); + return mStatus_UnknownErr; + } + } + +mDNSlocal void hndlServiceUpdateReply(mDNS * const m, ServiceRecordSet *srs, mStatus err) + { + //!!!KRS make sure we're doing the right thing w/ MemFree + + switch (srs->uDNS_info.state) + { + case regState_Pending: + case regState_Refresh: + if (err) + { + if (srs->uDNS_info.lease && err == mStatus_UnknownErr) + { + LogMsg("Re-trying update of service %s without lease option", srs->RR_SRV.resrec.name.c); + srs->uDNS_info.lease = mDNSfalse; + srs->uDNS_info.expire = -1; + SendServiceRegistration(m, srs); + return; + } + else + { + LogMsg("hndlServiceUpdateReply: Error %d returned for registration of %s", + err, srs->RR_SRV.resrec.name.c); + srs->uDNS_info.state = regState_Unregistered; + break; + } + } + else + { + if (srs->uDNS_info.state == regState_Refresh) + { + srs->uDNS_info.state = regState_Registered; + return; + } + srs->uDNS_info.state = regState_Registered; + break; + } + case regState_DeregPending: + if (err) LogMsg("hndlServiceUpdateReply: Error %d returned for dereg of %s", + err, srs->RR_SRV.resrec.name.c); + else err = mStatus_MemFree; + break; + case regState_DeregDeferred: + if (err) LogMsg("hndlServiceUpdateReply: Error %d received prior to deferred derigstration of %s", + err, srs->RR_SRV.resrec.name.c); + LogMsg("Performing deferred deregistration of %s", srs->RR_SRV.resrec.name.c); + uDNS_DeregisterService(m, srs); + return; + case regState_TargetChange: + if (err) + { + LogMsg("hdnlServiceUpdateReply: Error %d returned for host target update of %s", + err, srs->RR_SRV.resrec.name.c); + srs->uDNS_info.state = regState_Unregistered; + // !!!KRS we are leaving the ptr/txt records registered + } + else srs->uDNS_info.state = regState_Registered; + break; + default: + LogMsg("hndlServiceUpdateReply called for service %s in unexpected state %d with error %d. Unlinking.", + srs->RR_SRV.resrec.name.c, srs->uDNS_info.state, err); + err = mStatus_UnknownErr; + } + + if (err) + { + unlinkSRS(&m->uDNS_info, srs); // name conflicts, force dereg, and errors + srs->uDNS_info.state = regState_Unregistered; + } + + srs->ServiceCallback(m, srs, err); + // NOTE: do not touch structures after calling ServiceCallback + } + +mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + + if (rr->uDNS_info.state == regState_DeregPending) + { + debugf("Received reply for deregister record %s type %d", rr->resrec.name.c, rr->resrec.rrtype); + if (err) LogMsg("ERROR: Deregistration of record %s type %s failed with error %d", + rr->resrec.name.c, rr->resrec.rrtype, err); + else err = mStatus_MemFree; + if (unlinkAR(&m->uDNS_info.RecordRegistrations, rr)) + LogMsg("ERROR: Could not unlink resource record following deregistration"); + rr->uDNS_info.state = regState_Unregistered; + rr->RecordCallback(m, rr, err); + return; + } + + if (rr->uDNS_info.state == regState_DeregDeferred) + { + if (err) + { + LogMsg("Cancelling deferred deregistration record %s type %d due to registration error %d", + rr->resrec.name.c, rr->resrec.rrtype, err); + unlinkAR(&m->uDNS_info.RecordRegistrations, rr); + rr->uDNS_info.state = regState_Unregistered; + return; + } + LogMsg("Calling deferred deregistration of record %s type %d", + rr->resrec.name.c, rr->resrec.rrtype); + rr->uDNS_info.state = regState_Registered; + uDNS_DeregisterRecord(m, rr); + return; + } + + if (rr->uDNS_info.state == regState_Pending || rr->uDNS_info.state == regState_Refresh) + { + if (err) + { + if (rr->uDNS_info.lease && err == mStatus_UnknownErr) + { + LogMsg("Re-trying update of record %s without lease option", rr->resrec.name.c); + rr->uDNS_info.lease = mDNSfalse; + rr->uDNS_info.expire = -1; + sendRecordRegistration(m, rr); + return; + } + + LogMsg("Registration of record %s type %d failed with error %d", + rr->resrec.name.c, rr->resrec.rrtype, err); + unlinkAR(&u->RecordRegistrations, rr); + rr->uDNS_info.state = regState_Unregistered; + } + else + { + if (rr->uDNS_info.state == regState_Refresh) + rr->uDNS_info.state = regState_Registered; + else + { + rr->uDNS_info.state = regState_Registered; + rr->RecordCallback(m, rr, err); + } + return; + } + } + + LogMsg("Received unexpected response for record %s type %d, in state %d, with response error %d", + rr->resrec.name.c, rr->resrec.rrtype, rr->uDNS_info.state, err); + } + + +mDNSlocal void SetUpdateExpiration(mDNS *m, DNSMessage *msg, const mDNSu8 *end, uDNS_RegInfo *info) + { + LargeCacheRecord lcr; + const mDNSu8 *ptr; + int i; + mDNSu32 lease = 0; + + ptr = LocateAdditionals(msg, end); + + if (info->lease && (ptr = LocateAdditionals(msg, end))) + { + for (i = 0; i < msg->h.numAdditionals; i++) + { + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); + if (!ptr) break; + if (lcr.r.resrec.rrtype == kDNSType_OPT) + { + if (lcr.r.resrec.rdlength < LEASE_OPT_SIZE) continue; + if (lcr.r.resrec.rdata->u.opt.opt != kDNSOpt_Lease) continue; + lease = lcr.r.resrec.rdata->u.opt.OptData.lease; + break; + } + } + } + + if (lease > 0) + info->expire = (mDNSPlatformTimeNow() + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4); + else info->expire = -1; + } + +mDNSexport void uDNS_ReceiveMsg(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) + { + DNSQuestion *qptr; + AuthRecord *rptr; + ServiceRecordSet *sptr; + mStatus err = mStatus_NoError; + uDNS_GlobalInfo *u = &m->uDNS_info; + + mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + mDNSu8 UpdateR = kDNSFlag0_OP_Update | kDNSFlag0_QR_Response; + mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + mDNSu8 rcode = (mDNSu8)(msg->h.flags.b[1] & kDNSFlag1_RC); + + // unused + (void)srcaddr; + (void)srcport; + (void)dstaddr; + (void)dstport; + (void)ttl; + (void)InterfaceID; + + if (QR_OP == StdR) + { + // !!!KRS we should to a table lookup here to see if it answers an LLQ or a 1-shot + if (recvLLQResponse(m, msg, end, srcaddr, srcport, InterfaceID)) return; + + for (qptr = u->ActiveQueries; qptr; qptr = qptr->next) + { + //!!!KRS we should have a hashtable, hashed on message id + if (qptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) + { + if (msg->h.flags.b[0] & kDNSFlag0_TC) + { hndlTruncatedAnswer(qptr, srcaddr, m); return; } + else + { + u->CurrentQuery = qptr; + qptr->uDNS_info.responseCallback(m, msg, end, qptr, qptr->uDNS_info.context); + u->CurrentQuery = NULL; + // Note: responseCallback can invalidate qptr + return; + } + } + } + } + if (QR_OP == UpdateR) + { + for (sptr = u->ServiceRegistrations; sptr; sptr = sptr->next) + { + if (sptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) + { + err = checkUpdateResult(&sptr->RR_SRV.resrec.name, rcode, msg); + if (!err) SetUpdateExpiration(m, msg, end, &sptr->uDNS_info); + hndlServiceUpdateReply(m, sptr, err); + return; + } + } + for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next) + { + if (rptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) + { + err = checkUpdateResult(&rptr->resrec.name, rcode, msg); + if (!err) SetUpdateExpiration(m, msg, end, &rptr->uDNS_info); + hndlRecordUpdateReply(m, rptr, err); + return; + } + } + } + debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id)); + } + + +mDNSlocal void receiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, + const mDNSInterfaceID InterfaceID) + { + mDNSAddr *sa = NULL, *da = NULL; + mDNSIPPort sp, dp; + mDNSu8 ttl = 0; + + sp.NotAnInteger = 0; + dp.NotAnInteger = 0; + uDNS_ReceiveMsg(m, msg, end, sa, sp, da, dp, InterfaceID, ttl); + } + +//!!!KRS this should go away (don't just pick one randomly!) +mDNSlocal const mDNSAddr *getInitializedDNS(uDNS_GlobalInfo *u) + { + int i; + for (i = 0; i < 32; i++) + if (u->Servers[i].ip.v4.NotAnInteger) return &u->Servers[i]; + + return NULL; + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Query Routines +#endif + +#define sameID(x,y) mDNSPlatformMemSame(x,y,8) + +mDNSlocal void initializeQuery(DNSMessage *msg, DNSQuestion *question) + { + mDNSOpaque16 flags = QueryFlags; + + ubzero(msg, sizeof(msg)); + flags.b[0] |= kDNSFlag0_RD; // recursion desired + InitializeDNSMessage(&msg->h, question->uDNS_info.id, flags); + } + +mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestion *const question) + { + initializeQuery(msg, question); + + *endPtr = putQuestion(msg, msg->data, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); + if (!*endPtr) + { + LogMsg("ERROR: Unicast query out of space in packet"); + return mStatus_UnknownErr; + } + return mStatus_NoError; + } + +mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, DNSQuestion *question, LLQOptData *data, mDNSBool includeQuestion) + { + AuthRecord rr; + ResourceRecord *opt = &rr.resrec; + rdataOpt *optRD; + + //!!!KRS when we implement multiple llqs per message, we'll need to memmove anything past the question section + if (includeQuestion) + { + ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); + if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return NULL; } + } + // locate OptRR if it exists, set pointer to end + // !!!KRS implement me + + + // format opt rr (fields not specified are zero-valued) + ubzero(&rr, sizeof(AuthRecord)); + opt->rdata = &rr.rdatastorage; + + opt->RecordType = kDNSRecordTypeKnownUnique; // to avoid warnings in other layers + opt->rrtype = kDNSType_OPT; + opt->rdlength = LLQ_OPT_SIZE; + opt->rdestimate = LLQ_OPT_SIZE; + + optRD = &rr.resrec.rdata->u.opt; + optRD->opt = kDNSOpt_LLQ; + optRD->optlen = sizeof(LLQOptData); + umemcpy(&optRD->OptData.llq, data, sizeof(LLQOptData)); + ptr = PutResourceRecordTTL(msg, ptr, &msg->h.numAdditionals, opt, 0); + if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTL"); return NULL; } + + return ptr; + } + + +mDNSlocal mDNSBool getLLQAtIndex(mDNS *m, DNSMessage *msg, const mDNSu8 *end, LLQOptData *llq, int index) + { + LargeCacheRecord lcr; + int i; + const mDNSu8 *ptr; + + ptr = LocateAdditionals(msg, end); + if (!ptr) return mDNSfalse; + + // find the last additional + for (i = 0; i < msg->h.numAdditionals; i++) +// { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); if (!ptr) return mDNSfalse; } +//!!!KRS workaround for LH server bug, which puts OPT as first additional + { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); if (!ptr) return mDNSfalse; if (lcr.r.resrec.rrtype == kDNSType_OPT) break; } + if (lcr.r.resrec.rrtype != kDNSType_OPT) return mDNSfalse; + if (lcr.r.resrec.rdlength < (index + 1) * LLQ_OPT_SIZE) return mDNSfalse; // rdata too small + umemcpy(llq, (mDNSu8 *)&lcr.r.resrec.rdata->u.opt.OptData.llq + (index * sizeof(LLQOptData)), sizeof(LLQOptData)); + return mDNStrue; + } + +mDNSlocal void recvRefreshReply(mDNS *m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *q) + { + LLQ_Info *qInfo; + LLQOptData pktData; + + qInfo = q->uDNS_info.llq; + if (!getLLQAtIndex(m, msg, end, &pktData, 0)) { LogMsg("ERROR recvRefreshReply - getLLQAtIndex"); return; } + if (pktData.llqOp != kLLQ_Refresh) return; + if (!sameID(pktData.id, qInfo->id)) { LogMsg("recvRefreshReply - ID mismatch. Discarding"); return; } + if (pktData.err != LLQErr_NoError) { LogMsg("recvRefreshReply: received error %d from server", pktData.err); return; } + + qInfo->expire = mDNSPlatformTimeNow() + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond); + qInfo->retry = qInfo->expire + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond * 3/4); + + qInfo->origLease = pktData.lease; + qInfo->state = LLQ_Established; + } + +mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease) + { + DNSMessage msg; + mDNSu8 *end; + LLQOptData llq; + LLQ_Info *info = q->uDNS_info.llq; + mStatus err; + + if (info->state == kLLQ_Refresh && info->ntries >= kLLQ_MAX_TRIES) + { + LogMsg("sendLLQRefresh - %d failed attempts for llq %s", info->ntries, q->qname.c); + info->state = LLQ_Retry; + info->retry = mDNSPlatformTimeNow() + kLLQ_DEF_RETRY * mDNSPlatformOneSecond; + info->deriveRemovesOnResume = mDNStrue; + return; + //!!!KRS handle this - periodically try to re-establish + } + + llq.vers = kLLQ_Vers; + llq.llqOp = kLLQ_Refresh; + llq.err = LLQErr_NoError; + umemcpy(llq.id, info->id, 8); + llq.lease = lease; + + initializeQuery(&msg, q); + end = putLLQ(&msg, msg.data, q, &llq, mDNStrue); + if (!end) { LogMsg("ERROR: sendLLQRefresh - putLLQ"); return; } + + err = mDNSSendDNSMessage(m, &msg, end, q->InterfaceID, &info->servAddr, info->servPort); + if (err) LogMsg("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %d", err); + + if (info->state == LLQ_Established) info->ntries = 1; + else info->ntries++; + info->state = LLQ_Refresh; + q->LastQTime = mDNSPlatformTimeNow(); + info->retry = (info->expire - q->LastQTime) / 2; + } + +mDNSlocal void recvLLQEvent(mDNS *m, DNSQuestion *q, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, mDNSInterfaceID InterfaceID) + { + DNSMessage ack; + mDNSu8 *ackEnd = ack.data; + mStatus err; + + // invoke response handler + m->uDNS_info.CurrentQuery = q; + q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); + if (m->uDNS_info.CurrentQuery != q) return; + + // format and send ack + InitializeDNSMessage(&ack.h, msg->h.id, ResponseFlags); + ackEnd = putQuestion(&ack, ack.data, ack.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (!ackEnd) { LogMsg("ERROR: recvLLQEvent - putQuestion"); return; } + err = mDNSSendDNSMessage(m, &ack, ackEnd, InterfaceID, srcaddr, srcport); + if (err) LogMsg("ERROR: recvLLQEvent - mDNSSendDNSMessage returned %d", err); + } + + + +mDNSlocal void hndlChallengeResponseAck(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q) + { + LLQ_Info *info = q->uDNS_info.llq; + + if (llq->err) { LogMsg("hndlChallengeResponseAck - received error %d from server", llq->err); goto error; } + if (!sameID(info->id, llq->id)) { LogMsg("hndlChallengeResponseAck - ID changed. discarding"); return; } // this can happen rarely (on packet loss + reordering) + info->expire = mDNSPlatformTimeNow() + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); + info->retry = info->expire + ((mDNSs32)llq->lease * mDNSPlatformOneSecond * 3/4); + + info->origLease = llq->lease; + info->state = LLQ_Established; + q->uDNS_info.responseCallback = llqResponseHndlr; + llqResponseHndlr(m, pktMsg, end, q, NULL); + return; + + error: + info->state = LLQ_Error; + } + +mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) + { + LLQ_Info *info = q->uDNS_info.llq; + DNSMessage response; + mDNSu8 *responsePtr = response.data; + mStatus err; + LLQOptData llqBuf; + mDNSs32 timenow = mDNSPlatformTimeNow(); + + if (info->ntries++ == kLLQ_MAX_TRIES) + { + LogMsg("sendChallengeResponse: %d failed attempts for LLQ %s. Will re-try in %d minutes", + kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60); + info->state = LLQ_Retry; + info->retry = timenow + (kLLQ_DEF_RETRY * mDNSPlatformOneSecond); + // !!!KRS give a callback error in these cases? + return; + } + + + if (!llq) + { + llq = &llqBuf; + llq->vers = kLLQ_Vers; + llq->llqOp = kLLQ_Setup; + llq->err = LLQErr_NoError; + umemcpy(llq->id, info->id, 8); + llq->lease = info->origLease; + } + + q->LastQTime = timenow; + info->retry = timenow + (kLLQ_INIT_RESEND * info->ntries * mDNSPlatformOneSecond); + + if (constructQueryMsg(&response, &responsePtr, q)) goto error; + responsePtr = putLLQ(&response, responsePtr, q, llq, mDNSfalse); + if (!responsePtr) { LogMsg("ERROR: sendChallengeResponse - putLLQ"); goto error; } + + err = mDNSSendDNSMessage(m, &response, responsePtr, q->InterfaceID, &info->servAddr, info->servPort); + if (err) LogMsg("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %d", err); + // on error, we procede as normal and retry after the appropriate interval + + return; + + error: + info->state = LLQ_Error; + } + + + +mDNSlocal void hndlRequestChallenge(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q) + { + LLQ_Info *info = q->uDNS_info.llq; + mDNSs32 timenow = mDNSPlatformTimeNow(); + switch(llq->err) + { + case LLQErr_NoError: break; + case LLQErr_ServFull: + LogMsg("hndlRequestChallenge - received ServFull from server for LLQ %s. Retry in %d sec", q->qname.c, llq->lease); + info->retry = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); + info->state = LLQ_Retry; + simpleResponseHndlr(m, pktMsg, end, q, NULL); // get available answers + info->deriveRemovesOnResume = mDNStrue; + case LLQErr_Static: + info->state = LLQ_Static; + LogMsg("LLQ %s: static", q->qname.c); + simpleResponseHndlr(m, pktMsg, end, q, NULL); + return; + case LLQErr_FormErr: + LogMsg("ERROR: hndlRequestChallenge - received FormErr from server for LLQ %s", q->qname.c); + goto error; + case LLQErr_BadVers: + LogMsg("ERROR: hndlRequestChallenge - received BadVers from server"); + goto error; + case LLQErr_UnknownErr: + LogMsg("ERROR: hndlRequestChallenge - received UnknownErr from server for LLQ %s", q->qname.c); + goto error; + default: + LogMsg("ERROR: hndlRequestChallenge - received invalid error %d for LLQ %s", llq->err, q->qname.c); + goto error; + } + + if (info->origLease != llq->lease) + LogMsg("hndlRequestChallenge: requested lease %d, granted lease %d", info->origLease, llq->lease); + + // cache expiration in case we go to sleep before finishing setup + info->origLease = llq->lease; + info->expire = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); + + // update state and timestamp + info->state = LLQ_SecondaryRequest; + umemcpy(info->id, llq->id, 8); + info->ntries = 0; // first attempt to send response + + sendChallengeResponse(m, q, llq); + return; + + + error: + info->state = LLQ_Error; + } + + +// response handler for initial and secondary setup responses +mDNSlocal void recvSetupResponse(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, DNSQuestion *q, void *clientContext) + { + DNSQuestion pktQuestion; + LLQOptData llq; + const mDNSu8 *ptr = pktMsg->data; + LLQ_Info *info = q->uDNS_info.llq; + mDNSu8 rcode = (mDNSu8)(pktMsg->h.flags.b[1] & kDNSFlag1_RC); + + (void)clientContext; // unused + + if (rcode && rcode != kDNSFlag1_RC_NXDomain) + { + LogMsg("LLQ Setup for %s failed with rcode %d. Reverting to polling mode", q->qname.c, rcode); + info->state = LLQ_Poll; + q->uDNS_info.responseCallback = simpleResponseHndlr; + q->LastQTime = mDNSPlatformTimeNow(); + q->ThisQInterval = 1; + return; + } + + ptr = getQuestion(pktMsg, ptr, end, 0, &pktQuestion); + if (!ptr) { LogMsg("ERROR: recvSetupResponse - getQuestion"); goto error; } + if (!SameDomainName(&q->qname, &pktQuestion.qname)) + { LogMsg("ERROR: recvSetupResponse - mismatched question in response for llq setup %s", q->qname.c); goto error; } + + if (!getLLQAtIndex(m, pktMsg, end, &llq, 0)) { LogMsg("ERROR: recvSetupResponse - GetLLQAtIndex"); goto error; } + if (llq.llqOp != kLLQ_Setup) { LogMsg("ERROR: recvSetupResponse - bad op %d", llq.llqOp); goto error; } + if (llq.vers != kLLQ_Vers) { LogMsg("ERROR: recvSetupResponse - bad vers %d", llq.vers); goto error; } + + if (info->state == LLQ_InitialRequest) { hndlRequestChallenge(m, pktMsg, end, &llq, q); return; } + if (info->state == LLQ_SecondaryRequest) { hndlChallengeResponseAck(m, pktMsg, end, &llq, q); return; } + LogMsg("recvSetupResponse - bad state %d", info->state); + + + error: + info->state = LLQ_Error; + } + + + +mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info) + { + DNSMessage msg; + mDNSu8 *end; + LLQOptData llqData; + DNSQuestion *q = info->question; + mStatus err; + mDNSs32 timenow = mDNSPlatformTimeNow(); + + if (info->ntries++ == kLLQ_MAX_TRIES) + { + LogMsg("startLLQHandshake: %d failed attempts for LLQ %s. Will re-try in %d minutes", + kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60); + info->state = LLQ_Retry; + info->retry = timenow + (kLLQ_DEF_RETRY * mDNSPlatformOneSecond); + // !!!KRS give a callback error in these cases? + return; + } + + // set llq rdata + llqData.vers = kLLQ_Vers; + llqData.llqOp = kLLQ_Setup; + llqData.err = LLQErr_NoError; + ubzero(llqData.id, 8); + llqData.lease = kLLQ_DefLease; + + initializeQuery(&msg, q); + end = putLLQ(&msg, msg.data, q, &llqData, mDNStrue); + if (!end) + { + LogMsg("ERROR: startLLQHandshake - putLLQ"); + info->state = LLQ_Error; + return; + } + + err = mDNSSendDNSMessage(m, &msg, end, q->InterfaceID, &info->servAddr, info->servPort); + if (err) LogMsg("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %d", err); + // on error, we procede as normal and retry after the appropriate interval + + // update question/info state + info->state = LLQ_InitialRequest; + info->origLease = kLLQ_DefLease; + info->retry = timenow + (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); + q->LastQTime = timenow; + q->uDNS_info.responseCallback = recvSetupResponse; + q->uDNS_info.internal = mDNStrue; + } + +// wrapper for startLLQHandshake, invoked by async op callback +mDNSlocal void startLLQHandshakeCallback(mStatus err, mDNS *const m, void *llqInfo, const AsyncOpResult *result) + { + LLQ_Info *info = (LLQ_Info *)llqInfo; + const zoneData_t *zoneInfo = &result->zoneData; + + if (err) + { + LogMsg("ERROR: startLLQHandshakeCallback invoked with error code %d", err); + info->state = LLQ_Poll; + info->question->LastQTime = 0; // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + return; + } + + if (info->state == LLQ_Cancelled) + { + // StopQuery was called while we were getting the zone info + LogMsg("startLLQHandshake - LLQ Cancelled."); + info->question = NULL; // question may be deallocated + ufree(info); + return; + } + + if (info->state != LLQ_GetZoneInfo) + { + LogMsg("ERROR: startLLQHandshake - bad state %d", info->state); + return; + } + + // cache necessary zone data + info->servAddr.type = zoneInfo->primaryAddr.type; + info->servAddr.ip.v4.NotAnInteger = zoneInfo->primaryAddr.ip.v4.NotAnInteger; + info->servPort.NotAnInteger = zoneInfo->llqPort.NotAnInteger; + info->ntries = 0; + startLLQHandshake(m, info); + } + +mDNSlocal mStatus startLLQ(mDNS *m, DNSQuestion *question) + { + LLQ_Info *info; + mStatus err = mStatus_NoError; + + // allocate / init info struct + info = umalloc(sizeof(LLQ_Info)); + if (!info) { LogMsg("ERROR: startLLQ - malloc"); return mStatus_NoMemoryErr; } + ubzero(info, sizeof(LLQ_Info)); + info->state = LLQ_GetZoneInfo; + + // link info/question + info->question = question; + question->uDNS_info.llq = info; + + question->uDNS_info.responseCallback = llqResponseHndlr; + + err = startGetZoneData(&question->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, info); + if (err) + { + LogMsg("ERROR: startLLQ - startGetZoneData returned %d", err); + info->question = NULL; + ufree(info); + question->uDNS_info.llq = NULL; + return err; + } + + LinkActiveQuestion(&m->uDNS_info, question); + return err; + } + +mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID) + { + DNSQuestion pktQ, *q; + uDNS_GlobalInfo *u = &m->uDNS_info; + const mDNSu8 *ptr = msg->data; + LLQ_Info *llqInfo; + + if (!msg->h.numQuestions) return mDNSfalse; + + ptr = getQuestion(msg, ptr, end, 0, &pktQ); + if (!ptr) return mDNSfalse; + pktQ.uDNS_info.id = msg->h.id; + + // !!!KRS we should do a table lookup to quickly determine if this packet is for an LLQ + + q = u->ActiveQueries; + while (q) + { + llqInfo = q->uDNS_info.llq; + if (q->LongLived && + llqInfo && + q->qnamehash == pktQ.qnamehash && + q->qtype == pktQ.qtype && + SameDomainName(&q->qname, &pktQ.qname)) + { + u->CurrentQuery = q; + if (llqInfo->state == LLQ_Established || (llqInfo->state == LLQ_Refresh && msg->h.numAnswers)) + { recvLLQEvent(m, q, msg, end, srcaddr, srcport, InterfaceID); return mDNStrue; } + else if (msg->h.id.NotAnInteger != q->uDNS_info.id.NotAnInteger) + { q = q->next; continue; } + else if (llqInfo->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers) + { recvRefreshReply(m, msg, end, q); return mDNStrue; } + else if (llqInfo->state < LLQ_Static) + { q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); return mDNStrue; } + } + q = q->next; + } + return mDNSfalse; + } + +mDNSexport mDNSBool IsActiveUnicastQuery(DNSQuestion *const question, uDNS_GlobalInfo *u) + { + DNSQuestion *q; + + for (q = u->ActiveQueries; q; q = q->next) + { + if (q == question) + { + if (!question->uDNS_info.id.NotAnInteger || question->InterfaceID || IsLocalDomain(&question->qname)) + LogMsg("Warning: Question %s in Active Unicast Query list with id %d, interfaceID %x", + question->qname.c, question->uDNS_info.id.NotAnInteger, question->InterfaceID); + return mDNStrue; + } + } + return mDNSfalse; + } + +// stopLLQ happens IN ADDITION to stopQuery +mDNSlocal void stopLLQ(mDNS *m, DNSQuestion *question) + { + LLQ_Info *info = question->uDNS_info.llq; + (void)m; // unused + + if (!question->LongLived) { LogMsg("ERROR: stopLLQ - LongLived flag not set"); return; } + if (!info) { LogMsg("ERROR: stopLLQ - llq info is NULL"); return; } + + switch (info->state) + { + case LLQ_UnInit: + LogMsg("ERROR: stopLLQ - state LLQ_UnInit"); + //!!!KRS should we unlink info<->question here? + return; + case LLQ_GetZoneInfo: + info->question = NULL; // remove ref to question, as it may be freed when we get called back from async op + info->state = LLQ_Cancelled; + return; + case LLQ_Established: + case LLQ_Refresh: + // refresh w/ lease 0 + sendLLQRefresh(m, question, 0); + goto free_info; + default: + debugf("stopLLQ - silently discarding LLQ in state %d", info->state); + goto free_info; + } + + free_info: + info->question = NULL; + ufree(info); + question->uDNS_info.llq = NULL; + + } + +mDNSexport mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSQuestion *qptr, *prev = NULL; + CacheRecord *ka; + + qptr = u->ActiveQueries; + while (qptr) + { + if (qptr == question) + { + if (question->LongLived && question->uDNS_info.llq) + stopLLQ(m, question); + if (m->uDNS_info.CurrentQuery == question) + m->uDNS_info.CurrentQuery = m->uDNS_info.CurrentQuery->next; + while (question->uDNS_info.knownAnswers) + { + ka = question->uDNS_info.knownAnswers; + question->uDNS_info.knownAnswers = question->uDNS_info.knownAnswers->next; + ufree(ka); + } + if (prev) prev->next = question->next; + else u->ActiveQueries = question->next; + return mStatus_NoError; + } + prev = qptr; + qptr = qptr->next; + } + LogMsg("uDNS_StopQuery: no such active query (%s)", question->qname.c); + return mStatus_UnknownErr; + } + +mDNSexport void uDNS_SuspendLLQs(mDNS *m) + { + DNSQuestion *q; + LLQ_Info *llq; + for (q = m->uDNS_info.ActiveQueries; q; q = q->next) + { + llq = q->uDNS_info.llq; + if (q->LongLived && llq && llq->state < LLQ_Suspended) + { + if (llq->state == LLQ_Established || llq->state == LLQ_Refresh) + sendLLQRefresh(m, q, 0); + // note that we suspend LLQs in setup states too + if (llq->state != LLQ_Retry) llq->state = LLQ_Suspended; + } + } + } + +extern void uDNS_RestartLLQs(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSQuestion *q; + LLQ_Info *llqInfo; + + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) + { + q = u->CurrentQuery; + u->CurrentQuery = u->CurrentQuery->next; + llqInfo = q->uDNS_info.llq; + if (q->LongLived && llqInfo && llqInfo->state == LLQ_Suspended) + { llqInfo->ntries = 0; llqInfo->deriveRemovesOnResume = mDNStrue; startLLQHandshake(m, llqInfo); } + } + } + + +mDNSlocal mStatus startQuery(mDNS *const m, DNSQuestion *const question, mDNSBool internal) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSMessage msg; + mDNSu8 *endPtr; + mStatus err = mStatus_NoError; + const mDNSAddr *server; + + //!!!KRS we should check if the question is already in our acivequestion list + if (!ValidateDomainName(&question->qname)) + { + LogMsg("Attempt to start query with invalid qname %##s %s", question->qname.c, DNSTypeName(question->qtype)); + return mStatus_Invalid; + } + + question->next = NULL; + question->qnamehash = DomainNameHashValue(&question->qname); // to do quick domain name comparisons + question->uDNS_info.id = newMessageID(u); + + // break here if its and LLQ + if (question->LongLived) return startLLQ(m, question); + + err = constructQueryMsg(&msg, &endPtr, question); + if (err) return err; + + // else send the query to our server + + question->LastQTime = mDNSPlatformTimeNow(); + question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + // store the question/id in active question list + question->uDNS_info.timestamp = question->LastQTime; + question->uDNS_info.internal = internal; + LinkActiveQuestion(u, question); + question->uDNS_info.knownAnswers = NULL; + + server = getInitializedDNS(u); + if (!server) { LogMsg("startQuery - no initialized DNS"); err = mStatus_NotInitializedErr; } + else err = mDNSSendDNSMessage(m, &msg, endPtr, question->InterfaceID, server, UnicastDNSPort); + if (err) { LogMsg("ERROR: startQuery - %d (keeping question in list for retransmission", err); } + + return err; + } + +mDNSexport mStatus uDNS_StartQuery(mDNS *const m, DNSQuestion *const question) + { + ubzero(&question->uDNS_info, sizeof(uDNS_QuestionInfo)); + question->uDNS_info.responseCallback = simpleResponseHndlr; + question->uDNS_info.context = NULL; + return startQuery(m, question, 0); + } + +// explicitly set response handler +mDNSlocal mStatus startInternalQuery(DNSQuestion *q, mDNS *m, InternalResponseHndlr callback, void *hndlrContext) + { + ubzero(&q->uDNS_info, sizeof(uDNS_QuestionInfo)); + q->QuestionContext = hndlrContext; + q->uDNS_info.responseCallback = callback; + q->uDNS_info.context = hndlrContext; + return startQuery(m, q, 1); + } + + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Domain -> Name Server Conversion +#endif + + +/* startGetZoneData + * + * asyncronously find the address of the nameserver for the enclosing zone for a given domain name, + * i.e. the server to which update and LLQ requests will be sent for a given name. Once the address is + * derived, it will be passed to the callback, along with a context pointer. If the zone cannot + * be determined or if an error occurs, an all-zeros address will be passed and a message will be + * written to the syslog. + * + * If the FindUpdatePort arg is set, the port on which the server accepts dynamic updates is determined + * by querying for the _update._dns-sd._udp.. SRV record. Likewise, if the FindLLQPort arg is set, + * the port on which the server accepts long lived queries is determined by querying for _llq._dns-sd. + * _udp.. record. If either of these queries fail, or flags are not specified, the llqPort and + * updatePort fields in the result structure are set to zero. + * + * Steps for deriving the zone name are as follows: + * + * Query for an SOA record for the required domain. If we don't get an answer (or an SOA in the Authority + * section), we strip the leading label from the name and repeat, until we get an answer. + * + * The name of the SOA record is our enclosing zone. The mname field in the SOA rdata is the domain + * name of the primary NS. + * + * We verify that there is an NS record with this zone for a name and the mname for its rdata. + * (!!!KRS this seems redundant, but BIND does this, and it should normally be zero-overhead since + * the NS query will get us address records in the additionals section, which we'd otherwise have to + * explicitly query for.) + * + * We then query for the address record for this nameserver (if it is not in the addionals section of + * the NS record response.) + */ + + +// state machine types and structs +// + +// state machine states +typedef enum + { + init, + lookupSOA, + foundZone, + lookupNS, + foundNS, + lookupA, + foundA, + lookupPort, + foundPort, + complete + } ntaState; + +// state machine actions +typedef enum + { + smContinue, // continue immediately to next state + smBreak, // break until next packet/timeout + smError // terminal error - cleanup and abort + } smAction; + +typedef struct + { + domainname origName; // name we originally try to convert + domainname *curSOA; // name we have an outstanding SOA query for + ntaState state; // determines what we do upon receiving a packet + mDNS *m; + domainname zone; // left-hand-side of SOA record + mDNSu16 zoneClass; + domainname ns; // mname in SOA rdata, verified in confirmNS state + mDNSv4Addr addr; // address of nameserver + DNSQuestion question; // storage for any active question + DNSQuestion extraQuestion; // additional storage + mDNSBool questionActive; // if true, StopQuery() can be called on the question field + mDNSBool findUpdatePort; + mDNSBool findLLQPort; + mDNSIPPort updatePort; + mDNSIPPort llqPort; + AsyncOpCallback *callback; // caller specified function to be called upon completion + void *callbackInfo; + } ntaContext; + + +// function prototypes (for routines that must be used as fn pointers prior to their definitions, +// and allows states to be read top-to-bottom in logical order) +mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *contextPtr); +mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext *context); +mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr); +mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *context); +mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext *context); +mDNSlocal smAction hndlLookupPorts(DNSMessage *msg, const mDNSu8 *end, ntaContext *context); + +// initialization +mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort, + AsyncOpCallback callback, void *callbackInfo) + { + ntaContext *context = (ntaContext*)umalloc(sizeof(ntaContext)); + if (!context) { LogMsg("ERROR: startGetZoneData - umalloc failed"); return mStatus_NoMemoryErr; } + ubzero(context, sizeof(ntaContext)); + ustrcpy(context->origName.c, name->c); + context->state = init; + context->m = m; + context->callback = callback; + context->callbackInfo = callbackInfo; + context->findUpdatePort = findUpdatePort; + context->findLLQPort = findLLQPort; + getZoneData(m, NULL, NULL, NULL, context); + return mStatus_NoError; + } + +// state machine entry routine +mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *contextPtr) + { + AsyncOpResult result; + ntaContext *context = (ntaContext*)contextPtr; + smAction action; + + // unused + (void)m; + (void)question; + + // stop any active question + if (context->questionActive) + { + uDNS_StopQuery(context->m, &context->question); + context->questionActive = mDNSfalse; + } + + if (msg && msg->h.flags.b[2] >> 4 && msg->h.flags.b[2] >> 4 != kDNSFlag1_RC_NXDomain) + { + // rcode non-zero, non-nxdomain + LogMsg("ERROR: getZoneData - received response w/ rcode %d", msg->h.flags.b[2] >> 4); + goto error; + } + + switch (context->state) + { + case init: + case lookupSOA: + action = hndlLookupSOA(msg, end, context); + if (action == smError) goto error; + if (action == smBreak) return; + case foundZone: + case lookupNS: + action = confirmNS(msg, end, context); + if (action == smError) goto error; + if (action == smBreak) return; + case foundNS: + case lookupA: + action = lookupNSAddr(msg, end, context); + if (action == smError) goto error; + if (action == smBreak) return; + case foundA: + if (!context->findUpdatePort && !context->findLLQPort) + { + context->state = complete; + break; + } + case lookupPort: + action = hndlLookupPorts(msg, end, context); + if (action == smError) goto error; + if (action == smBreak) return; + if (action == smContinue) context->state = complete; + case foundPort: + case complete: break; + } + + if (context->state != complete) + { + LogMsg("ERROR: getZoneData - exited state machine with state %d", context->state); + goto error; + } + + result.type = zoneDataResult; + result.zoneData.primaryAddr.ip.v4.NotAnInteger = context->addr.NotAnInteger; + result.zoneData.primaryAddr.type = mDNSAddrType_IPv4; + ustrcpy(result.zoneData.zoneName.c, context->zone.c); + result.zoneData.zoneClass = context->zoneClass; + result.zoneData.llqPort = context->findLLQPort ? context->llqPort : zeroIPPort; + result.zoneData.updatePort = context->findUpdatePort ? context->updatePort : zeroIPPort; + context->callback(mStatus_NoError, context->m, context->callbackInfo, &result); + goto cleanup; + +error: + if (context && context->callback) + context->callback(mStatus_UnknownErr, context->m, context->callbackInfo, NULL); +cleanup: + if (context && context->questionActive) + { + uDNS_StopQuery(context->m, &context->question); + context->questionActive = mDNSfalse; + } + if (context) ufree(context); + } + +mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext *context) + { + mStatus err; + LargeCacheRecord lcr; + ResourceRecord *rr = &lcr.r.resrec; + DNSQuestion *query = &context->question; + const mDNSu8 *ptr; + + if (msg) + { + // if msg contains SOA record in answer or authority sections, update context/state and return + int i; + ptr = LocateAnswers(msg, end); + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { LogMsg("ERROR: hndlLookupSOA, Answers - GetLargeResourceRecord returned NULL"); return smError; } + if (rr->rrtype == kDNSType_SOA && SameDomainName(context->curSOA, &rr->name)) + { + processSOA(context, rr); + return smContinue; + } + } + ptr = LocateAuthorities(msg, end); + // SOA not in answers, check in authority + for (i = 0; i < msg->h.numAuthorities; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); ///!!!KRS using type PacketAns for auth + if (!ptr) { LogMsg("ERROR: hndlLookupSOA, Authority - GetLargeResourceRecord returned NULL"); return smError; } + if (rr->rrtype == kDNSType_SOA) + { + processSOA(context, rr); + return smContinue; + } + } + } + + if (context->state != init && !context->curSOA->c[0]) + { + // we've gone down to the root and have not found an SOA + LogMsg("ERROR: hndlLookupSOA - recursed to root label of %s without finding SOA", + context->origName.c); + return smError; + } + + ubzero(query, sizeof(DNSQuestion)); + // chop off leading label unless this is our first try + if (context->state == init) context->curSOA = &context->origName; + else context->curSOA = (domainname *)(context->curSOA->c + context->curSOA->c[0]+1); + + context->state = lookupSOA; + ustrcpy(query->qname.c, context->curSOA->c); + query->qtype = kDNSType_SOA; + query->qclass = kDNSClass_IN; + err = startInternalQuery(query, context->m, getZoneData, context); + context->questionActive = mDNStrue; + if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %d (breaking until next periodic retransmission)", err); + + return smBreak; // break from state machine until we receive another packet + } + +mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr) + { + ustrcpy(context->zone.c, rr->name.c); + context->zoneClass = rr->rrclass; + ustrcpy(context->ns.c, rr->rdata->u.soa.mname.c); + context->state = foundZone; + } + + +mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *context) + { + DNSQuestion *query = &context->question; + mStatus err; + LargeCacheRecord lcr; + ResourceRecord *rr = &lcr.r.resrec; + const mDNSu8 *ptr; + int i; + + if (context->state == foundZone) + { + // we've just learned the zone. confirm that an NS record exists + ustrcpy(query->qname.c, context->zone.c); + query->qtype = kDNSType_NS; + query->qclass = kDNSClass_IN; + err = startInternalQuery(query, context->m, getZoneData, context); + context->questionActive = mDNStrue; + if (err) LogMsg("confirmNS: startInternalQuery returned error %d (breaking until next periodic retransmission", err); + context->state = lookupNS; + return smBreak; // break from SM until we receive another packet + } + else if (context->state == lookupNS) + { + ptr = LocateAnswers(msg, end); + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { LogMsg("ERROR: confirmNS, Answers - GetLargeResourceRecord returned NULL"); return smError; } + if (rr->rrtype == kDNSType_NS && + SameDomainName(&context->zone, &rr->name) && SameDomainName(&context->ns, &rr->rdata->u.name)) + { + context->state = foundNS; + return smContinue; // next routine will examine additionals section of A record + } + } + LogMsg("ERROR: could not confirm existance of NS record %s", context->zone.c); + return smError; + } + else { LogMsg("ERROR: confirmNS - bad state %d", context->state); return smError; } + } + +mDNSlocal smAction queryNSAddr(ntaContext *context) + { + mStatus err; + DNSQuestion *query = &context->question; + + ustrcpy(query->qname.c, context->ns.c); + query->qtype = kDNSType_A; + query->qclass = kDNSClass_IN; + err = startInternalQuery(query, context->m, getZoneData, context); + context->questionActive = mDNStrue; + if (err) LogMsg("confirmNS: startInternalQuery returned error %d (breaking until next periodic retransmission)", err); + context->state = lookupA; + return smBreak; + } + +mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext *context) + { + const mDNSu8 *ptr; + int i; + LargeCacheRecord lcr; + ResourceRecord *rr = &lcr.r.resrec; + + if (context->state == foundNS) + { + // we just found the NS record - look for the corresponding A record in the Additionals section + if (!msg->h.numAdditionals) return queryNSAddr(context); + ptr = LocateAdditionals(msg, end); + if (!ptr) + { + LogMsg("ERROR: lookupNSAddr - LocateAdditionals returned NULL, expected %d additionals", msg->h.numAdditionals); + return queryNSAddr(context); + } + else + { + for (i = 0; i < msg->h.numAdditionals; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) + { + LogMsg("ERROR: lookupNSAddr, Additionals - GetLargeResourceRecord returned NULL"); + return queryNSAddr(context); + } + if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, &rr->name)) + { + context->addr.NotAnInteger = rr->rdata->u.ip.NotAnInteger; + context->state = foundA; + return smContinue; + } + } + } + // no A record in Additionals - query the server + return queryNSAddr(context); + } + else if (context->state == lookupA) + { + ptr = LocateAnswers(msg, end); + if (!ptr) { LogMsg("ERROR: lookupNSAddr: LocateAnswers returned NULL"); return smError; } + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { LogMsg("ERROR: lookupNSAddr, Answers - GetLargeResourceRecord returned NULL"); break; } + if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, &rr->name)) + { + context->addr.NotAnInteger = rr->rdata->u.ip.NotAnInteger; + context->state = foundA; + return smContinue; + } + } + LogMsg("ERROR: lookupNSAddr: Address record not found in answer section"); + return smError; + } + else { LogMsg("ERROR: lookupNSAddr - bad state %d", context->state); return smError; } + } + +mDNSlocal smAction lookupDNSPort(DNSMessage *msg, const mDNSu8 *end, ntaContext *context, char *portName, mDNSIPPort *port) + { + int i; + LargeCacheRecord lcr; + const mDNSu8 *ptr; + DNSQuestion *q; + mStatus err; + + if (context->state == lookupPort) // we've already issued the query + { + if (!msg) { LogMsg("ERROR: hndlLookupUpdatePort - NULL message"); return smError; } + ptr = LocateAnswers(msg, end); + for (i = 0; i < msg->h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { LogMsg("ERROR: hndlLookupUpdatePort - GetLargeResourceRecord returned NULL"); return smError; } + if (ResourceRecordAnswersQuestion(&lcr.r.resrec, &context->question)) + { + port->NotAnInteger = lcr.r.resrec.rdata->u.srv.port.NotAnInteger; + context->state = foundPort; + return smContinue; + } + } + LogMsg("hndlLookupUpdatePort %s - answer not contained in reply. Guessing port %d", portName, UnicastDNSPort); + *port = UnicastDNSPort; + context->state = foundPort; + return smContinue; + } + + // query the server for the update port for the zone + context->state = lookupPort; + q = &context->question; + MakeDomainNameFromDNSNameString(&q->qname, portName); + ustrcpy((q->qname.c + ustrlen(q->qname.c)), context->zone.c); + q->qtype = kDNSType_SRV; + q->qclass = kDNSClass_IN; + err = startInternalQuery(q, context->m, getZoneData, context); + context->questionActive = mDNStrue; + if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %d (breaking until next periodic retransmission)", err); + return smBreak; // break from state machine until we receive another packet + } + +mDNSlocal smAction hndlLookupPorts(DNSMessage *msg, const mDNSu8 *end, ntaContext *context) + { + smAction action; + + if (context->findUpdatePort && !context->updatePort.NotAnInteger) + { + action = lookupDNSPort(msg, end, context, UPDATE_PORT_NAME, &context->updatePort); + if (action != smContinue) return action; + } + if (context->findLLQPort && !context->llqPort.NotAnInteger) + return lookupDNSPort(msg, end, context, LLQ_PORT_NAME, &context->llqPort); + + return smContinue; + } + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Truncation Handling +#endif + +typedef struct + { + DNSQuestion *question; + DNSMessage reply; + mDNSu16 replylen; + int nread; + mDNS *m; + } tcpInfo_t; + +// issue queries over a conected socket +mDNSlocal void conQueryCallback(int sd, void *context, mDNSBool ConnectionEstablished) + { + mStatus err = 0; + char msgbuf[356]; // 96 (hdr) + 256 (domain) + 4 (class/type) + DNSMessage *msg; + mDNSu8 *end; + tcpInfo_t *info = (tcpInfo_t *)context; + DNSQuestion *question = info->question; + int n; + + question->uDNS_info.id.NotAnInteger = (mDNSu16)~0; + + if (ConnectionEstablished) + { + // connection is established - send the message + msg = (DNSMessage *)&msgbuf; + err = constructQueryMsg(msg, &end, question); + if (err) { LogMsg("ERROR: conQueryCallback: constructQueryMsg - %d", err); goto error; } + err = mDNSSendDNSMessage_tcp(info->m, msg, end, sd); + if (err) { LogMsg("ERROR: conQueryCallback: mDNSSendDNSMessage_tcp - %d", err); goto error; } + return; + } + else + { + if (!info->nread) + { + // read msg len + n = mDNSPlatformReadTCP(sd, &info->replylen, 2); + if (n != 2) + { + LogMsg("ERROR:conQueryCallback - attempt to read message length failed (read returned %d)", n); + goto error; + } + } + n = mDNSPlatformReadTCP(sd, ((char *)&info->reply) + info->nread, info->replylen - info->nread); + if (n < 0) { LogMsg("ERROR: conQueryCallback - read returned %d", n); goto error; } + info->nread += n; + if (info->nread == info->replylen) + { + // finished reading message + receiveMsg(info->m, &info->reply, ((mDNSu8 *)&info->reply) + info->replylen, question->InterfaceID); + mDNSPlatformTCPCloseConnection(sd); + ufree(info); + return; + } + else return; + } + return; + + error: + mDNSPlatformTCPCloseConnection(sd); + ufree(info); + } + +mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m) + { + mStatus connectionStatus; + uDNS_QuestionInfo *info = &question->uDNS_info; + int sd; + tcpInfo_t *context; + + context = (tcpInfo_t *)umalloc(sizeof(tcpInfo_t)); + if (!context) { LogMsg("ERROR: hndlTruncatedAnswer - memallocate failed"); return; } + ubzero(context, sizeof(tcpInfo_t)); + context->question = question; + context->m = m; + + info->id.NotAnInteger = (mDNSu16)~0; // all 1's indicates TCP queries + info->timestamp = mDNSPlatformTimeNow(); // reset timestamp + + connectionStatus = mDNSPlatformTCPConnect(src, UnicastDNSPort, question->InterfaceID, conQueryCallback, context, &sd); + if (connectionStatus == mStatus_ConnectionEstablished) // manually invoke callback if connection completes + { + conQueryCallback(sd, context, mDNStrue); + return; + } + if (connectionStatus == mStatus_ConnectionPending) return; // callback will be automatically invoked when connection completes + LogMsg("hndlTruncatedAnswer: connection failed"); + uDNS_StopQuery(m, question); //!!!KRS can we really call this here? + } + + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Dynamic Updates +#endif + + +mDNSlocal mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass) + { + ptr = putDomainNameAsLabels(msg, ptr, limit, zone); + if (!ptr || ptr + 4 > limit) return NULL; // If we're out-of-space, return NULL + ((mDNSOpaque16 *)ptr)->NotAnInteger = kDNSType_SOA; + ptr += 2; + ((mDNSOpaque16 *)ptr)->NotAnInteger = zoneClass.NotAnInteger; + ptr += 2; + msg->h.mDNS_numZones++; + return ptr; + } + + + +mDNSlocal mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end) + { + AuthRecord prereq; + + ubzero(&prereq, sizeof(AuthRecord)); + ustrcpy(prereq.resrec.name.c, name->c); + prereq.resrec.rrtype = kDNSQType_ANY; + prereq.resrec.rrclass = kDNSClass_NONE; + ptr = putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq); + return ptr; + } + +mDNSlocal mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr) + { + mDNSu16 origclass; + // deletion: specify record w/ TTL 0, class NONE + + origclass = rr->rrclass; + rr->rrclass = kDNSClass_NONE; + ptr = PutResourceRecordTTL(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0); + rr->rrclass = origclass; + return ptr; + } + +mDNSlocal mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end) + { + AuthRecord rr; + ResourceRecord *opt = &rr.resrec; + rdataOpt *optRD; + + ubzero(&rr, sizeof(AuthRecord)); + opt->rdata = &rr.rdatastorage; + + opt->RecordType = kDNSRecordTypeKnownUnique; // to avoid warnings in other layers + opt->rrtype = kDNSType_OPT; + opt->rdlength = LEASE_OPT_SIZE; + opt->rdestimate = LEASE_OPT_SIZE; + + optRD = &rr.resrec.rdata->u.opt; + optRD->opt = kDNSOpt_Lease; + optRD->optlen = sizeof(mDNSs32); + optRD->OptData.lease = kUpdate_DefLease; + end = PutResourceRecordTTL(msg, end, &msg->h.numAdditionals, opt, 0); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return NULL; } + + return end; + + } + +mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr) + { + DNSMessage msg; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSOpaque16 id; + uDNS_AuthInfo *authInfo; + uDNS_RegInfo *regInfo = &rr->uDNS_info; + mStatus err = mStatus_UnknownErr; + + id = newMessageID(u); + InitializeDNSMessage(&msg.h, id, UpdateReqFlags); + rr->uDNS_info.id.NotAnInteger = id.NotAnInteger; + + // set zone + ptr = putZone(&msg, ptr, end, ®Info->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto error; + + if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->uDNS_info.state == regState_Refresh) + { + // KnownUnique means the record must ALREADY exist, as does refresh + // prereq: record must exist (put record in prereq section w/ TTL 0) + ptr = PutResourceRecordTTL(&msg, ptr, &msg.h.mDNS_numPrereqs, &rr->resrec, 0); + if (!ptr) goto error; + } + else if (rr->resrec.RecordType != kDNSRecordTypeShared) + { + ptr = putPrereqNameNotInUse(&rr->resrec.name, &msg, ptr, end); + if (!ptr) goto error; + } + + ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec); + if (!ptr) goto error; + + if (rr->uDNS_info.lease) + ptr = putUpdateLease(&msg, ptr); + + rr->uDNS_info.expire = -1; + + authInfo = GetAuthInfoForZone(u, ®Info->zone); + if (authInfo) + { + err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, ®Info->ns, regInfo->port, authInfo); + if (err) { LogMsg("ERROR: sendRecordRegistration - mDNSSendSignedDNSMessage - %d", err); goto error; } + } + else + { + err = mDNSSendDNSMessage(m, &msg, ptr, 0, ®Info->ns, regInfo->port); + if (err) { LogMsg("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %d", err); goto error; } + } + + if (regInfo->state != regState_Refresh) regInfo->state = regState_Pending; + return; + +error: + if (rr->uDNS_info.state != regState_Unregistered) + { + unlinkAR(&u->RecordRegistrations, rr); + rr->uDNS_info.state = regState_Unregistered; + } + rr->RecordCallback(m, rr, err); + // NOTE: not safe to touch any client structures here + } + +mDNSlocal void RecordRegistrationCallback(mStatus err, mDNS *const m, void *authPtr, const AsyncOpResult *result) + { + AuthRecord *newRR = (AuthRecord*)authPtr; + const zoneData_t *zoneData = &result->zoneData; + uDNS_GlobalInfo *u = &m->uDNS_info; + AuthRecord *ptr; + + for (ptr = u->RecordRegistrations; ptr; ptr = ptr->next) + if (ptr == newRR) break; + if (!ptr) { LogMsg("RecordRegistrationCallback - RR no longer in list. Discarding."); return; } + + if (err) { LogMsg("RecordRegistrationCallback: error %d", err); goto error; } + if (newRR->uDNS_info.state == regState_Cancelled) + { + //!!!KRS we should send a memfree callback here! + LogMsg("Registration of %s type %d cancelled prior to update", + newRR->resrec.name.c, newRR->resrec.rrtype); + newRR->uDNS_info.state = regState_Unregistered; + unlinkAR(&u->RecordRegistrations, newRR); + return; + } + + if (result->type != zoneDataResult) + { + LogMsg("ERROR: buildUpdatePacket passed incorrect result type %d", result->type); + goto error; + } + + if (newRR->resrec.rrclass != zoneData->zoneClass) + { + LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)", + newRR->resrec.rrclass, zoneData->zoneClass); + goto error; + } + + // cache zone data + ustrcpy(newRR->uDNS_info.zone.c, zoneData->zoneName.c); + newRR->uDNS_info.ns.type = mDNSAddrType_IPv4; + newRR->uDNS_info.ns.ip.v4.NotAnInteger = zoneData->primaryAddr.ip.v4.NotAnInteger; + newRR->uDNS_info.port.NotAnInteger = zoneData->updatePort.NotAnInteger; + + sendRecordRegistration(m, newRR); + return; + +error: + if (newRR->uDNS_info.state != regState_Unregistered) + { + unlinkAR(&u->RecordRegistrations, newRR); + newRR->uDNS_info.state = regState_Unregistered; + } + newRR->RecordCallback(m, newRR, err); + // NOTE: not safe to touch any client structures here + } + + +mDNSlocal mDNSBool setHostTarget(AuthRecord *rr, mDNS *m) + { + domainname *target; + + if (!rr->HostTarget) + { + debugf("Service %s - not updating host target", rr->resrec.name.c); + return mDNSfalse; + } + + // set SRV target + target = GetRRDomainNameTarget(&rr->resrec); + if (!target) + { + LogMsg("ERROR: setHostTarget: Can't set target of rrtype %d", rr->resrec.rrtype); + return mDNSfalse; + } + + if (SameDomainName(target, &m->uDNS_info.hostname)) + { + debugf("Host target for %s unchanged", rr->resrec.name.c); + return mDNSfalse; + } + AssignDomainName(*target, m->uDNS_info.hostname); + SetNewRData(&rr->resrec, NULL, 0); + return mDNStrue; + } + +mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs) + { + DNSMessage msg; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSOpaque16 id; + uDNS_AuthInfo *authInfo; + uDNS_RegInfo *rInfo = &srs->uDNS_info; + mStatus err = mStatus_UnknownErr; + + id = newMessageID(u); + InitializeDNSMessage(&msg.h, id, UpdateReqFlags); + rInfo->id.NotAnInteger = id.NotAnInteger; + + // setup resource records + if (setHostTarget(&srs->RR_SRV, m)) + SetNewRData(&srs->RR_SRV.resrec, NULL, 0); // set rdlen/estimate/hash + + //SetNewRData(&srs->RR_ADV.resrec, NULL, 0); //!!!KRS + SetNewRData(&srs->RR_PTR.resrec, NULL, 0); + SetNewRData(&srs->RR_TXT.resrec, NULL, 0); + + // construct update packet + // set zone + ptr = putZone(&msg, ptr, end, &rInfo->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass)); + if (!ptr) goto error; + + if (srs->uDNS_info.state == regState_Refresh) + { + // prereq: record must exist (put record in prereq section w/ TTL 0) + ptr = PutResourceRecordTTL(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0); + if (!ptr) goto error; + } + else + { + // use SRV for prereq + ptr = putPrereqNameNotInUse(&srs->RR_SRV.resrec.name, &msg, ptr, end); + if (!ptr) goto error; + } + + //!!!KRS Need to do bounds checking and use TCP if it won't fit!!! + //if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_ADV.resrec))) goto error; + if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_PTR.resrec))) goto error; + if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_SRV.resrec))) goto error; + if (!(ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_TXT.resrec))) goto error; + // !!!KRS do subtypes/extras etc. + + if (srs->uDNS_info.lease) + ptr = putUpdateLease(&msg, ptr); + + srs->uDNS_info.expire = -1; + + authInfo = GetAuthInfoForZone(u, &rInfo->zone); + if (authInfo) + { + err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, &rInfo->ns, rInfo->port, authInfo); + if (err) { LogMsg("ERROR: SendServiceRegistration - mDNSSendSignedDNSMessage - %d", err); goto error; } + } + else + { + err = mDNSSendDNSMessage(m, &msg, ptr, 0, &rInfo->ns, rInfo->port); + if (err) { LogMsg("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %d", err); goto error; } + } + if (rInfo->state != regState_Refresh) + rInfo->state = regState_Pending; + return; + +error: + unlinkSRS(u, srs); + rInfo->state = regState_Unregistered; + srs->ServiceCallback(m, srs, err); + //!!!KRS will mem still be free'd on error? + // NOTE: not safe to touch any client structures here + } + +mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result) + { + ServiceRecordSet *srs = (ServiceRecordSet *)srsPtr; + const zoneData_t *zoneData = &result->zoneData; + uDNS_GlobalInfo *u = &m->uDNS_info; + + if (err) goto error; + if (result->type != zoneDataResult) + { + LogMsg("ERROR: buildUpdatePacket passed incorrect result type %d", result->type); + goto error; + } + + if (srs->uDNS_info.state == regState_Cancelled) + { + // client cancelled registration while fetching zone data + srs->uDNS_info.state = regState_Unregistered; + unlinkSRS(u, srs); + srs->ServiceCallback(m, srs, mStatus_MemFree); + return; + } + + if (srs->RR_SRV.resrec.rrclass != zoneData->zoneClass) + { + LogMsg("Service %s - class does not match zone", srs->RR_SRV.resrec.name.c); + goto error; + } + // cache zone data + ustrcpy(srs->uDNS_info.zone.c, zoneData->zoneName.c); + srs->uDNS_info.ns.type = mDNSAddrType_IPv4; + srs->uDNS_info.ns.ip.v4.NotAnInteger = zoneData->primaryAddr.ip.v4.NotAnInteger; + srs->uDNS_info.port.NotAnInteger = zoneData->updatePort.NotAnInteger; + + SendServiceRegistration(m, srs); + return; + +error: + unlinkSRS(u, srs); + srs->uDNS_info.state = regState_Unregistered; + srs->ServiceCallback(m, srs, err); + //!!!KRS will mem still be free'd on error? + // NOTE: not safe to touch any client structures here + } + +mDNSexport void uDNS_UpdateServiceTargets(mDNS *const m) + { + DNSMessage msg; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + uDNS_GlobalInfo *u = &m->uDNS_info; + ServiceRecordSet *srs; + AuthRecord *rr; + mStatus err = mStatus_NoError; + + if (!m->uDNS_info.hostname.c[0]) + { + LogMsg("ERROR: uDNS_UpdateServiceTargets called before registration of hostname"); + return; + //!!!KRS need to handle this case properly! + } + + for (srs = u->ServiceRegistrations; srs; srs = srs->next) + { + if (err) srs = u->ServiceRegistrations; + // start again from beginning of list, since it may have changed + // (setHostTarget() will skip records already updated) + rr = &srs->RR_SRV; + if (srs->uDNS_info.state != regState_Registered) + { + LogMsg("ERROR: uDNS_UpdateServiceTargets - service %s not registered", rr->resrec.name.c); + continue; + //!!!KRS need to handle this + } + InitializeDNSMessage(&msg.h, srs->uDNS_info.id, UpdateReqFlags); + + // construct update packet + ptr = putZone(&msg, ptr, end, &srs->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (ptr) ptr = putDeletionRecord(&msg, ptr, &rr->resrec); // delete the old target + // update the target + if (!setHostTarget(rr, m)) continue; + if (ptr) ptr = PutResourceRecord(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec); // put the new target + // !!!KRS do subtypes/extras etc. + if (!ptr) err = mStatus_UnknownErr; + else err = mDNSSendDNSMessage(m, &msg, ptr, 0, &srs->uDNS_info.ns, srs->uDNS_info.port); + if (err) + { + LogMsg("ERROR: uDNS_UpdateServiceTargets - %s", ptr ? "mDNSSendDNSMessage" : "message formatting error"); + unlinkSRS(u, srs); + srs->uDNS_info.state = regState_Unregistered; + srs->ServiceCallback(m, srs, err); + //!!!KRS will mem still be free'd on error? + // NOTE: not safe to touch any client structures here + } + else srs->uDNS_info.state = regState_TargetChange; + } +} + + +mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr) + { + domainname *target = GetRRDomainNameTarget(&rr->resrec); + + if (rr->uDNS_info.state == regState_FetchingZoneData || + rr->uDNS_info.state == regState_Pending || + rr->uDNS_info.state == regState_Registered) + { + LogMsg("Requested double-registration of physical record %s type %s", + rr->resrec.name.c, rr->resrec.rrtype); + return mStatus_AlreadyRegistered; + } + + rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse); + rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue); + + 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; + + rr->uDNS_info.state = regState_FetchingZoneData; + rr->next = m->uDNS_info.RecordRegistrations; + m->uDNS_info.RecordRegistrations = rr; + + rr->uDNS_info.lease = mDNStrue; + return startGetZoneData(&rr->resrec.name, m, mDNStrue, mDNSfalse, RecordRegistrationCallback, rr); + } + + + +mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSMessage msg; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + mStatus err; + uDNS_AuthInfo *authInfo; + switch (rr->uDNS_info.state) + { + case regState_FetchingZoneData: + rr->uDNS_info.state = regState_Cancelled; + return mStatus_NoError; + case regState_Pending: + rr->uDNS_info.state = regState_DeregDeferred; + debugf("Deferring deregistration of record %s until registration completes", rr->resrec.name.c); + return mStatus_NoError; + case regState_Registered: + break; + case regState_DeregPending: + case regState_Cancelled: + LogMsg("Double deregistration of record %s type %d", + rr->resrec.name.c, rr->resrec.rrtype); + return mStatus_UnknownErr; + case regState_Unregistered: + LogMsg("Requested deregistration of unregistered record %s type %d", + rr->resrec.name.c, rr->resrec.rrtype); + return mStatus_UnknownErr; + default: + LogMsg("ERROR: uDNS_DeregisterRecord called for record %s type %d with unknown state %d", + rr->resrec.name.c, rr->resrec.rrtype, rr->uDNS_info.state); + return mStatus_UnknownErr; + } + + InitializeDNSMessage(&msg.h, rr->uDNS_info.id, UpdateReqFlags); + + // put zone + ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto error; + + if (!(ptr = putDeletionRecord(&msg, ptr, &rr->resrec))) goto error; + + authInfo = GetAuthInfoForZone(u, &rr->uDNS_info.zone); + if (authInfo) + { + err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, &rr->uDNS_info.ns, rr->uDNS_info.port, authInfo); + if (err) { LogMsg("ERROR: uDNS_DeregiserRecord - mDNSSendSignedDNSMessage - %d", err); goto error; } + } + else + { + err = mDNSSendDNSMessage(m, &msg, ptr, 0, &rr->uDNS_info.ns, rr->uDNS_info.port); + if (err) { LogMsg("ERROR: uDNS_DeregisterRecord - mDNSSendDNSMessage - %d", err); goto error; } + } + + return mStatus_NoError; + + error: + if (rr->uDNS_info.state != regState_Unregistered) + { + unlinkAR(&u->RecordRegistrations, rr); + rr->uDNS_info.state = regState_Unregistered; + } + return mStatus_UnknownErr; + } + + +mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs) + { + if (!*m->uDNS_info.NameRegDomain) + { + LogMsg("ERROR: uDNS_RegisterService - cannot register unicast service " + "without setting the NameRegDomain via mDNSResponder.conf"); + srs->uDNS_info.state = regState_Unregistered; + return mStatus_UnknownErr; + } + + srs->RR_SRV.resrec.rroriginalttl = 3; + srs->RR_TXT.resrec.rroriginalttl = 3; + srs->RR_PTR.resrec.rroriginalttl = 3; + + // set state and link into list + srs->uDNS_info.state = regState_FetchingZoneData; + srs->next = m->uDNS_info.ServiceRegistrations; + m->uDNS_info.ServiceRegistrations = srs; + srs->uDNS_info.lease = mDNStrue; + + return startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + } + +mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSMessage msg; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + mStatus err = mStatus_UnknownErr; + uDNS_AuthInfo *authInfo; + + //!!!KRS make sure we're doing the right thing w/ memfree + + switch (srs->uDNS_info.state) + { + case regState_Unregistered: + LogMsg("ERROR: uDNS_DeregisterService - service not registerd"); + return mStatus_UnknownErr; + case regState_FetchingZoneData: + case regState_Pending: + // let the async op complete, then terminate + srs->uDNS_info.state = regState_Cancelled; + return mStatus_NoError; // deliver memfree upon completion of async op + case regState_DeregPending: + case regState_DeregDeferred: + case regState_Cancelled: + LogMsg("uDNS_DeregisterService - deregistration in process"); + return mStatus_UnknownErr; + } + + srs->uDNS_info.state = regState_DeregPending; + InitializeDNSMessage(&msg.h, srs->uDNS_info.id, UpdateReqFlags); + + // put zone + ptr = putZone(&msg, ptr, end, &srs->uDNS_info.zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass)); + if (!ptr) { LogMsg("ERROR: uDNS_DeregisterService - putZone"); goto error; } + + // prereq: record must exist (put record in prereq section w/ TTL 0) + ptr = PutResourceRecordTTL(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0); + if (!ptr) { LogMsg("ERROR: uDNS_DeregisterService - PutResourceRecordTTL"); goto error; } + + if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_SRV.resrec))) goto error; + if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_TXT.resrec))) goto error; + if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_PTR.resrec))) goto error; + //if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_ADV.resrec))) goto error; + //!!!KRS need to handle extras/subtypes etc + + + authInfo = GetAuthInfoForZone(u, &srs->uDNS_info.zone); + if (authInfo) + { + err = mDNSSendSignedDNSMessage(m, &msg, ptr, 0, &srs->uDNS_info.ns, srs->uDNS_info.port, authInfo); + if (err) { LogMsg("ERROR: uDNS_DeregiserService - mDNSSendSignedDNSMessage - %d", err); goto error; } + } + else + { + err = mDNSSendDNSMessage(m, &msg, ptr, 0, &srs->uDNS_info.ns, srs->uDNS_info.port); + if (err) { LogMsg("ERROR: uDNS_DeregisterService - mDNSSendDNSMessage - %d", err); goto error; } + } + + return mStatus_NoError; + + error: + unlinkSRS(u, srs); + srs->uDNS_info.state = regState_Unregistered; + return err; + } + +mDNSexport void uDNS_Execute(mDNS *const m) + { + DNSQuestion *q; + DNSMessage msg; + mStatus err; + mDNSu8 *end; + mDNSs32 sendtime; + LLQ_Info *llq; + AuthRecord *rr; + ServiceRecordSet *srs; + uDNS_RegInfo *rInfo; + uDNS_GlobalInfo *u = &m->uDNS_info; + const mDNSAddr *server = getInitializedDNS(&m->uDNS_info); + mDNSs32 timenow = mDNSPlatformTimeNow(); + + u->nextevent = timenow + 0x78000000; + if (!server) { debugf("uDNS_Execute - no DNS server"); return; } + + for (q = u->ActiveQueries; q; q = q->next) + { + llq = q->uDNS_info.llq; + if (q->LongLived && llq->state != LLQ_Poll) + { + if (llq->state >= LLQ_InitialRequest && llq->state <= LLQ_Suspended && llq->retry <= timenow) + { + + // sanity check to avoid packet flood bugs + if (!llq->retry) + LogMsg("ERROR: retry timer not set for LLQ %s in state %d", q->qname.c, llq->state); + else if (llq->state == LLQ_Established || llq->state == LLQ_Refresh) + sendLLQRefresh(m, q, llq->origLease); + else if (llq->state == LLQ_InitialRequest) + startLLQHandshake(m, llq); + else if (llq->state == LLQ_SecondaryRequest) + sendChallengeResponse(m, q, NULL); + else if (llq->state == LLQ_Retry) + { llq->ntries = 0; startLLQHandshake(m, llq); } + } + } + else + { + sendtime = q->LastQTime + q->ThisQInterval; + if (sendtime <= timenow) + { + err = constructQueryMsg(&msg, &end, q); + if (err) + { + LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %s", + q->qname.c); + continue; + } + err = mDNSSendDNSMessage(m, &msg, end, q->InterfaceID, server, UnicastDNSPort); + if (err) { debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %d", err); } // surpress syslog messages if we have no network + q->LastQTime = timenow; + if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = q->ThisQInterval * 2; + } + else if (u->nextevent - sendtime > 0) u->nextevent = sendtime; + } + } + + //!!!KRS list should be pre-sorted by expiration + for (rr = u->RecordRegistrations; rr; rr = rr->next) + { + rInfo = &rr->uDNS_info; + if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0) + { + if (rInfo->expire < timenow) + { + debugf("refreshing record %s", rr->resrec.name.c); + rInfo->state = regState_Refresh; + sendRecordRegistration(m, rr); + } + else if (u->nextevent - rInfo->expire > 0) u->nextevent = rInfo->expire; + } + } + //!!!KRS list should be pre-sorted by expiration + for (srs = u->ServiceRegistrations; srs; srs = srs->next) + { + rInfo = &srs->uDNS_info; + if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0) + { + if (rInfo->expire < timenow) + { + debugf("refreshing service %s", srs->RR_SRV.resrec.name.c); + rInfo->state = regState_Refresh; + SendServiceRegistration(m, srs); + } + else if (u->nextevent - rInfo->expire > 0) u->nextevent = rInfo->expire; + } + } + } + +mDNSexport void uDNS_Init(mDNS *const m) + { + mDNSPlatformMemZero(&m->uDNS_info, sizeof(uDNS_GlobalInfo)); + m->uDNS_info.nextevent = mDNSPlatformTimeNow() + 0x78000000; + } diff --git a/mDNSCore/uDNS.h b/mDNSCore/uDNS.h new file mode 100755 index 0000000..52fc878 --- /dev/null +++ b/mDNSCore/uDNS.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: uDNS.h,v $ +Revision 1.11 2004/06/17 01:13:11 ksekar +: polling interval too short + +Revision 1.10 2004/06/11 05:45:03 ksekar +: Change SRV names for LLQ/Update port lookups + +Revision 1.9 2004/06/01 23:46:50 ksekar +: DynDNS: dynamically look up LLQ/Update ports + +Revision 1.8 2004/05/28 23:42:37 ksekar +: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.7 2004/05/18 23:51:25 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.6 2004/03/13 01:57:33 ksekar +: DynDNS: Dynamic update of service records + +Revision 1.5 2004/02/21 08:56:58 bradley +Wrap prototypes with extern "C" for C++ builds. + +Revision 1.4 2004/02/06 23:04:19 ksekar +Basic Dynamic Update support via mDNS_Register (dissabled via +UNICAST_REGISTRATION #define) + +Revision 1.3 2004/01/24 03:38:27 cheshire +Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport" + +Revision 1.2 2004/01/23 23:23:15 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.1 2003/12/13 03:05:27 ksekar +: DynDNS: Unicast query of service records + + + */ + +#ifndef __UDNS_H_ +#define __UDNS_H_ + +#include "mDNSClientAPI.h" +#include "DNSCommon.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#define INIT_UCAST_POLL_INTERVAL (15 * mDNSPlatformOneSecond) +#define MAX_UCAST_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) +#define NO_GOODBYE // will we receive goodbye packets from the server? +#define UPDATE_PORT_NAME "_dns-update._udp." +#define LLQ_PORT_NAME "_dns-llq._udp" + +// Entry points into unicast-specific routines + +extern void uDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set); +extern void uDNS_DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set); + +extern mStatus uDNS_StartQuery(mDNS *const m, DNSQuestion *const question); +extern mDNSBool IsActiveUnicastQuery(DNSQuestion *const question, uDNS_GlobalInfo *u); // returns true if OK to call StopQuery +extern mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question); + +// SuspendLLQs stops all LLQs, preserving known answers. RestartLLQs re-starts these suspended LLQs, generating appropriate add/removes +// Call SuspendLLQs prior to sleep, and on shutdown. Call RestartLLQs on wake from sleep. +extern void uDNS_SuspendLLQs(mDNS *m); +extern void uDNS_RestartLLQs(mDNS *m); + +extern mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr); +extern mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr); + +extern mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs); +extern mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs); +extern void uDNS_UpdateServiceTargets(mDNS *const m); // call following namechange + +// integer fields of msg header must be in HOST byte order before calling this routine +extern void uDNS_ReceiveMsg(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); + +// returns time of next scheduled event +extern void uDNS_Execute(mDNS *const m); + +extern void uDNS_Init(mDNS *const m); + +#ifdef __cplusplus + } +#endif + +#endif // __UDNS_H_ diff --git a/mDNSMacOS9/CarbonResource.r b/mDNSMacOS9/CarbonResource.r index 105a398..853a4ac 100644 --- a/mDNSMacOS9/CarbonResource.r +++ b/mDNSMacOS9/CarbonResource.r @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSMacOS9/Mac OS Test Responder.c b/mDNSMacOS9/Mac OS Test Responder.c index 7c55ca3..9ff78dc 100644 --- a/mDNSMacOS9/Mac OS Test Responder.c +++ b/mDNSMacOS9/Mac OS Test Responder.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,20 @@ Change History (most recent first): $Log: Mac\040OS\040Test\040Responder.c,v $ +Revision 1.21 2004/03/12 21:30:25 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + +Revision 1.20 2004/02/09 23:23:32 cheshire +Advertise "IL 2\4th Floor.apple.com." as another test "browse domain" + +Revision 1.19 2004/01/24 23:55:15 cheshire +Change to use mDNSOpaque16fromIntVal/mDNSVal16 instead of shifting and masking + +Revision 1.18 2003/11/14 21:27:08 cheshire +: Security: Crashing bug in mDNSResponder +Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. + Revision 1.17 2003/08/14 02:19:54 cheshire Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord @@ -46,7 +62,7 @@ Update to APSL 2.0 static mDNS m; static mDNS_PlatformSupport p; static ServiceRecordSet p1, p2, afp, http, njp; -static AuthRecord browsedomain; +static AuthRecord browsedomain1, browsedomain2; // 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. @@ -71,14 +87,11 @@ 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]; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; UInt8 txtbuffer[512]; - port.b[0] = (UInt8)(PortAsNumber >> 8); - port.b[1] = (UInt8)(PortAsNumber ); MakeDomainNameFromDNSNameString(&t, type); MakeDomainNameFromDNSNameString(&d, domain); @@ -92,10 +105,10 @@ mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, mDNS_RegisterService(m, recordset, n, &t, &d, // Name, type, domain - mDNSNULL, port, // Host and port + mDNSNULL, mDNSOpaque16fromIntVal(PortAsNumber), txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length mDNSNULL, 0, // Subtypes (none) - mDNSInterface_Any, // Interace ID + mDNSInterface_Any, // Interface ID Callback, mDNSNULL); // Callback and context ConvertDomainNameToCString(&recordset->RR_SRV.resrec.name, buffer); @@ -120,7 +133,6 @@ mDNSlocal void RegisterFakeServiceForTesting(mDNS *m, ServiceRecordSet *recordse mDNSlocal OSStatus CreateProxyRegistrationForRealService(mDNS *m, UInt16 PortAsNumber, const char txtinfo[], const char *servicetype, ServiceRecordSet *recordset) { - mDNSIPPort port; InetAddress ia; TBind bindReq; OSStatus err; @@ -128,10 +140,8 @@ mDNSlocal OSStatus CreateProxyRegistrationForRealService(mDNS *m, UInt16 PortAsN 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.fPort = mDNSOpaque16fromIntVal(PortAsNumber).NotAnInteger; ia.fHost = 0; bindReq.addr.maxlen = sizeof(ia); bindReq.addr.len = sizeof(ia); @@ -151,7 +161,7 @@ mDNSlocal OSStatus CreateProxyRegistrationForRealService(mDNS *m, UInt16 PortAsN // Done once on startup, and then again every time our address changes mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) { - char buffer[256]; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; ConvertDomainNameToCString(&m->hostname, buffer); @@ -185,7 +195,8 @@ mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) //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."); + mDNS_AdvertiseDomains(m, &browsedomain1, mDNS_DomainTypeBrowse, mDNSInterface_Any, "apple.com."); + mDNS_AdvertiseDomains(m, &browsedomain2, mDNS_DomainTypeBrowse, mDNSInterface_Any, "IL 2\\4th Floor.apple.com."); return(kOTNoError); } @@ -202,16 +213,13 @@ mDNSlocal Boolean YieldSomeTime(UInt32 milliseconds) 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("Multicast DNS Responder\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"); @@ -225,9 +233,12 @@ int main() 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() +#if MDNS_ONLYSYSTEMTASK + // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. + // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() + extern void mDNSPlatformIdle(mDNS *const m); mDNSPlatformIdle(&m); // Only needed for debugging version +#endif if (m.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) { DoneSetup = true; diff --git a/mDNSMacOS9/Mac OS Test Searcher.c b/mDNSMacOS9/Mac OS Test Searcher.c index d55dcd5..6b5d90d 100644 --- a/mDNSMacOS9/Mac OS Test Searcher.c +++ b/mDNSMacOS9/Mac OS Test Searcher.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,20 @@ Change History (most recent first): $Log: Mac\040OS\040Test\040Searcher.c,v $ +Revision 1.17 2004/06/10 04:37:27 cheshire +Add new parameter in mDNS_GetDomains() + +Revision 1.16 2004/03/12 21:30:25 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + +Revision 1.15 2004/01/24 23:55:15 cheshire +Change to use mDNSOpaque16fromIntVal/mDNSVal16 instead of shifting and masking + +Revision 1.14 2003/11/14 21:27:09 cheshire +: Security: Crashing bug in mDNSResponder +Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. + Revision 1.13 2003/08/14 02:19:54 cheshire Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord @@ -75,7 +91,7 @@ static void PrintServiceInfo(SearcherServices *services) if (ls->dom) { - char c_dom[256]; + char c_dom[MAX_ESCAPED_DOMAIN_NAME]; 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); @@ -84,9 +100,7 @@ static void PrintServiceInfo(SearcherServices *services) { 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]; - + char c_name[MAX_DOMAIN_LABEL+1], c_type[MAX_ESCAPED_DOMAIN_NAME], c_dom[MAX_ESCAPED_DOMAIN_NAME], c_ip[20]; DeconstructServiceName(&s->name, &name, &type, &domain); ConvertDomainLabelToCString_unescaped(&name, c_name); ConvertDomainNameToCString(&type, c_type); @@ -94,7 +108,7 @@ static void PrintServiceInfo(SearcherServices *services) 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); + if (ls->add) printf("%-15s %5d %#s\n", c_ip, mDNSVal16(s->port), s->TXTinfo); else printf("Removed\n"); } @@ -141,7 +155,7 @@ static void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRe 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.ip.ip.v4 = zeroIPAddr; info->i.port = zeroIPPort; info->add = AddRecord; info->dom = mDNSfalse; @@ -174,7 +188,7 @@ static void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceReco 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.ip.ip.v4 = zeroIPAddr; info->i.port = zeroIPPort; info->add = AddRecord; info->dom = mDNStrue; @@ -194,18 +208,16 @@ static Boolean YieldSomeTime(UInt32 milliseconds) int main() { - extern void mDNSPlatformIdle(mDNS *const m); // Only needed for debugging version mStatus err; Boolean DoneSetup = false; + void *tempmem; 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("Multicast DNS Searcher\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"); @@ -217,15 +229,23 @@ int main() mDNS_Init_DontAdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); if (err) return(err); + // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time + tempmem = OTAllocMem(0x10000); + if (tempmem) OTFreeMem(tempmem); + else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n"); + 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() +#if MDNS_ONLYSYSTEMTASK + // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. + // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() + extern void mDNSPlatformIdle(mDNS *const m); mDNSPlatformIdle(&mDNSStorage); // Only needed for debugging version +#endif if (mDNSStorage.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) { domainname srvtype, srvdom; @@ -235,7 +255,7 @@ int main() 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); + err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, NULL, mDNSInterface_Any, FoundDomain, &services); if (err) break; } diff --git a/mDNSMacOS9/README.txt b/mDNSMacOS9/README.txt index 4d13761..c30c423 100644 --- a/mDNSMacOS9/README.txt +++ b/mDNSMacOS9/README.txt @@ -7,4 +7,42 @@ 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 +standalone (embedded) mDNS Responder and Searcher, respectively. + +"mDNSLibrary.c" builds a CFM Shared Library that goes in the Extensions Folder + +The CFM Shared Library inplements the same "/usr/include/dns_sd.h" API +that exists on OS X, Windows, Linux, etc., one exception: + + - You don't need to call DNSServiceRefSockFD() to get a file descriptor, + and add that file descriptor to your select loop (or equivalent), + wait for data, + and then call DNSServiceProcessResult() every time data arrives. + + On OS 9, your callback functions are called "by magic" without having + to do any of this. Of course no magic comes without a price, and + the magic being used here is an OT Notifier function. Your callback + functions are called directly from the OT Notifier function that + received the UDP packet from the wire, which is fast and efficient, + but it does mean that you're being called at OT Notifier time -- so + no QuickDraw calls. If you need to allocate memory in your callback + function, use OTAllocMem(), not NewPtr() or NewHandle(). Typically + what you'll do in your callback function is just update your program's + data structures, and leave screen updating and UI to some foreground + code. + +"Searcher.c" and "Responder.c" build sample applications that link with +this CFM Shared Library in the Extensions Folder. You'll see that if +you try to run them without first putting the "Multicast DNS & DNS-SD" +library in the Extensions Folder and restarting, they will report an +error message. + +"Searcher.c" builds a sample application that browses for HTTP servers. + +"Responder.c" builds a sample application that advertises some test +services. By default it advertises a couple of (nonexistent) HTTP servers +called "Web Server One" and "Web Server Two". In addition, if it finds that +TCP port 548 is occupied, then it concludes that personal file sharing is +running, and advertises the existence of an AFP server. Similarly, if it +finds that TCP port 80 is occupied, then it concludes that personal Web +sharing is running, advertises the existence of an HTTP server. diff --git a/mDNSMacOS9/Responder.c b/mDNSMacOS9/Responder.c new file mode 100644 index 0000000..d1612f8 --- /dev/null +++ b/mDNSMacOS9/Responder.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Responder.c,v $ +Revision 1.2 2004/05/20 18:38:31 cheshire +Fix build broken by removal of 'kDNSServiceFlagsAutoRename' from dns_sd.h + +Revision 1.1 2004/03/12 21:30:25 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + + */ + +#include // For printf() +#include // For strcpy() + +#include // For WaitNextEvent() + +#include +#include + +#include // For SIOUXHandleOneEvent() + +#include "dns_sd.h" + +typedef union { UInt8 b[2]; UInt16 NotAnInteger; } mDNSOpaque16; +static UInt16 mDNSVal16(mDNSOpaque16 x) { return((UInt16)(x.b[0]<<8 | x.b[1])); } +static mDNSOpaque16 mDNSOpaque16fromIntVal(UInt16 v) + { mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); } + +typedef struct RegisteredService_struct RegisteredService; +struct RegisteredService_struct + { + RegisteredService *next; + DNSServiceRef sdRef; + Boolean gotresult; + DNSServiceErrorType errorCode; + char namestr[64]; + char typestr[kDNSServiceMaxDomainName]; + char domstr [kDNSServiceMaxDomainName]; + }; + +static RegisteredService p1, p2, afp, http, njp; +static RegisteredService *services = NULL, **nextservice = &services; + +static void RegCallback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, + const char *name, const char *regtype, const char *domain, void *context) + { + RegisteredService *rs = (RegisteredService *)context; + (void)sdRef; // Unused + (void)flags; // Unused + rs->gotresult = true; + rs->errorCode = errorCode; + strcpy(rs->namestr, name); + strcpy(rs->typestr, regtype); + strcpy(rs->domstr, domain); + } + +static DNSServiceErrorType RegisterService(RegisteredService *rs, mDNSOpaque16 OpaquePort, + const char name[], const char type[], const char domain[], const char txtinfo[]) + { + DNSServiceErrorType err; + unsigned char txtbuffer[257]; + strncpy((char*)txtbuffer+1, txtinfo, 255); + txtbuffer[256] = 0; + txtbuffer[0] = (unsigned char)strlen((char*)txtbuffer); + rs->gotresult = 0; + rs->errorCode = kDNSServiceErr_NoError; + err = DNSServiceRegister(&rs->sdRef, /* kDNSServiceFlagsAutoRename*/ 0, 0, + name, type, domain, NULL, OpaquePort.NotAnInteger, (unsigned short)(1+txtbuffer[0]), txtbuffer, RegCallback, rs); + if (err) + printf("RegisterService(%s %s %s) failed %d\n", name, type, domain, err); + else + { *nextservice = rs; nextservice = &rs->next; } + return(err); + } + +// 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. +static DNSServiceErrorType RegisterFakeServiceForTesting(RegisteredService *rs, + const char name[], const char type[], const char domain[], const char txtinfo[]) + { + static UInt16 NextPort = 0xF000; + return RegisterService(rs, mDNSOpaque16fromIntVal(NextPort++), name, type, domain, txtinfo); + } + +// 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. +static DNSServiceErrorType CreateProxyRegistrationForRealService(RegisteredService *rs, + const char *servicetype, UInt16 PortAsNumber, const char txtinfo[]) + { + mDNSOpaque16 OpaquePort = mDNSOpaque16fromIntVal(PortAsNumber); + 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); } + + ia.fAddressType = AF_INET; + ia.fPort = OpaquePort.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) + err = RegisterService(rs, OpaquePort, "", servicetype, "local.", txtinfo); + else if (err) + printf("OTBind failed %d", err); + + OTCloseProvider(ep); + return(err); + } + +// 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() + { + OSStatus err; + RegisteredService *s; + + SIOUXSettings.asktosaveonclose = false; + SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; + + printf("Multicast DNS Responder\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\n"); + + err = InitOpenTransport(); + if (err) { printf("InitOpenTransport failed %d", err); return(err); } + + printf("Advertising Services...\n"); + +#define SRSET 0 +#if SRSET==0 + RegisterFakeServiceForTesting(&p1, "Web Server One", "_http._tcp.", "local.", "path=/index.html"); + RegisterFakeServiceForTesting(&p2, "Web Server Two", "_http._tcp.", "local.", "path=/path.html"); +#elif SRSET==1 + RegisterFakeServiceForTesting(&p1, "Epson Stylus 900N", "_printer._tcp.", "local.", "rn=lpq1"); + RegisterFakeServiceForTesting(&p2, "HP LaserJet", "_printer._tcp.", "local.", "rn=lpq2"); +#else + RegisterFakeServiceForTesting(&p1, "My Printer", "_printer._tcp.", "local.", "rn=lpq3"); + RegisterFakeServiceForTesting(&p2, "My Other Printer", "_printer._tcp.", "local.", "lrn=pq4"); +#endif + + // If AFP Server is running, register a record for it + CreateProxyRegistrationForRealService(&afp, "_afpovertcp._tcp.", 548, ""); + + // If Web Server is running, register a record for it + CreateProxyRegistrationForRealService(&http, "_http._tcp.", 80, "path=/index.html"); + + while (!YieldSomeTime(35)) + for (s = services; s; s = s->next) + if (s->gotresult) + { + printf("%s %s %s registered\n", s->namestr, s->typestr, s->domstr); + s->gotresult = false; + } + + for (s = services; s; s = s->next) + if (s->sdRef) DNSServiceRefDeallocate(s->sdRef); + + CloseOpenTransport(); + return(0); + } diff --git a/mDNSMacOS9/Searcher.c b/mDNSMacOS9/Searcher.c new file mode 100644 index 0000000..4d0d494 --- /dev/null +++ b/mDNSMacOS9/Searcher.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Searcher.c,v $ +Revision 1.2 2004/05/27 06:30:21 cheshire +Add code to test DNSServiceQueryRecord() + +Revision 1.1 2004/03/12 21:30:25 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + + */ + +#include // For printf() +#include // For strcpy() + +#include // For WaitNextEvent() +#include // For SIOkUnresolvedCFragSymbolAddress + +#include // For SIOUXHandleOneEvent() + +#include +#include + +#include "dns_sd.h" + +#define ns_c_in 1 +#define ns_t_a 1 + +typedef union { UInt8 b[2]; UInt16 NotAnInteger; } mDNSOpaque16; +static UInt16 mDNSVal16(mDNSOpaque16 x) { return((UInt16)(x.b[0]<<8 | x.b[1])); } +static mDNSOpaque16 mDNSOpaque16fromIntVal(UInt16 v) + { mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); } + +typedef struct + { + OTLIFO serviceinfolist; + Boolean headerPrinted; + Boolean lostRecords; + } SearcherServices; + +typedef struct + { + SearcherServices *services; + char name[kDNSServiceMaxDomainName]; + char type[kDNSServiceMaxDomainName]; + char domn[kDNSServiceMaxDomainName]; + char host[kDNSServiceMaxDomainName]; + char text[kDNSServiceMaxDomainName]; + InetHost address; + mDNSOpaque16 notAnIntPort; + DNSServiceRef sdRef; + Boolean add; + Boolean dom; + OTLink link; + } linkedServiceInfo; + +static SearcherServices services; + +// 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 *s = OTGetLinkObject(link, linkedServiceInfo, link); + + if (!services->headerPrinted) + { + printf("%-55s Type Domain Target Host IP Address Port Info\n", "Name"); + services->headerPrinted = true; + } + + if (s->dom) + { + if (s->add) printf("%-55s available for browsing\n", s->domn); + else printf("%-55s no longer available for browsing\n", s->domn); + } + else + { + char ip[16]; + unsigned char *p = (unsigned char *)&s->address; + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + printf("%-55s %-16s %-14s ", s->name, s->type, s->domn); + if (s->add) printf("%-15s %-15s %5d %s\n", s->host, ip, mDNSVal16(s->notAnIntPort), s->text); + else printf("Removed\n"); + } + + link = link->fNext; + OTFreeMem(s); + } + } + +static void FoundInstanceAddress(DNSServiceRef sdRef, 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) + { + linkedServiceInfo *info = (linkedServiceInfo *)context; + SearcherServices *services = info->services; + (void)sdRef; // Unused + (void)interfaceIndex; // Unused + (void)fullname; // Unused + (void)ttl; // Unused + if (errorCode == kDNSServiceErr_NoError) + if (flags & kDNSServiceFlagsAdd) + if (rrclass == ns_c_in && rrtype == ns_t_a && rdlen == sizeof(info->address)) + { + memcpy(&info->address, rdata, sizeof(info->address)); + DNSServiceRefDeallocate(info->sdRef); + OTLIFOEnqueue(&services->serviceinfolist, &info->link); + } + } + +static void FoundInstanceInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t notAnIntPort, + uint16_t txtLen, const char *txtRecord, void *context) + { + linkedServiceInfo *info = (linkedServiceInfo *)context; + SearcherServices *services = info->services; + (void)sdRef; // Unused + (void)flags; // Unused + (void)interfaceIndex; // Unused + (void)errorCode; // Unused + (void)fullname; // Unused + strcpy(info->host, hosttarget); + if (txtLen == 0) info->text[0] = 0; + else + { + strncpy(info->text, txtRecord+1, txtRecord[0]); + info->text[txtRecord[0]] = 0; + } + info->notAnIntPort.NotAnInteger = notAnIntPort; + DNSServiceRefDeallocate(info->sdRef); + DNSServiceQueryRecord(&info->sdRef, 0, 0, info->host, ns_t_a, ns_c_in, FoundInstanceAddress, info); + } + +// When a new named instance of a service is found, FoundInstance() is called. +// In this sample code we turn around and immediately to a DNSServiceResolve() to resolve that service name +// to find its target host, port, and txtinfo, but a normal browing application would just display the name. +// Resolving every single thing you find can be quite hard on the network, so you shouldn't do this +// in a real application. Defer resolving until the client has picked which instance from the +// long list of services is the one they want to use, and then resolve only that one. +static void FoundInstance(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, + const char *replyName, const char *replyType, const char *replyDomain, void *context) + { +#pragma unused(client, interface, errorCode) + SearcherServices *services = (SearcherServices *)context; + linkedServiceInfo *info; + + if (!services) { DebugStr("\pFoundInstance: services is NULL"); return; } + + info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); + if (!info) { services->lostRecords = true; return; } + + info->services = services; + strcpy(info->name, replyName); + strcpy(info->type, replyType); + strcpy(info->domn, replyDomain); + info->text[0] = 0; + info->add = (flags & kDNSServiceFlagsAdd) ? true : false; + info->dom = false; + + if (!info->add) // If TTL == 0 we're deleting a service, + OTLIFOEnqueue(&services->serviceinfolist, &info->link); + else // else we're adding a new service + DNSServiceResolve(&info->sdRef, 0, 0, info->name, info->type, info->domn, FoundInstanceInfo, info); + } + +// 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() + { + OSStatus err; + void *tempmem; + DNSServiceRef sdRef; + DNSServiceErrorType dse; + + SIOUXSettings.asktosaveonclose = false; + SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher"; + SIOUXSettings.rows = 40; + SIOUXSettings.columns = 160; + + printf("DNS-SD Search Client\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\n"); + + if (DNSServiceBrowse == (void*)kUnresolvedCFragSymbolAddress) + { + printf("Before you can use mDNS/DNS-SD clients, you need to place the \n"); + printf("\"Multicast DNS & DNS-SD\" Extension in the Extensions Folder and restart\n"); + return(-1); + } + + err = InitOpenTransport(); + if (err) { printf("InitOpenTransport failed %d", err); return(err); } + + // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time + tempmem = OTAllocMem(0x10000); + if (tempmem) OTFreeMem(tempmem); + else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n"); + + services.serviceinfolist.fHead = NULL; + services.headerPrinted = false; + services.lostRecords = false; + + printf("Sending mDNS service lookup queries and waiting for responses...\n\n"); + dse = DNSServiceBrowse(&sdRef, 0, 0, "_http._tcp", "", FoundInstance, &services); + if (dse == kDNSServiceErr_NoError) + { + while (!YieldSomeTime(35)) + { + if (services.serviceinfolist.fHead) + PrintServiceInfo(&services); + + if (services.lostRecords) + { + services.lostRecords = false; + printf("**** Warning: Out of memory: Records have been missed.\n"); + } + } + } + + DNSServiceRefDeallocate(sdRef); + CloseOpenTransport(); + return(0); + } diff --git a/mDNSMacOS9/ShowInitIcon.c b/mDNSMacOS9/ShowInitIcon.c new file mode 100755 index 0000000..5844c5e --- /dev/null +++ b/mDNSMacOS9/ShowInitIcon.c @@ -0,0 +1,160 @@ +// ShowInitIcon - version 1.0.1, May 30th, 1995 +// This code is intended to let INIT writers easily display an icon at startup time. +// View in Geneva 9pt, 4-space tabs + +// Written by: Peter N Lewis , Jim Walker +// and François Pottier , with thanks to previous ShowINIT authors. +// Send comments and bug reports to François Pottier. + +// This version features: +// - Short and readable code. +// - Correctly wraps around when more than one row of icons has been displayed. +// - works with System 6 +// - Built with Universal Headers & CodeWarrior. Should work with other headers/compilers. + +#include +#include +#include +#include +#include "ShowInitIcon.h" + +// You should set SystemSixOrLater in your headers to avoid including glue for SysEnvirons. + +// --------------------------------------------------------------------------------------------------------------------- +// Set this flag to 1 if you want to compile this file into a stand-alone resource (see note below). +// Set it to 0 if you want to include this source file into your INIT project. + +#if 0 +#define ShowInitIcon main +#endif + +// --------------------------------------------------------------------------------------------------------------------- +// The ShowINIT mechanism works by having each INIT read/write data from these globals. +// The MPW C compiler doesn't accept variables declared at an absolute address, so I use these macros instead. +// Only one macro is defined per variable; there is no need to define a Set and a Get accessor like in . + +#define LMVCheckSum (* (unsigned short*) 0x928) +#define LMVCoord (* ( short*) 0x92A) +#define LMHCoord (* ( short*) 0x92C) +#define LMHCheckSum (* (unsigned short*) 0x92E) + +// --------------------------------------------------------------------------------------------------------------------- +// Prototypes for the subroutines. The main routine comes first; this is necessary to make THINK C's "Custom Header" option work. + +static unsigned short CheckSum (short x); +static void ComputeIconRect (Rect* iconRect, Rect* screenBounds); +static void AdvanceIconPosition (Rect* iconRect); +static void DrawBWIcon (short iconID, Rect *iconRect); + +// --------------------------------------------------------------------------------------------------------------------- +// Main routine. + +typedef struct { + QDGlobals qd; // Storage for the QuickDraw globals + long qdGlobalsPtr; // A5 points to this place; it will contain a pointer to qd +} QDStorage; + +pascal void ShowInitIcon (short iconFamilyID, Boolean advance) +{ + long oldA5; // Original value of register A5 + QDStorage qds; // Fake QD globals + CGrafPort colorPort; + GrafPort bwPort; + Rect destRect; + SysEnvRec environment; // Machine configuration. + + oldA5 = SetA5((long) &qds.qdGlobalsPtr); // Tell A5 to point to the end of the fake QD Globals + InitGraf(&qds.qd.thePort); // Initialize the fake QD Globals + + SysEnvirons(curSysEnvVers, &environment); // Find out what kind of machine this is + + ComputeIconRect(&destRect, &qds.qd.screenBits.bounds); // Compute where the icon should be drawn + + if (environment.systemVersion >= 0x0700 && environment.hasColorQD) { + OpenCPort(&colorPort); + PlotIconID(&destRect, atNone, ttNone, iconFamilyID); + CloseCPort(&colorPort); + } + else { + OpenPort(&bwPort); + DrawBWIcon(iconFamilyID, &destRect); + ClosePort(&bwPort); + } + + if (advance) + AdvanceIconPosition (&destRect); + + SetA5(oldA5); // Restore A5 to its previous value +} + +// --------------------------------------------------------------------------------------------------------------------- +// A checksum is used to make sure that the data in there was left by another ShowINIT-aware INIT. + +static unsigned short CheckSum (short x) +{ + return (unsigned short)(((x << 1) | (x >> 15)) ^ 0x1021); +} + +// --------------------------------------------------------------------------------------------------------------------- +// ComputeIconRect computes where the icon should be displayed. + +static void ComputeIconRect (Rect* iconRect, Rect* screenBounds) +{ + if (CheckSum(LMHCoord) != LMHCheckSum) // If we are first, we need to initialize the shared data. + LMHCoord = 8; + if (CheckSum(LMVCoord) != LMVCheckSum) + LMVCoord = (short)(screenBounds->bottom - 40); + + if (LMHCoord + 34 > screenBounds->right) { // Check whether we must wrap + iconRect->left = 8; + iconRect->top = (short)(LMVCoord - 40); + } + else { + iconRect->left = LMHCoord; + iconRect->top = LMVCoord; + } + iconRect->right = (short)(iconRect->left + 32); + iconRect->bottom = (short)(iconRect->top + 32); +} + +// AdvanceIconPosition updates the shared global variables so that the next extension will draw its icon beside ours. + +static void AdvanceIconPosition (Rect* iconRect) +{ + LMHCoord = (short)(iconRect->left + 40); // Update the shared data + LMVCoord = iconRect->top; + LMHCheckSum = CheckSum(LMHCoord); + LMVCheckSum = CheckSum(LMVCoord); +} + +// DrawBWIcon draws the 'ICN#' member of the icon family. It works under System 6. + +static void DrawBWIcon (short iconID, Rect *iconRect) +{ + Handle icon; + BitMap source, destination; + GrafPtr port; + + icon = Get1Resource('ICN#', iconID); + if (icon != NULL) { + HLock(icon); + // Prepare the source and destination bitmaps. + source.baseAddr = *icon + 128; // Mask address. + source.rowBytes = 4; + SetRect(&source.bounds, 0, 0, 32, 32); + GetPort(&port); + destination = port->portBits; + // Transfer the mask. + CopyBits(&source, &destination, &source.bounds, iconRect, srcBic, nil); + // Then the icon. + source.baseAddr = *icon; + CopyBits(&source, &destination, &source.bounds, iconRect, srcOr, nil); + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// Notes + +// Checking for PlotIconID: +// We (PNL) now check for system 7 and colour QD, and use colour graf ports and PlotIconID only if both are true +// Otherwise we use B&W grafport and draw using PlotBWIcon. diff --git a/mDNSMacOS9/ShowInitIcon.h b/mDNSMacOS9/ShowInitIcon.h new file mode 100755 index 0000000..cbf680c --- /dev/null +++ b/mDNSMacOS9/ShowInitIcon.h @@ -0,0 +1,20 @@ +#ifndef __ShowInitIcon__ +#define __ShowInitIcon__ + +#include + +// Usage: pass the ID of your icon family (ICN#/icl4/icl8) to have it drawn in the right spot. +// If 'advance' is true, the next INIT icon will be drawn to the right of your icon. If it is false, the next INIT icon will overwrite +// yours. You can use it to create animation effects by calling ShowInitIcon several times with 'advance' set to false. + +#ifdef __cplusplus +extern "C" { +#endif + +pascal void ShowInitIcon (short iconFamilyID, Boolean advance); + +#ifdef __cplusplus +} +#endif + +#endif /* __ShowInitIcon__ */ diff --git a/mDNSMacOS9/mDNS.mcp b/mDNSMacOS9/mDNS.mcp index dc4932a6665e1c78f31d5268727305f885a66db4..fdf6c0ee07628c8364217bfda06a1fbd5bcedd03 100644 GIT binary patch literal 967336 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

%d(_7x*jfxO&`DF>~w1yxyM=466hv))5 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 Y$GWdVjMQH&@Rk!BILmqo_s+-v1JJOTs{jB1 delta 11907 zcmbW72Y6Lgw#QG|=hh^o7fNzTLJTd15?X|iP(lllAPOjugaCmQCW(Lw+zWytN(l!z z*dU-|SIR{WgNjmg7#$(jafHF3fQ+CZV?j}Q|8@4hNzj@1zIprmb~(S@*IsMwb8hnV z@&PM1$F(o5uC9(|bxajn+=sXv@m@p+;$*~X#FdCE5bsBv zg18Q`FJd-gf5g#<4>D6?TBMh|6@)QfVPB=r%`gG4^CRX?xn&Z{V{tt~J0 zj43Xqfv6F7Wi>{gBc2C~JXhp-A|E31p&}pFYH*h;uehe9x=IYKQWLBzupS!v1_Fb9 zs;$?=muVliUKOunx3aCzz6@KRof*U=$tUZloHxjJ{en8VA9f^p6eNg45&IwxK+HwV zLmYxQ6xCKcj4sg($RgKi?bNA~qDO1J^&)M8UgwNcB#hiMBG_t63F1)wrtEf3S%xwo z%KGcB82x>FxE;fie&`qMg|e*Ihs7Bu*V=-xsI9&jeY?#ySVJEQ=!OJCQ$~F-XdlD@ zh`ES)h(i#Y7iq_^h=cWMwmvG_>(?Cf^lr9TqhTK#Y*fd%9QsGjar(=wzdqDnXc&4> zsD9AZOMiUbFnwXrt;VWj%&8wyZ_u}}_IjI~Z2kMFj~sk*S<-(B$}+BYbS4@Ped2CQ zwq^R(s4=pBWk$HZDdbyY)ZMmt{ez%R`j#-ajbVCK)(@y@7;J)GY|GN;B`4}Hwn;R$ z|Ek67e^e8rNIBaeQXcej-)Hl#UE8+COqX4$vQZdi`^ubL6y#C$*|BdK$Jf~6j1%Fm zIREq*n`~SS5W>SA6>5Nvv_wjUQyxDoQ{C8Pa^i})f^~JGH z{jte!>p8cj>PIH0+HXY1?TTz1EtKQ+?zi8qXWU+>H(D{(@0oJ5;+MO2wm}>n8Puki z|5#>c)sn!POuIgIpwJG(53(e~L)F9RHK4VYRJw&^INArf~z3$e~DkDIxbMy+nzhlAUCK&=$)MWqqrfj7=q*p}wFC3Z=iw zSnn97H-tsk7rG$36aJYsiH@jH-bsq798yqJSW`Bmd_i8Zv9PV9OZ0V!9LJEdk~?M^ z-%MfWF?NV?XPNziK5J&8b*lQzxAm-9srvqzsd`HJWByU)9@U)m0eiTYaqA|>B-(TY zRIJG1pQtA}&EZkDv9cMrUGb>K*aPYieNe#|W8?xyu)fF_p8}O4Pj2l6DU1_=PS`R` z%79{wQ>PuV{&}7+l_-#e9OA6&oB=$al4HugpzjpR*kCbk|FAPRJ3G=?W~fTYcU!kmbbATo|F+v}yj%2_ zqRmb?m*2K|owupn%iFZyi~aw$%>mwK3^n;lHve%*`83SG_RS@B!@Jg25q_P&Bew^B z7+y%Pz{ymm$^-Srmy`9Rss{h4s*b98Q4VnB8994Ym-&-hJ`rS`yy!e&4w7B5 zvSk-=D{k!DLx+Jl!rT$=j3N!~V;l~5_0+o$au~-hI)l>rNs%1M7m$qUw5S_7Qwp>h zx;~wVZv9)e(1F@Mh=YvFXM>VW-&*@uc4PY&sw0e?H3rC`&a$MYrO}3Mim_vdJ=mP% zb7a{p)rLl$kQ`iA>^fU(10w$ci8zhMC^^??Jg>OSxufKDiV4e{WEl5s zP!j43R~WCYaio|Lg%OZD- z-F)t%tf;-PTA_V?cOS=t#QH92C`zj=ol6hR;hx>BrQR4EYWu9daOzXO2kmlw;RbWi zOUi1E4s}|b`C11h+iiYY=?In0x_o(_VsxGF=%7EiFM({$MN-44o8dS!4yy6~-TUUq z`mvW&5j*)8-F`(e_pfpMV}$vBtbD=FAoW<|rd7eQMnbq8WwL(CW>x0IG$%XG$xlI& za*Hc6;fi*s|Me*_M&gPzUU(H(4kUGXbbX6B1?$HqZZ%%GM|DJmw{}(Jm(phG zVeaIx-*IwPmq@T1UH-QlB4vO1u@h}99W&Fzls#eQ`L@oBHvQd+hfMbrhuvu`-sbig z13N2jQ|_m1Rn4<$imX`TF<;y54wd~epSzT1e`Ug7oe2m^%lWOkHXLAXJty8~R{I=1 zextGwR?RcPaYo4;Wg0Y(0;JNh*W8iZOAQD5@6%6q4^W{yCB+>r~u0vQR2c&&?e=y}V9jzDSm` zfS*jt7$2IEmkiyy^T7MaC6+RziKPtnRLrWbtxGQ`tDEn0N;?X=Qk}Hp?m}w!@1yrW zRUCM~pa>rXV~hHwXQ$WJ)s$Dw9O!a7vuo>EO77Z$Gs-K<`lqK?RF@X>NhefS+)-Ip zn_g6RNBZEob1TYAi|fj(t7<#-$VeSiECpFMmm$e2eRxrbM)Yj;Ht%2rb}`bE)*~&G zCdB)t+7J<(gFEHHh#_V3%1g_-S60>4WcBNv(Ic~GmV|yRCGQe;zLup750%2G-D_N&|xyU>Deg9s3|5aAse>=!b&v!%y8K>sh&zf6qchoxN2pD_>*<+BA5-x{utLM}N{Yu|L0DzYiZFL9l*J zO~Ua;`n-}WpLJf+cggZ5)~epvk)*tj+bVvU{Tg0ejD+R(YxK=({PKmv5gF0yvf^L% zX|s$2FFBLKe_uoQ)kXTykR@|RJI6b2Tv)B8H!+C5%{mzF3i~Tugobam-9y4_L+@4A zS%sPnSC#duiJHLOxVGsc{#s24QjwP)--%)DP@2NTV3UE;fLO|sprIA*iU#+YRJPV+*e?S5vc>ui0^ zxbGZU_|h6=_D_;m$>zmPEZ<>`YQEl0R&Bz?Xb$tlTo&&|F+8EwDI*teCKFhpCtl5A+~`DX$O7e0-Vw&sS?ISN8Az~ zUMWdg#OYVBR$+XC1czp{JG2NI zwef+|*JCSgTY%?Ka7=a`UxDXXbJkrU;i}EH0}oFazC|H}?UD~ONQuUgMIpHb*{fU4 z#y`$bi8uUHNCLC9;1ON%zu5Lg(UZRC11b3v2VB z3Bo)`-xkUF1bkU29W5-M#ioWm%K1xw2T!!P^gtJUgd}oa7Y?4pdBkuC(Zl(*0pQ7; ze~LG28|LkTiCr1DprRA6u+KxztPZXLSe%j~_$K2OZ^-Jlv5=sg!v~y#9~PDFqW&&p zx-TS68NAw9?+Y2`z1j`G)tun%md2u8Pu|Y)2J$S6@7V!1WaU$wmP`do&@j5 zJ2;Aw3(3*1tP1^NieRVTZ(Y27!V}<-9t8*GL7Yd=1*czG5u#@zd5bT|{}jZm+-Pwr zqaNFf+WsD3=krJS5HQc&Sxm>>v=Fcc2toSyxewycM^f#&wmAem&KKCo6$a!D;fLtpQyN-SJa~O z2(I<)JDi`oknz+G@@_ zKziyji_1gzgA2*s|2_C}a9UKG2apIoSosJl?zK8dJwM09+-}*ve}La-ab;LdvtQ79 zmV(oHBZQ(X`~o|Y4t~Fdl?hm!@C(`~=$Q(ALXgu^F~4vO+D3?3rG;PzQ{Tf%Q}_<% zW`I9vacMo}A9FtTFW_W$Xg~c5M9uvI`w4B-)fSi9JcFE@rM6Q6U&DDQDK6(;c4!7F z*76G1PUPHSXziTfuzLz^-bTV52HQUse4T~mZcsq!FketdD56Rhg^+X&Nd@OU(N4?kLpI=;SFf3joYzS-~l%{pA&-i{OsAs*uA=m z^NRKm2VbZ*rXx7*6e09wBtjB*90GsX!cwRg=NdTs@CsDm{3*m_A`uH3S_b~8g{7Dg z$hnfTw_XE(jPp1WwV3!$ay~FG3MacD+01vm#|OTJsHFULB+A&B7nhe-H3bh1R)yqz z2f5JFC*UU4trk}rA!=dY?9W)ex{dRX=C#=OycqDuIX@017Pl`u`e$%h4uWzDeT8{z z3-&?()jwJlYF7*->|1LOrasAePl#67xAqLgsXoPd)?Q2??Au4y@wCMy$5rIQzK1q~ zlVlL$$k7S&p7ay=vqUj}_7R*VzmD1E1*ovTyP!mss2BGA&0SF?SySHM)V3Iv~u zgx~w@bRsy~A*d6f)dBln_BQ}O=cVB&i}BN-fa-3}XQVWnH}ighJ6$EwAe%E9sj%;O zITlM(jKF7*R{)#7T+uPyeSfa6p5bAA^V z9Wd{tSa4Vlg1S(_{KCH3uNfe(^NOYKHQTq={xVuVl$rRwE0GAtwZVrnj)8*f6cS3L^#~3Gs5**bNG#erR#^N`8xdcO#L0#Q87C1NOamBREWrg0BdPIO7$VOvQ!} zn1WpfuJ6W6;D6^_z1(cxTAsHW6%ZMMoSBW3+qdSP4}RR@ECM17*!N>yz)x`AVI0cB zzB8x{1w&w-8O`>sO@sSZu@K^&$ZEKGvlBhQKjrTqEM+=GE$llj2Ng|dh_C@Xpw%~{ zu}aE=a3&Us(DsIVz(2FF6h~(&uJ6@Z;Gc6od?0e6yaP!wPg|UIyNm2!*!TR$QE|qq zP^waqSMZ~WH^I;H4&5=3{Q~EC(coWj-WyU8_RTJT4}OmGtXyo2TLC+W7h3h7oZlFZ z_Lgh+O4^ujQSl|O80cxXZ*4YAQax{RxjP08>^L76kV<#a5s_LlWG)+1XNXY#M=iM`C9I`;Oa#itl(up$#3x%jf&#+erfG zFrjUWeUHij|AF%@s1LlJpSlwq=1c9LhOPw6d%hjzA31-nFUAk$@Bdm}9V&jZD)9F{ z>c-EwT|4lr7MDlD;RHlC4f>(}Y;m?TA7{z0Z*Au|@LzcQNLqlnzPr-Ke&y|Vt!Odt z@?W5T>NQ@mH@d~XZ7?NfVM!j@gn~HZ@g?B!Y6$GrcuXwJ??DHE!ukFlE%x0V=Eqdd zkA@%@=3V_UI8A{0+0h{__WcMSP7KeWcnD^uz;7*InBOwuQK&x+f4;3!n;VdbixWCUsoXpKTWIlmh*thBf zhoU0Lnb7cn@^07&9&B;;ML2R{-V>pWj3j{Ui;s|U^Y&`5V)d*IuQ-?6Y~SoMl#7LO zekl#JaC2h6!U?f3&c7Lkoe3-GItH93M0N#7DPGUDRk%@E1m`~{x0rWP7I-A&PaUpe zvcQi23@5>&EUr|oM=s93@F+M2MqpRRAr%XH2Nu9$1fPvuyq=#s3l8l^U{{wR73SR* zPK?C~-gzww!oGJyQ5eh%f&Gjf3;SlLapSReM#b`w4ql~d7Y;x0vmHNR>L6$CLmK!o zpcPM}6$E|^XvKT<2k$1n?_0R!dk$Q@AXIF@#O?8n>>8BCcAK~^JCc@muqrg$BrHhW z)6P%8VHOCQ8=`1-AP<5ka2~d}#evL#LNE{KlQB-ffqcCXyd&qOC!^A9>Q_3E`lGy#KOK!-1Q7@ z7D2o7M2iFIMoLF_0QmiTX@22BdgH=i-9^RMEe<3TVq;Jq>Y&%OI1tB^;E*2ijrSrE z-}mL!;H3RPkC2481JNFU4YN$nABFaE2ZHPJPmiFY2d~)nFmmxC&<9R}^%VS-7XAm^ zK&+SGCy@&WvImP`y#+sqT$smO!9kqQSgX$`oy53hK9FLLfdwAc?&(U^r;5Y(E z$ofrIdvPWzaz#Ze2eP6LJdg9eSask9Vc=2lA)LSbE6Q9+^X7kq@=(rS$HpZ7f{@qt z8Sr6(mo!T>_n%)w|JZO|aipNx{PG^49prO<6uXpqSVxfi%VO{mf@7g#;@nH*Ctzv_ z+IyRj1|~iYK1y($y*SFe=!4*+!7;z~NmKL0xm!(C!0{t!O_z}JiSt&~gJXfjPbH$v zcbvOxGRk8)KSO57C(iwdE{Q_H?`@elAIHRQ;{2-`%r6{H-dHFEqcg+=|5}PvOq@rK zrAYAIC<{?9d=lmHoc}=9Atru&2KddKUx{s=IQKn_!6sPTMsFfwUT^L*7f}JHgJ82o zA{C-;x)1zT!H1zNCZ0$Vn#j3}BqBuJYdbhxE`rVVZ1coOkDpS%A^Zu-@XZ{n0Oh)$)*yPY#ks)amKlqsP8n+JCRv( b*PiFZ;#o20U8DMFjPQ0CFy?vV5n{G diff --git a/mDNSMacOS9/mDNSLibrary.c b/mDNSMacOS9/mDNSLibrary.c new file mode 100644 index 0000000..a8ac4a9 --- /dev/null +++ b/mDNSMacOS9/mDNSLibrary.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSLibrary.c,v $ +Revision 1.1 2004/03/12 21:30:26 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + + */ + +// Define the required CFM Shared Library entry and exit points +#include +#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 + +mDNS mDNSStorage; +static mDNS_PlatformSupport PlatformSupportStorage; +#define RR_CACHE_SIZE 64 +static CacheRecord rrcachestorage[RR_CACHE_SIZE]; + +mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) + { + 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 = OTAllocMem(sizeof(CacheRecord) * numrecords); + LogMsg("mStatus_GrowCache %d", numrecords); + if (storage) mDNS_GrowCache(m, storage, numrecords); + } + } + +extern pascal OSErr mDNS_CFMInit(const CFragInitBlock *theInitBlock); +pascal OSErr mDNS_CFMInit(const CFragInitBlock *theInitBlock) + { + extern pascal OSErr __initialize(const CFragInitBlock *theInitBlock); + __initialize(theInitBlock); // MUST do this first! + { + mStatus err; + THz oldZone = GetZone(); + SetZone(SystemZone()); + LogMsg("mDNS/DNS-SD with Macsbug breaks -- do not ship this version to customers"); + err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE, + mDNS_Init_AdvertiseLocalAddresses, mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); + SetZone(oldZone); + return((OSErr)err); + } + } + +extern void mDNS_CFMTerm(void); +void mDNS_CFMTerm(void) + { + extern pascal void __terminate(void); + LogMsg("mDNS_CFMTerm"); + mDNS_Close(&mDNSStorage); + __terminate(); + } diff --git a/mDNSMacOS9/mDNSLibraryLoader.c b/mDNSMacOS9/mDNSLibraryLoader.c new file mode 100644 index 0000000..c708eac --- /dev/null +++ b/mDNSMacOS9/mDNSLibraryLoader.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSLibraryLoader.c,v $ +Revision 1.1 2004/03/12 21:30:26 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + + */ + +#include +#include +#include "ShowInitIcon.h" + +extern pascal OSErr FragRegisterFileLibs(ConstFSSpecPtr fss, Boolean unregister); + +extern void main(void) + { + OSStatus err; + FCBPBRec fcbPB; + FSSpec fss; + + // 1. Show our "icon march" icon + ShowInitIcon(128, true); + + // 2. Find our FSSpec + fss.name[0] = 0; + fcbPB.ioNamePtr = fss.name; + fcbPB.ioVRefNum = 0; + fcbPB.ioRefNum = (short)CurResFile(); + fcbPB.ioFCBIndx = 0; + err = PBGetFCBInfoSync(&fcbPB); + + // 3. Tell CFM that we're a CFM library container file + fss.vRefNum = fcbPB.ioFCBVRefNum; + fss.parID = fcbPB.ioFCBParID; + if (err == noErr) err = FragRegisterFileLibs(&fss, false); + + // 4. Now that CFM knows we're a library container, tell it to go and get our library + if (err == noErr) + { + CFragConnectionID c; + Ptr m; + Str255 e; + THz oldZone = GetZone(); + SetZone(SystemZone()); + err = GetSharedLibrary("\pDarwin;mDNS", kPowerPCCFragArch, kLoadCFrag, &c, &m, e); + SetZone(oldZone); + } + } + +// There's no CFM stub library for the FragRegisterFileLibs() call, so we'll make our own +#if __ide_target("FragRegisterFileLibsStub") +#pragma export on +pascal OSErr FragRegisterFileLibs(ConstFSSpecPtr fss, Boolean unregister) + { + (void)fss; // Unused + (void)unregister; // Unused + return(0); + } +#endif diff --git a/mDNSMacOS9/mDNSLibraryResources.r b/mDNSMacOS9/mDNSLibraryResources.r new file mode 100644 index 0000000..c77311c --- /dev/null +++ b/mDNSMacOS9/mDNSLibraryResources.r @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSLibraryResources.r,v $ +Revision 1.4 2004/06/10 20:28:16 cheshire +Update version string to 1.0a66 + +Revision 1.3 2004/06/05 00:37:12 cheshire +Update version string to 1.0a65 + +Revision 1.2 2004/05/27 06:24:21 cheshire +Update version string to 1.0a64 + +Revision 1.1 2004/03/12 21:30:26 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + + */ + +#ifndef __TYPES.R__ +#include "Types.r" +#endif + +/* Format is : + * Two-char BCD major version (0-99) + * One-char BCD minor version, One-char BCD bugfix version (0-9, 0-9) + * development/alpha/beta/final + * One-byte non-final build number (0-255) + * Version numbers can therefore range from 0.0.0 to 99.9.9, + * with a following build stage and build number (e.g. 2.0.1 beta 219) + */ + +resource 'vers' (1, purgeable) + { + 0x01, 0x00, alpha, 66, verUS, + "1.0a66", + "Multicast DNS & DNS Service Discovery 1.0a66" + }; + +resource 'vers' (2, purgeable) + { + 0x01, 0x00, alpha, 66, verUS, + "1.0a66", + "developer.apple.com/darwin/projects/rendezvous/" + }; + +/* We need to load OT, so make sure the system heap has enough space for it */ +type 'sysz' { longint; }; +resource 'sysz' (0, purgeable) { 2500000 }; + +resource 'BNDL' (128, purgeable, protected) { + 'mDNS', + 0, + { /* array TypeArray: 2 elements */ + /* [1] */ + 'FREF', + { /* array IDArray: 1 elements */ + /* [1] */ + 0, 128 + }, + /* [2] */ + 'ICN#', + { /* array IDArray: 1 elements */ + /* [1] */ + 0, 128 + } + } +}; + +resource 'FREF' (128, purgeable, protected) { + 'INIT', + 0, + "" +}; + +resource 'icl8' (128, purgeable, protected) { + $"FDFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" + $"FFFF FFFF FFFF FFFF FFFF FFFF FD00 0000" + $"FF00 0000 0000 0000 0000 0000 0000 0000" + $"0000 0000 0000 0000 0000 00F6 FF00 0000" + $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" + $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FF00 0000" + $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" + $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FD00 0000" + $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" + $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FE00 0000" + $"FF00 F6F6 F6F6 F6F6 F62B 07F6 F6F6 F6F6" + $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FE00 0000" + $"FF00 F6F6 F6F6 F6F6 3A16 402B F7F6 F6F6" + $"F6F6 F656 F9F9 F956 2BF6 F6F9 FE00 0000" + $"FF00 F6F6 F6F6 2B08 4116 4008 F8FA 2BF6" + $"F6F7 FAFA FA81 FAFA FAF7 F6F9 FE00 0000" + $"FF00 F9F6 F62B F92B 163A 172B F881 FAF6" + $"2BF8 F6F6 2BF7 8181 FA81 F6F9 FE00 FD00" + $"FF00 FFF9 F6F9 F9F8 2B2C 2BF6 F6F6 F8FA" + $"2BF6 F6F6 F6F6 F781 F956 F8F9 FEFD FFFD" + $"FFFF 00FE F5FA FA81 F7F6 F6F6 F6F6 F656" + $"56F6 F6F6 F6F6 F62B 2B2B F6F9 FEFF 00FF" + $"0000 00FF F5FA FAFA F6F6 F6F6 F6F6 2BF6" + $"F7F8 F6F6 F6F6 F607 3A16 39F9 FF00 F9FF" + $"0000 00FF 00FA 81FA F6F6 F6F6 F62B 2BF6" + $"F6F8 F6F6 F6F6 F633 1C3B 1CF6 F6F6 F9FF" + $"0000 00FE 0056 81FA F6F6 F6F6 F6F8 F6F6" + $"F6F6 F8F6 F6F6 F62C 163A 3AF6 F6F6 F9FF" + $"0000 00FE 00F6 8181 2BF6 F6F6 F72B F6F6" + $"F6F6 F62B F6F6 2B2B 2C32 F6F6 F6F6 F9FF" + $"0000 00FE 00F6 2BFA 81F6 F6F6 F9F6 F6F6" + $"F6F6 F62B F6F7 FA81 F8F6 F6F6 F6F6 F9FF" + $"0000 00FE 00F6 F6F6 F7F9 F72B F9F6 F6F6" + $"F62B F756 F9FA F9F7 F6F6 F6F6 F6F6 F9FF" + $"0000 00FE 00F6 F6F6 F6F6 F6F9 F82B 2BF7" + $"F7F7 F72B F8F6 F6F6 F6F6 F6F6 F6F6 F9FF" + $"0000 00FE 00F6 F6F6 F6F6 F681 2BF6 F6F6" + $"F6F6 F6F6 F7F6 F6F6 F6F6 F6F6 F6F6 F9FF" + $"0000 00FE 00F6 F6F6 F6F6 2B81 F7F6 F6F6" + $"F6F6 F6F6 562B F6F6 F6F6 F6F6 F9F6 F9FF" + $"0000 00FE 00F6 F6F6 F6F6 F7FB F7F6 F6F6" + $"F6F6 F6F6 FAF7 F6F6 F6F6 F6F9 FEF9 F9FF" + $"FFFF 00FE 00F6 F6F6 F6F6 F7F7 2BF6 F6F6" + $"F6F6 F6F8 81F7 F6F6 F6F6 F6F9 FEFF F9FF" + $"FF00 FF00 00F6 F6F6 F6F6 F60E 3A16 32F6" + $"F6F6 56FA 812B F6F6 F6F6 F6F9 FEFD FFFD" + $"FF00 0000 F6F6 F6F6 F6F6 F640 1640 16F6" + $"F9F9 FA81 F9F6 F6F6 F6F6 F6F9 FE00 FD00" + $"FF00 F6F6 F6F6 F6F6 F6F6 F633 173A 332B" + $"FAFA 8181 2BF6 F6F6 F6F6 F6F9 FE00 0000" + $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 3232 F6F9" + $"8181 FA2B F6F6 F6F6 F6F6 F6F9 FE00 0000" + $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 2BF8" + $"F82B F6F6 F6F6 F6F6 F6F6 F6F9 FE00 0000" + $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" + $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FE00 0000" + $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" + $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FD00 0000" + $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" + $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FF00 0000" + $"FFF6 F9F9 F9F9 F9F9 F9F9 F9F9 F9F9 F9F9" + $"F9F9 F9F9 F9F9 F9F9 F9F9 F9F9 FF00 0000" + $"FDFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" + $"FFFF FFFF FFFF FFFF FFFF FFFF FD" +}; + +data 'mDNS' (0, "Owner resource") { + $"0644 6172 7769 6E" /* .Darwin */ +}; + +resource 'ICN#' (128) { + { /* array: 2 elements */ + /* [1] */ + $"FFFF FFF8 8000 0008 8000 0008 8000 0008" + $"8000 0008 80E0 0408 81FC 3F88 83FE 7FC8" + $"87FF EFEA A7E3 83EF D781 81ED 1F02 C1E9" + $"1706 61F1 1704 21E1 178C 33C1 13C8 1781" + $"10F8 FF01 101F F801 1018 0801 1038 0C01" + $"1038 0C09 D03C 1C0D A01E 7C0F 801F F80A" + $"801F F808 800F F008 8003 C008 8000 0008" + $"8000 0008 8000 0008 8000 0008 FFFF FFF8", + /* [2] */ + $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" + $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" + $"FFFF FFFA FFFF FFFF DFFF FFFF 1FFF FFFF" + $"1FFF FFFF 1FFF FFFF 1FFF FFFF 1FFF FFFF" + $"1FFF FFFF 1FFF FFFF 1FFF FFFF 1FFF FFFF" + $"1FFF FFFF DFFF FFFF FFFF FFFF FFFF FFFA" + $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" + $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" + } +}; + +resource 'ics#' (128, purgeable) { + { /* array: 2 elements */ + /* [1] */ + $"FFFE 8002 8002 8C02 DE73 D19B 5299 5451" + $"4C61 47C1 C443 C483 8702 8302 8002 FFFE", + /* [2] */ + $"FFFE FFFE FFFE FFFE FFFF FFFF 7FFF 7FFF" + $"7FFF 7FFF FFFF FFFF FFFE FFFE FFFE FFFE" + } +}; + +resource 'ics8' (128) { + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF00" + $"FFF6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 FD00" + $"FFF6 F6F6 32F6 F6F6 F6F6 F7F7 F6F6 FF00" + $"FFF6 F632 2233 81F6 2B56 81AC FBF6 FF00" + $"FFFE FAF9 2CF6 2BFA 2BF6 F6F8 FAF8 FEFF" + $"FFFD FCFA F6F6 F62B F8F6 F6F6 3333 FEFF" + $"00FE 81F9 F6F6 2BF6 F62B F6F6 4040 F6FF" + $"00FF 2BFB 2BF6 F7F6 F62B F6F9 32F6 F6FF" + $"00FE F6F6 F7F8 F7F6 2BF8 56F8 F6F6 F6FF" + $"00FE F6F6 F6FA F6F6 F6F6 F7F6 F6F6 F6FF" + $"FFFE F6F6 F6FA 2BF6 F6F6 FAF6 F6F6 FEFF" + $"FFFE F6F6 F632 3AF6 2BF9 81F6 F6F6 FEFF" + $"FFF6 F6F6 F632 4632 FCAC F7F6 F6F6 FE00" + $"FFF6 F6F6 F6F6 F6F8 562B F6F6 F6F6 FE00" + $"FFF6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 FE00" + $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF" +}; + diff --git a/mDNSMacOS9/mDNSMacOS9.c b/mDNSMacOS9/mDNSMacOS9.c index cdb49b3..6563112 100644 --- a/mDNSMacOS9/mDNSMacOS9.c +++ b/mDNSMacOS9/mDNSMacOS9.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,38 @@ Change History (most recent first): $Log: mDNSMacOS9.c,v $ +Revision 1.29 2004/05/26 20:53:16 cheshire +Remove unncecessary "return( -1 );" at the end of mDNSPlatformUTC() + +Revision 1.28 2004/05/20 18:39:06 cheshire +Fix build broken by addition of mDNSPlatformUTC requirement + +Revision 1.27 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.26 2004/04/09 17:43:03 cheshire +Make sure to set the McastTxRx field so that duplicate suppression works correctly + +Revision 1.25 2004/03/15 18:55:38 cheshire +Comment out debugging message + +Revision 1.24 2004/03/12 21:30:26 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + +Revision 1.23 2004/02/09 23:24:43 cheshire +Need to set TTL 255 to interoperate with peers that check TTL (oops!) + +Revision 1.22 2004/01/27 20:15:23 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.21 2004/01/24 04:59:16 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.20 2003/11/14 20:59:09 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. + Revision 1.19 2003/08/18 23:09:20 cheshire mDNSResponder divide by zero in mDNSPlatformTimeNow() @@ -31,15 +65,14 @@ Update to APSL 2.0 */ +#include +#include // For va_list support + #include // For LMGetCurApName() #include // For smSystemScript #include // For ConvertFromPStringToUnicode() -#include -#include // For va_list support - #include "mDNSClientAPI.h" // Defines the interface provided to the client layer above -#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below #include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform @@ -47,17 +80,29 @@ Update to APSL 2.0 // Constants static const TSetBooleanOption kReusePortOption = - { sizeof(TSetBooleanOption), INET_IP, IP_REUSEPORT, 0, true }; + { kOTBooleanOptionSize, INET_IP, IP_REUSEPORT, 0, true }; + +// IP_RCVDSTADDR with TSetByteOption/kOTOneByteOptionSize works on OS 9, OS X Classic, and OS 9 Carbon, +// but gives error #-3151 (kOTBadOptionErr) on OS X Carbon. +// If we instead use TSetBooleanOption/kOTBooleanOptionSize then OTOptionManagement on OS X Carbon +// no longer returns -3151 but it still doesn't actually work -- no destination addresses +// are delivered by OTRcvUData. I think it's just a bug in OS X Carbon. +static const TSetByteOption kRcvDestAddrOption = + { kOTOneByteOptionSize, INET_IP, IP_RCVDSTADDR, 0, true }; +//static const TSetBooleanOption kRcvDestAddrOption = +// { kOTBooleanOptionSize, INET_IP, IP_RCVDSTADDR, 0, true }; -// IP_RCVDSTADDR gives error #-3151 (kOTBadOptionErr) -static const TSetBooleanOption kRcvDestAddrOption = - { sizeof(TSetBooleanOption), INET_IP, IP_REUSEPORT, 0, true }; +static const TSetByteOption kSetUnicastTTLOption = + { kOTOneByteOptionSize, INET_IP, IP_TTL, 0, 255 }; + +static const TSetByteOption kSetMulticastTTLOption = + { kOTOneByteOptionSize, INET_IP, IP_MULTICAST_TTL, 0, 255 }; 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 } }; +//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. @@ -72,6 +117,14 @@ static const TNetbuf zeroTNetbuf = { 0 }; // *************************************************************************** // Functions +mDNSlocal void SafeDebugStr(unsigned char *buffer) + { + int i; + // Don't want semicolons in MacsBug messages -- they signify commands to execute + for (i=1; i<= buffer[0]; i++) if (buffer[i] == ';') buffer[i] = '.'; + DebugStr(buffer); + } + #if MDNS_DEBUGMSGS mDNSexport void debugf_(const char *format, ...) { @@ -80,16 +133,21 @@ mDNSexport void debugf_(const char *format, ...) va_start(ptr,format); buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); va_end(ptr); -#if __ONLYSYSTEMTASK__ +#if MDNS_ONLYSYSTEMTASK buffer[1+buffer[0]] = 0; fprintf(stderr, "%s\n", buffer+1); fflush(stderr); #else - DebugStr(buffer); + SafeDebugStr(buffer); #endif } #endif +#if MDNS_BUILDINGSHAREDLIBRARY >= 2 +// When building the non-debug version of the Extension, intended to go on end-user systems, we don't want +// MacsBug breaks for *anything*, not even for the serious LogMsg messages that on OS X would be written to syslog +mDNSexport void LogMsg(const char *format, ...) { (void)format; } +#else mDNSexport void LogMsg(const char *format, ...) { unsigned char buffer[256]; @@ -97,24 +155,27 @@ mDNSexport void LogMsg(const char *format, ...) va_start(ptr,format); buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); va_end(ptr); -#if __ONLYSYSTEMTASK__ +#if MDNS_ONLYSYSTEMTASK buffer[1+buffer[0]] = 0; fprintf(stderr, "%s\n", buffer+1); fflush(stderr); #else - DebugStr(buffer); + SafeDebugStr(buffer); #endif } +#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) + mDNSInterfaceID InterfaceID, 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) + #pragma unused(InterfaceID) InetAddress InetDest; TUnitData senddata; + if (dst->type != mDNSAddrType_IPv4) return(mStatus_NoError); + InetDest.fAddressType = AF_INET; InetDest.fPort = dstPort.NotAnInteger; InetDest.fHost = dst->ip.v4.NotAnInteger; @@ -136,7 +197,7 @@ mDNSlocal OSStatus readpacket(mDNS *m) mDNSInterfaceID interface; mDNSIPPort senderport; InetAddress sender; - char options[512]; + char options[256]; DNSMessage packet; TUnitData recvdata; OTFlags flags = 0; @@ -160,11 +221,28 @@ mDNSlocal OSStatus readpacket(mDNS *m) 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 + destaddr.ip.v4 = zeroIPAddr; + + #if OTCARBONAPPLICATION + // IP_RCVDSTADDR is known to fail on OS X Carbon, so we'll just assume the packet was probably multicast + destaddr.ip.v4 = AllDNSLinkGroup; + #endif + + if (recvdata.opt.len) + { + TOption *c = nil; + while (1) + { + err = OTNextOption(recvdata.opt.buf, recvdata.opt.len, &c); + if (err || !c) break; + if (c->level == INET_IP && c->name == IP_RCVDSTADDR && c->len - kOTOptionHeaderSize == sizeof(destaddr.ip.v4)) + mDNSPlatformMemCopy(c->value, &destaddr.ip.v4, sizeof(destaddr.ip.v4)); + } + } + 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); @@ -174,15 +252,51 @@ mDNSlocal OSStatus readpacket(mDNS *m) return(err); } +mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context, int *descriptor) + { + (void)dst; // Unused + (void)dstport; // Unused + (void)InterfaceID; // Unused + (void)callback; // Unused + (void)context; // Unused + (void)descriptor; // Unused + return(mStatus_UnsupportedErr); + } + +mDNSexport void mDNSPlatformTCPCloseConnection(int sd) + { + (void)sd; // Unused + } + +mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) + { + (void)sd; // Unused + (void)buf; // Unused + (void)buflen; // Unused + return(0); + } + +mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len) + { + (void)sd; // Unused + (void)msg; // Unused + (void)len; // Unused + return(0); + } 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; + m->p->optReq.opt.len = m->p->optBlock.h.len; + m->p->optReq.opt.maxlen = m->p->optBlock.h.len; + if (m->p->optReq.opt.maxlen < 4) + m->p->optReq.opt.maxlen = 4; + err = OTOptionManagement(m->p->ep, &m->p->optReq, NULL); - if (err) debugf("OTOptionManagement failed %d", err); + if (err) LogMsg("OTOptionManagement failed %d", err); } mDNSlocal void mDNSinitComplete(mDNS *const m, mStatus result) @@ -201,50 +315,63 @@ mDNSlocal pascal void mDNSNotifier(void *contextPtr, OTEventCode code, OTResult { OSStatus err; InetInterfaceInfo interfaceinfo; - if (result) { debugf("T_OPENCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; } + if (result) { LogMsg("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; } + if (err) { LogMsg("OTInetGetInterfaceInfo failed %d", err); mDNSinitComplete(m, err); return; } // Make our basic standard host resource records (address, PTR, etc.) + m->p->interface.InterfaceID = (mDNSInterfaceID)&m->p->interface; 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); + m->p->interface.McastTxRx = mDNStrue; } case T_OPTMGMTCOMPLETE: - if (result) { debugf("T_OPTMGMTCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; } - //debugf("T_OPTMGMTCOMPLETE"); + case T_BINDCOMPLETE: + // IP_RCVDSTADDR is known to fail on OS X Carbon, so we don't want to abort for that error + // (see comment above at the definition of kRcvDestAddrOption) + #if OTCARBONAPPLICATION + if (result && m->p->mOTstate == mOT_RcvDestAddr) + LogMsg("Carbon IP_RCVDSTADDR option failed %d; continuing anyway", result); + else + #endif + if (result) { LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d failed %d", m->p->mOTstate, result); mDNSinitComplete(m, result); return; } + //LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d", m->p->mOTstate); 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_RcvDestAddr: m->p->optBlock.i = kRcvDestAddrOption; mDNSOptionManagement(m); break; + case mOT_SetUTTL: m->p->optBlock.i = kSetUnicastTTLOption; mDNSOptionManagement(m); break; + case mOT_SetMTTL: m->p->optBlock.i = kSetMulticastTTLOption; 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_AdminScope: m->p->optBlock.m = kAddAdminMulticastOption; mDNSOptionManagement(m); break; case mOT_Bind: OTBind(m->p->ep, (TBind*)&mDNSbindReq, NULL); break; + case mOT_Ready: mDNSinitComplete(m, mStatus_NoError); + // Can't do mDNS_RegisterInterface until *after* mDNSinitComplete has set m->mDNSPlatformStatus to mStatus_NoError + mDNS_RegisterInterface(m, &m->p->interface); + break; + default: LogMsg("Unexpected m->p->mOTstate %d", m->p->mOTstate-1); } 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 kOTProviderWillClose: LogMsg("kOTProviderWillClose"); break; case kOTProviderIsClosed: // Machine is going to sleep, shutting down, or reconfiguring IP + LogMsg("kOTProviderIsClosed"); + if (m->p->mOTstate == mOT_Ready) + { + m->p->mOTstate = mOT_Closed; + mDNS_DeregisterInterface(m, &m->p->interface); + } if (m->p->ep) { OTCloseProvider(m->p->ep); m->p->ep = NULL; } break; // Do we need to do anything? @@ -253,7 +380,7 @@ mDNSlocal pascal void mDNSNotifier(void *contextPtr, OTEventCode code, OTResult } } -#if __ONLYSYSTEMTASK__ +#if MDNS_ONLYSYSTEMTASK static Boolean ONLYSYSTEMTASKevent; static void *ONLYSYSTEMTASKcontextPtr; @@ -275,22 +402,17 @@ mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTRes { 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++; + if (m->p->nesting) LogMsg("CallmDNSNotifier ERROR! OTEnterNotifier is supposed to suppress notifier callbacks"); mDNSNotifier(contextPtr, code, result, cookie); - m->p->nesting--; - ScheduleNextTimerCallback(m); } #endif +static OTNotifyUPP CallmDNSNotifierUPP; + 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); @@ -302,14 +424,8 @@ mDNSlocal OSStatus mDNSOpenEndpoint(const mDNS *const m) //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); } - + err = OTAsyncOpenEndpoint(OTCreateConfiguration(kUDPName), 0, NULL, CallmDNSNotifierUPP, (void*)m); + if (err) { LogMsg("ERROR: OTAsyncOpenEndpoint(UDP) failed with error <%d>", err); return(err); } return(kOTNoError); } @@ -333,10 +449,23 @@ mDNSlocal pascal void ClientNotifier(void *contextPtr, OTEventCode code, OTResul switch (code) { - case xOTStackIsLoading: break; - case xOTStackWasLoaded: m->mDNSPlatformStatus = mStatus_Waiting; m->p->mOTstate = mOT_Reset; break; - case xOTStackIsUnloading: break; - case kOTPortNetworkChange: break; + case xOTStackIsLoading: break; + case xOTStackWasLoaded: if (m->p->mOTstate == mOT_Closed) + { + LogMsg("kOTStackWasLoaded: Re-opening endpoint"); + if (m->p->ep) + LogMsg("kOTStackWasLoaded: ERROR: m->p->ep already set"); + m->mDNSPlatformStatus = mStatus_Waiting; + m->p->mOTstate = mOT_Reset; + #if !MDNS_ONLYSYSTEMTASK + mDNSOpenEndpoint(m); + #endif + } + else + LogMsg("kOTStackWasLoaded (no action)"); + break; + case xOTStackIsUnloading: break; + case kOTPortNetworkChange: break; default: debugf("ClientNotifier unknown code %X, %X, %d", contextPtr, code, result); break; } } @@ -393,18 +522,20 @@ mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel) static pascal void mDNSTimerTask(void *arg) { -#if __ONLYSYSTEMTASK__ +#if MDNS_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); + if (!m->p->ep) LogMsg("mDNSTimerTask NO endpoint"); + if (m->mDNS_busy) LogMsg("mDNS_busy"); + if (m->p->nesting) LogMsg("mDNSTimerTask ERROR! OTEnterNotifier is supposed to suppress timer callbacks too"); + + // If our timer fires at a time when we have no endpoint, ignore it -- + // once we reopen our endpoint and get our T_BINDCOMPLETE message we'll call + // mDNS_RegisterInterface(), which does a lock/unlock, which retriggers the timer. + // Likewise, if m->mDNS_busy or m->p->nesting, we'll catch this on the unlock + if (m->p->ep && m->mDNS_busy == 0 && m->p->nesting == 0) mDNS_Execute(m); #endif } @@ -414,8 +545,22 @@ long sleep, wake, mode; mDNSexport mStatus mDNSPlatformInit(mDNS *const m) { - OSStatus err; + OSStatus err = InitOpenTransport(); + + ClientNotifierContext = m; + // Note: OTRegisterAsClient returns kOTNotSupportedErr when running as Carbon code on OS X + // -- but that's okay, we don't need a ClientNotifier when running as Carbon code on OS X + OTRegisterAsClient(NULL, NewOTNotifyUPP(ClientNotifier)); + m->p->OTTimerTask = OTCreateTimerTask(NewOTProcessUPP(mDNSTimerTask), m); + m->p->nesting = 0; + +#if TEST_SLEEP + sleep = TickCount() + 600; + wake = TickCount() + 1200; + mode = 0; +#endif + // Set up the nice label m->nicelabel.c[0] = 0; GetUserSpecifiedComputerName(&m->nicelabel); @@ -429,39 +574,37 @@ mDNSexport mStatus mDNSPlatformInit(mDNS *const m) mDNS_GenerateFQDN(m); - ClientNotifierContext = m; - -#if !TARGET_API_MAC_CARBON - err = OTRegisterAsClient(LMGetCurApName(), NewOTNotifyUPP(ClientNotifier)); - if (err) debugf("OTRegisterAsClient failed %d", err); -#endif - + // When it's finished mDNSOpenEndpoint asynchronously calls mDNSinitComplete() and then mDNS_RegisterInterface() + CallmDNSNotifierUPP = NewOTNotifyUPP(CallmDNSNotifier); 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 - + if (err) + { + LogMsg("mDNSOpenEndpoint failed %d", err); + if (m->p->OTTimerTask) OTDestroyTimerTask(m->p->OTTimerTask); + OTUnregisterAsClient(); + CloseOpenTransport(); + } return(err); } extern void mDNSPlatformClose (mDNS *const m) { - if (m->p->OTTimerTask) { OTDestroyTimerTask(m->p->OTTimerTask); m->p->OTTimerTask = 0; } + if (m->p->mOTstate == mOT_Ready) + { + m->p->mOTstate = mOT_Closed; + mDNS_DeregisterInterface(m, &m->p->interface); + } if (m->p->ep) { OTCloseProvider (m->p->ep); m->p->ep = NULL; } + if (m->p->OTTimerTask) { OTDestroyTimerTask(m->p->OTTimerTask); m->p->OTTimerTask = 0; } + + OTUnregisterAsClient(); CloseOpenTransport(); } +#if MDNS_ONLYSYSTEMTASK extern void mDNSPlatformIdle(mDNS *const m); mDNSexport void mDNSPlatformIdle(mDNS *const m) { -#if __ONLYSYSTEMTASK__ while (ONLYSYSTEMTASKcontextPtr) { void *contextPtr = ONLYSYSTEMTASKcontextPtr; @@ -473,7 +616,6 @@ mDNSexport void mDNSPlatformIdle(mDNS *const m) ONLYSYSTEMTASKevent = false; mDNS_Execute(m); } -#endif if (m->p->mOTstate == mOT_Reset) { @@ -494,42 +636,42 @@ mDNSexport void mDNSPlatformIdle(mDNS *const m) break; } #endif - } +#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++; + // If we haven't even opened our endpoint yet, then just increment m->p->nesting for the same reason + if (m->p->mOTstate == mOT_Ready && !m->p->ep) DebugStr("\pmDNSPlatformLock: m->p->mOTstate == mOT_Ready && !m->p->ep"); + if (!m->p->ep || 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); + if (m->mDNSPlatformStatus == mStatus_NoError) + { + SInt32 interval = m->NextScheduledEvent - mDNSPlatformTimeNow(); + if (interval < 1) interval = 1; + else if (interval > 0x70000000 / 1000) interval = 0x70000000 / mDNSPlatformOneSecond; + else interval = (interval * 1000 + mDNSPlatformOneSecond-1)/ mDNSPlatformOneSecond; + 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->ep && m->mDNS_busy == 0) ScheduleNextTimerCallback(m); + if (m->p->nesting) m->p->nesting--; - else - { - ScheduleNextTimerCallback(m); - OTLeaveNotifier(m->p->ep); - } + else OTLeaveNotifier(m->p->ep); } mDNSexport void mDNSPlatformStrCopy(const void *src, void *dst) { OTStrCopy((char*)dst, (char*)src); } @@ -542,3 +684,17 @@ mDNSexport void mDNSPlatformMemFree (void *mem) mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) { *timenow = mDNSPlatformTimeNow(); return(mStatus_NoError); } mDNSexport SInt32 mDNSPlatformTimeNow() { return((SInt32)TickCount()); } mDNSexport SInt32 mDNSPlatformOneSecond = 60; + +mDNSexport mDNSs32 mDNSPlatformUTC(void) + { + // Classic Mac OS since Midnight, 1st Jan 1904 + // Standard Unix counts from 1970 + // This value adjusts for the 66 years and 17 leap-days difference + mDNSu32 SecsSince1904; + MachineLocation ThisLocation; + #define TIME_ADJUST (((1970 - 1904) * 365 + 17) * 24 * 60 * 60) + #define ThisLocationGMTdelta ((ThisLocation.u.gmtDelta << 8) >> 8) + GetDateTime(&SecsSince1904); + ReadLocation(&ThisLocation); + return((mDNSs32)(SecsSince1904 - ThisLocationGMTdelta - TIME_ADJUST)); + } diff --git a/mDNSMacOS9/mDNSMacOS9.h b/mDNSMacOS9/mDNSMacOS9.h index d707f9f..03c7bd5 100755 --- a/mDNSMacOS9/mDNSMacOS9.h +++ b/mDNSMacOS9/mDNSMacOS9.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,13 @@ Change History (most recent first): $Log: mDNSMacOS9.h,v $ +Revision 1.10 2004/03/12 21:30:26 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + +Revision 1.9 2004/02/09 23:25:35 cheshire +Need to set TTL 255 to interoperate with peers that check TTL (oops!) + Revision 1.8 2003/08/12 19:56:24 cheshire Update to APSL 2.0 @@ -31,28 +40,33 @@ Update to APSL 2.0 // *************************************************************************** // Classic Mac (Open Transport) structures +//#include // OpenTransport.h requires this #include #include #include typedef enum { - mOT_Reset = 0, - mOT_Start, - mOT_ReusePort, - mOT_RcvDestAddr, - mOT_LLScope, - mOT_AdminScope, - mOT_Bind, - mOT_Ready + mOT_Closed = 0, // We got kOTProviderIsClosed message + mOT_Reset, // We got xOTStackWasLoaded message + mOT_Start, // We've called OTAsyncOpenEndpoint + mOT_ReusePort, // Have just done kReusePortOption + mOT_RcvDestAddr, // Have just done kRcvDestAddrOption + mOT_SetUTTL, // Have just done kSetUnicastTTLOption + mOT_SetMTTL, // Have just done kSetMulticastTTLOption + mOT_LLScope, // Have just done kAddLinkMulticastOption +// mOT_AdminScope, // Have just done kAddAdminMulticastOption + mOT_Bind, // We've just called OTBind + mOT_Ready // Got T_BINDCOMPLETE; Interface is registered and active } mOT_State; typedef struct { TOptionHeader h; mDNSv4Addr multicastGroupAddress; mDNSv4Addr InterfaceAddress; } TIPAddMulticastOption; +typedef struct { TOptionHeader h; UInt8 val; } TSetByteOption; 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; +typedef union { TOptionHeader h; TIPAddMulticastOption m; TSetByteOption i; TSetBooleanOption b; } TOptionBlock; struct mDNS_PlatformSupport_struct { diff --git a/mDNSMacOS9/mDNSPrefix.h b/mDNSMacOS9/mDNSPrefix.h new file mode 100644 index 0000000..3d1b0aa --- /dev/null +++ b/mDNSMacOS9/mDNSPrefix.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mDNSPrefix.h,v $ +Revision 1.2 2004/05/21 01:57:08 cheshire +Add macros for malloc() and free() so that dnssd_clientlib.c can use them + +Revision 1.1 2004/03/12 21:30:26 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + + */ + +// Global definitions that apply to all source files +// +// Symbols that are defined here are available within all source files. +// This is the equivalent of using using "-d SYMBOL=VALUE" in a Makefile +// in command-line build environments. + +// For normal DeferredTask time execution, set MDNS_ONLYSYSTEMTASK to 0 +// For easier debugging, set MDNS_ONLYSYSTEMTASK to 1, and OT Notifier executions +// will be deferred until SystemTask time. (This option is available only for building +// the standalone application samples that have their own event loop -- don't try +// to build the System Extension with MDNS_ONLYSYSTEMTASK set because it won't work.) + +#if __ide_target("Standalone TestResponder") || __ide_target("Standalone TestSearcher") +#define TARGET_API_MAC_CARBON 1 +#define OTCARBONAPPLICATION 1 +#define MDNS_ONLYSYSTEMTASK 0 +#define MDNS_DEBUGMSGS 0 +#elif __ide_target("Standalone TestResponder (Debug)") || __ide_target("Standalone TestSearcher (Debug)") +#define TARGET_API_MAC_CARBON 1 +#define OTCARBONAPPLICATION 1 +#define MDNS_ONLYSYSTEMTASK 1 +#define MDNS_DEBUGMSGS 1 +#elif __ide_target("Standalone TestResponder (Classic)") || __ide_target("Standalone TestSearcher (Classic)") +#define MDNS_ONLYSYSTEMTASK 0 +#define MDNS_DEBUGMSGS 0 +#elif __ide_target("CFM Library for Extensions Folder") +#define MDNS_BUILDINGSHAREDLIBRARY 2 +#elif __ide_target("CFM Library for Extensions (Debug)") +#define MDNS_DEBUGMSGS 0 +#define MDNS_BUILDINGSHAREDLIBRARY 1 +#elif __ide_target("CFM Stub for clients to link against") +#define MDNS_BUILDINGSTUBLIBRARY 1 +#else +#error Options for this target not found in prefix file +#endif + +// dnssd_clientlib.c assumes malloc() and free(), so we #define them here to be the OT equivalents +#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY +#define malloc(x) OTAllocMem(x) +#define free(x) OTFreeMem(x) +#endif diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h deleted file mode 100755 index f58c914..0000000 --- a/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: BrowserController.h,v $ -Revision 1.7 2003/08/12 19:55:07 cheshire -Update to APSL 2.0 - - */ - -#import -#import - -#include - -@interface BrowserController : NSObject -{ - IBOutlet id domainField; - IBOutlet id nameField; - IBOutlet id typeField; - - IBOutlet id serviceDisplayTable; - IBOutlet id typeColumn; - IBOutlet id nameColumn; - IBOutlet id serviceTypeField; - IBOutlet id serviceNameField; - - IBOutlet id ipAddressField; - IBOutlet id portField; - IBOutlet id textField; - - NSMutableArray *srvtypeKeys; - NSMutableArray *srvnameKeys; - NSMutableArray *domainKeys; - NSMutableArray *nameKeys; - NSString *Domain; - NSString *SrvType; - NSString *SrvName; - NSString *Name; - - dns_service_discovery_ref browse_client; - -} - -- (IBAction)handleDomainClick:(id)sender; -- (IBAction)handleNameClick:(id)sender; -- (IBAction)handleTypeClick:(id)sender; - -- (IBAction)connect:(id)sender; - -- (IBAction)handleTableClick:(id)sender; -- (IBAction)removeSelected:(id)sender; -- (IBAction)addNewService:(id)sender; - -- (IBAction)update:(NSString *)Type Domain:(NSString *)Domain; -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication; -- (IBAction)loadDomains:(id)sender; - -- (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags; -- (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags; -- (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord; - -@end \ No newline at end of file diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m deleted file mode 100755 index 1dbe2a4..0000000 --- a/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m +++ /dev/null @@ -1,541 +0,0 @@ -/* - * 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 deleted file mode 100644 index 1cb8edd..0000000 --- a/mDNSMacOSX/Applications/DNSServiceBrowser/DNS Service Browser.pbproj/project.pbxproj +++ /dev/null @@ -1,377 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 38; - objects = { - 080E96DCFE201CFB7F000001 = { - fileRef = 29B97318FDCFA39411CA2CEA; - isa = PBXBuildFile; - settings = { - }; - }; - 080E96DDFE201D6D7F000001 = { - children = ( - 6515F1C002245DF2000001D2, - 6515F1C102245DF2000001D2, - ); - isa = PBXGroup; - name = Classes; - refType = 4; - }; - 089C165CFE840E0CC02AAC07 = { - children = ( - 089C165DFE840E0CC02AAC07, - ); - isa = PBXVariantGroup; - name = InfoPlist.strings; - refType = 4; - }; - 089C165DFE840E0CC02AAC07 = { - fileEncoding = 10; - isa = PBXFileReference; - name = English; - path = English.lproj/InfoPlist.strings; - refType = 4; - }; - 089C165EFE840E0CC02AAC07 = { - fileRef = 089C165CFE840E0CC02AAC07; - isa = PBXBuildFile; - settings = { - }; - }; -//080 -//081 -//082 -//083 -//084 -//100 -//101 -//102 -//103 -//104 - 1058C7A0FEA54F0111CA2CBB = { - children = ( - 1058C7A1FEA54F0111CA2CBB, - ); - isa = PBXGroup; - name = "Linked Frameworks"; - refType = 4; - }; - 1058C7A1FEA54F0111CA2CBB = { - isa = PBXFrameworkReference; - name = Cocoa.framework; - path = /System/Library/Frameworks/Cocoa.framework; - refType = 0; - }; - 1058C7A2FEA54F0111CA2CBB = { - children = ( - 29B97325FDCFA39411CA2CEA, - 29B97324FDCFA39411CA2CEA, - ); - isa = PBXGroup; - name = "Other Frameworks"; - refType = 4; - }; - 1058C7A3FEA54F0111CA2CBB = { - fileRef = 1058C7A1FEA54F0111CA2CBB; - isa = PBXBuildFile; - settings = { - }; - }; -//100 -//101 -//102 -//103 -//104 -//170 -//171 -//172 -//173 -//174 - 17587328FF379C6511CA2CBB = { - isa = PBXApplicationReference; - path = "DNS Service Browser.app"; - refType = 3; - }; -//170 -//171 -//172 -//173 -//174 -//190 -//191 -//192 -//193 -//194 - 19C28FACFE9D520D11CA2CBB = { - children = ( - 17587328FF379C6511CA2CBB, - ); - isa = PBXGroup; - name = Products; - refType = 4; - }; -//190 -//191 -//192 -//193 -//194 -//290 -//291 -//292 -//293 -//294 - 29B97313FDCFA39411CA2CEA = { - buildStyles = ( - 4A9504CCFFE6A4B311CA0CBA, - 4A9504CDFFE6A4B311CA0CBA, - ); - isa = PBXProject; - mainGroup = 29B97314FDCFA39411CA2CEA; - projectDirPath = ""; - targets = ( - 29B97326FDCFA39411CA2CEA, - ); - }; - 29B97314FDCFA39411CA2CEA = { - children = ( - 080E96DDFE201D6D7F000001, - 29B97315FDCFA39411CA2CEA, - 29B97317FDCFA39411CA2CEA, - 29B97323FDCFA39411CA2CEA, - 19C28FACFE9D520D11CA2CBB, - ); - isa = PBXGroup; - name = "DNS Service Browser"; - path = ""; - refType = 4; - }; - 29B97315FDCFA39411CA2CEA = { - children = ( - 29B97316FDCFA39411CA2CEA, - ); - isa = PBXGroup; - name = "Other Sources"; - path = ""; - refType = 4; - }; - 29B97316FDCFA39411CA2CEA = { - isa = PBXFileReference; - path = main.m; - refType = 4; - }; - 29B97317FDCFA39411CA2CEA = { - children = ( - 29B97318FDCFA39411CA2CEA, - 089C165CFE840E0CC02AAC07, - 6515F1C5022460A1000001D2, - ); - isa = PBXGroup; - name = Resources; - path = ""; - refType = 4; - }; - 29B97318FDCFA39411CA2CEA = { - children = ( - 29B97319FDCFA39411CA2CEA, - ); - isa = PBXVariantGroup; - name = MainMenu.nib; - path = ""; - refType = 4; - }; - 29B97319FDCFA39411CA2CEA = { - isa = PBXFileReference; - name = English; - path = English.lproj/MainMenu.nib; - refType = 4; - }; - 29B97323FDCFA39411CA2CEA = { - children = ( - 1058C7A0FEA54F0111CA2CBB, - 1058C7A2FEA54F0111CA2CBB, - ); - isa = PBXGroup; - name = Frameworks; - path = ""; - refType = 4; - }; - 29B97324FDCFA39411CA2CEA = { - isa = PBXFrameworkReference; - name = AppKit.framework; - path = /System/Library/Frameworks/AppKit.framework; - refType = 0; - }; - 29B97325FDCFA39411CA2CEA = { - isa = PBXFrameworkReference; - name = Foundation.framework; - path = /System/Library/Frameworks/Foundation.framework; - refType = 0; - }; - 29B97326FDCFA39411CA2CEA = { - buildPhases = ( - 29B97327FDCFA39411CA2CEA, - 29B97328FDCFA39411CA2CEA, - 29B9732BFDCFA39411CA2CEA, - 29B9732DFDCFA39411CA2CEA, - ); - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ""; - HEADER_SEARCH_PATHS = ""; - INSTALL_PATH = "$(HOME)/Applications"; - LIBRARY_SEARCH_PATHS = ""; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - PRODUCT_NAME = "DNS Service Browser"; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; - WRAPPER_EXTENSION = app; - }; - dependencies = ( - ); - isa = PBXApplicationTarget; - name = "DNS Service Browser"; - productInstallPath = "$(HOME)/Applications"; - productName = "DNS Service Browser"; - productReference = 17587328FF379C6511CA2CBB; - productSettingsXML = " - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - DNS Service Browser - CFBundleIconFile - - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleVersion - 0.1 - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - -"; - shouldUseHeadermap = 1; - }; - 29B97327FDCFA39411CA2CEA = { - buildActionMask = 2147483647; - files = ( - 6515F1C202245DF2000001D2, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 29B97328FDCFA39411CA2CEA = { - buildActionMask = 2147483647; - files = ( - 080E96DCFE201CFB7F000001, - 089C165EFE840E0CC02AAC07, - ); - isa = PBXResourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 29B9732BFDCFA39411CA2CEA = { - buildActionMask = 2147483647; - files = ( - 29B9732CFDCFA39411CA2CEA, - 6515F1C302245DF3000001D2, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 29B9732CFDCFA39411CA2CEA = { - fileRef = 29B97316FDCFA39411CA2CEA; - isa = PBXBuildFile; - settings = { - ATTRIBUTES = ( - ); - }; - }; - 29B9732DFDCFA39411CA2CEA = { - buildActionMask = 2147483647; - files = ( - 1058C7A3FEA54F0111CA2CBB, - ); - isa = PBXFrameworksBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; -//290 -//291 -//292 -//293 -//294 -//4A0 -//4A1 -//4A2 -//4A3 -//4A4 - 4A9504CCFFE6A4B311CA0CBA = { - buildRules = ( - ); - buildSettings = { - COPY_PHASE_STRIP = NO; - OPTIMIZATION_CFLAGS = "-O0"; - }; - isa = PBXBuildStyle; - name = Development; - }; - 4A9504CDFFE6A4B311CA0CBA = { - buildRules = ( - ); - buildSettings = { - COPY_PHASE_STRIP = YES; - }; - isa = PBXBuildStyle; - name = Deployment; - }; -//4A0 -//4A1 -//4A2 -//4A3 -//4A4 -//650 -//651 -//652 -//653 -//654 - 6515F1C002245DF2000001D2 = { - isa = PBXFileReference; - path = BrowserController.h; - refType = 4; - }; - 6515F1C102245DF2000001D2 = { - isa = PBXFileReference; - path = BrowserController.m; - refType = 4; - }; - 6515F1C202245DF2000001D2 = { - fileRef = 6515F1C002245DF2000001D2; - isa = PBXBuildFile; - settings = { - }; - }; - 6515F1C302245DF3000001D2 = { - fileRef = 6515F1C102245DF2000001D2; - isa = PBXBuildFile; - settings = { - }; - }; - 6515F1C5022460A1000001D2 = { - isa = PBXFileReference; - name = DNSServiceDiscovery.h; - path = /usr/include/DNSServiceDiscovery/DNSServiceDiscovery.h; - refType = 0; - }; - }; - rootObject = 29B97313FDCFA39411CA2CEA; -} diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/InfoPlist.strings deleted file mode 100644 index 08c0f0c67367eba42629b7bb234c30cb8d7a5f25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 612 zcmb`ENe=-*5QX2_ujn|6*skJ&SP~pe;wobs!WbD_`0@C7(3dBfds%D@iQ({UlX>;J*QrTxiSy diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib deleted file mode 100644 index 2668d5d..0000000 --- a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/classes.nib +++ /dev/null @@ -1,37 +0,0 @@ -{ - 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 deleted file mode 100644 index e31cf4c..0000000 --- a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/info.nib +++ /dev/null @@ -1,22 +0,0 @@ - - - - - IBDocumentLocation - 14 102 356 240 0 0 1152 746 - IBEditorPositions - - 29 - 22 474 271 44 0 0 1152 746 - - IBFramework Version - 273.0 - IBOpenObjects - - 220 - 201 - - IBSystem Version - 6C35 - - diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib b/mDNSMacOSX/Applications/DNSServiceBrowser/English.lproj/MainMenu.nib/objects.nib deleted file mode 100644 index b330188906e31390fc577041682f1d3332a25082..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9876 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 diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj b/mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj deleted file mode 100644 index 9487a76..0000000 --- a/mDNSMacOSX/Applications/DNSServiceRegistration/DNS Service Registration.pbproj/project.pbxproj +++ /dev/null @@ -1,377 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 38; - objects = { - 080E96DCFE201CFB7F000001 = { - fileRef = 29B97318FDCFA39411CA2CEA; - isa = PBXBuildFile; - settings = { - }; - }; - 080E96DDFE201D6D7F000001 = { - children = ( - 654EC28D0226D54A006533C2, - 654EC28C0226D54A006533C2, - ); - isa = PBXGroup; - name = Classes; - refType = 4; - }; - 089C165CFE840E0CC02AAC07 = { - children = ( - 089C165DFE840E0CC02AAC07, - ); - isa = PBXVariantGroup; - name = InfoPlist.strings; - refType = 4; - }; - 089C165DFE840E0CC02AAC07 = { - fileEncoding = 10; - isa = PBXFileReference; - name = English; - path = English.lproj/InfoPlist.strings; - refType = 4; - }; - 089C165EFE840E0CC02AAC07 = { - fileRef = 089C165CFE840E0CC02AAC07; - isa = PBXBuildFile; - settings = { - }; - }; -//080 -//081 -//082 -//083 -//084 -//100 -//101 -//102 -//103 -//104 - 1058C7A0FEA54F0111CA2CBB = { - children = ( - 1058C7A1FEA54F0111CA2CBB, - ); - isa = PBXGroup; - name = "Linked Frameworks"; - refType = 4; - }; - 1058C7A1FEA54F0111CA2CBB = { - isa = PBXFrameworkReference; - name = Cocoa.framework; - path = /System/Library/Frameworks/Cocoa.framework; - refType = 0; - }; - 1058C7A2FEA54F0111CA2CBB = { - children = ( - 29B97325FDCFA39411CA2CEA, - 29B97324FDCFA39411CA2CEA, - 65DD378E028194AE000001D1, - ); - isa = PBXGroup; - name = "Other Frameworks"; - refType = 4; - }; - 1058C7A3FEA54F0111CA2CBB = { - fileRef = 1058C7A1FEA54F0111CA2CBB; - isa = PBXBuildFile; - settings = { - }; - }; -//100 -//101 -//102 -//103 -//104 -//170 -//171 -//172 -//173 -//174 - 17587328FF379C6511CA2CBB = { - isa = PBXApplicationReference; - path = "DNS Service Registration.app"; - refType = 3; - }; -//170 -//171 -//172 -//173 -//174 -//190 -//191 -//192 -//193 -//194 - 19C28FACFE9D520D11CA2CBB = { - children = ( - 17587328FF379C6511CA2CBB, - ); - isa = PBXGroup; - name = Products; - refType = 4; - }; -//190 -//191 -//192 -//193 -//194 -//290 -//291 -//292 -//293 -//294 - 29B97313FDCFA39411CA2CEA = { - buildStyles = ( - 4A9504CCFFE6A4B311CA0CBA, - 4A9504CDFFE6A4B311CA0CBA, - ); - isa = PBXProject; - mainGroup = 29B97314FDCFA39411CA2CEA; - projectDirPath = ""; - targets = ( - 29B97326FDCFA39411CA2CEA, - ); - }; - 29B97314FDCFA39411CA2CEA = { - children = ( - 080E96DDFE201D6D7F000001, - 29B97315FDCFA39411CA2CEA, - 29B97317FDCFA39411CA2CEA, - 29B97323FDCFA39411CA2CEA, - 19C28FACFE9D520D11CA2CBB, - ); - isa = PBXGroup; - name = "DNS Service Registration"; - path = ""; - refType = 4; - }; - 29B97315FDCFA39411CA2CEA = { - children = ( - 29B97316FDCFA39411CA2CEA, - ); - isa = PBXGroup; - name = "Other Sources"; - path = ""; - refType = 4; - }; - 29B97316FDCFA39411CA2CEA = { - isa = PBXFileReference; - path = main.m; - refType = 4; - }; - 29B97317FDCFA39411CA2CEA = { - children = ( - 29B97318FDCFA39411CA2CEA, - 089C165CFE840E0CC02AAC07, - ); - isa = PBXGroup; - name = Resources; - path = ""; - refType = 4; - }; - 29B97318FDCFA39411CA2CEA = { - children = ( - 29B97319FDCFA39411CA2CEA, - ); - isa = PBXVariantGroup; - name = MainMenu.nib; - path = ""; - refType = 4; - }; - 29B97319FDCFA39411CA2CEA = { - isa = PBXFileReference; - name = English; - path = English.lproj/MainMenu.nib; - refType = 4; - }; - 29B97323FDCFA39411CA2CEA = { - children = ( - 1058C7A0FEA54F0111CA2CBB, - 1058C7A2FEA54F0111CA2CBB, - ); - isa = PBXGroup; - name = Frameworks; - path = ""; - refType = 4; - }; - 29B97324FDCFA39411CA2CEA = { - isa = PBXFrameworkReference; - name = AppKit.framework; - path = /System/Library/Frameworks/AppKit.framework; - refType = 0; - }; - 29B97325FDCFA39411CA2CEA = { - isa = PBXFrameworkReference; - name = Foundation.framework; - path = /System/Library/Frameworks/Foundation.framework; - refType = 0; - }; - 29B97326FDCFA39411CA2CEA = { - buildPhases = ( - 29B97327FDCFA39411CA2CEA, - 29B97328FDCFA39411CA2CEA, - 29B9732BFDCFA39411CA2CEA, - 29B9732DFDCFA39411CA2CEA, - ); - buildSettings = { - FRAMEWORK_SEARCH_PATHS = ""; - HEADER_SEARCH_PATHS = ""; - INSTALL_PATH = "$(HOME)/Applications"; - LIBRARY_SEARCH_PATHS = ""; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - PRODUCT_NAME = "DNS Service Registration"; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; - WRAPPER_EXTENSION = app; - }; - dependencies = ( - ); - isa = PBXApplicationTarget; - name = "DNS Service Registration"; - productInstallPath = "$(HOME)/Applications"; - productName = "DNS Service Registration"; - productReference = 17587328FF379C6511CA2CBB; - productSettingsXML = " - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - DNS Service Registration - CFBundleIconFile - - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleVersion - 0.1 - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - -"; - shouldUseHeadermap = 1; - }; - 29B97327FDCFA39411CA2CEA = { - buildActionMask = 2147483647; - files = ( - 654EC28E0226D54A006533C2, - ); - isa = PBXHeadersBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 29B97328FDCFA39411CA2CEA = { - buildActionMask = 2147483647; - files = ( - 080E96DCFE201CFB7F000001, - 089C165EFE840E0CC02AAC07, - ); - isa = PBXResourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 29B9732BFDCFA39411CA2CEA = { - buildActionMask = 2147483647; - files = ( - 29B9732CFDCFA39411CA2CEA, - 654EC28F0226D54A006533C2, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - 29B9732CFDCFA39411CA2CEA = { - fileRef = 29B97316FDCFA39411CA2CEA; - isa = PBXBuildFile; - settings = { - ATTRIBUTES = ( - ); - }; - }; - 29B9732DFDCFA39411CA2CEA = { - buildActionMask = 2147483647; - files = ( - 1058C7A3FEA54F0111CA2CBB, - ); - isa = PBXFrameworksBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; -//290 -//291 -//292 -//293 -//294 -//4A0 -//4A1 -//4A2 -//4A3 -//4A4 - 4A9504CCFFE6A4B311CA0CBA = { - buildRules = ( - ); - buildSettings = { - COPY_PHASE_STRIP = NO; - OPTIMIZATION_CFLAGS = "-O0"; - }; - isa = PBXBuildStyle; - name = Development; - }; - 4A9504CDFFE6A4B311CA0CBA = { - buildRules = ( - ); - buildSettings = { - COPY_PHASE_STRIP = YES; - }; - isa = PBXBuildStyle; - name = Deployment; - }; -//4A0 -//4A1 -//4A2 -//4A3 -//4A4 -//650 -//651 -//652 -//653 -//654 - 654EC28C0226D54A006533C2 = { - isa = PBXFileReference; - path = RegistrationController.m; - refType = 4; - }; - 654EC28D0226D54A006533C2 = { - isa = PBXFileReference; - path = RegistrationController.h; - refType = 4; - }; - 654EC28E0226D54A006533C2 = { - fileRef = 654EC28D0226D54A006533C2; - isa = PBXBuildFile; - settings = { - }; - }; - 654EC28F0226D54A006533C2 = { - fileRef = 654EC28C0226D54A006533C2; - isa = PBXBuildFile; - settings = { - }; - }; - 65DD378E028194AE000001D1 = { - isa = PBXFrameworkReference; - name = CoreFoundation.framework; - path = /System/Library/Frameworks/CoreFoundation.framework; - refType = 0; - }; - }; - rootObject = 29B97313FDCFA39411CA2CEA; -} diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/InfoPlist.strings deleted file mode 100644 index 2aadfdc937f9b10aa5703db7e702c32f21be366b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 642 zcmcJMOAEp<5QOLKuLvGRd|kzhiVsi^7Q9N;T1Bf;YoY#l^-Fvlq$fc_vdQjbXLt5? zS5+k$jdavfzV_Pcsz57zDK^#_&rt`g!LB)nyw=<+IywAV3Emy(%%Mu;EU-H2sjUm> z6}UhX@<&*s7Q3h}9*>*ncv5(lE;{85dD*-%f09=PMKyD>dfHG+3~J``yzBMc?=9@% z&y3$17}e;yPVIS>^x;oF>im - - - - IBDocumentLocation - 32 111 356 240 0 0 1152 746 - IBEditorPositions - - 29 - 103 609 252 44 0 0 1152 746 - - IBFramework Version - 273.0 - IBOpenObjects - - 243 - 21 - - IBSystem Version - 6C30 - - diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib b/mDNSMacOSX/Applications/DNSServiceRegistration/English.lproj/MainMenu.nib/objects.nib deleted file mode 100644 index 705e77d75023c87108c7ca36a155b098c343ca3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9113 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 - -@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 deleted file mode 100644 index 6255179..0000000 --- a/mDNSMacOSX/Applications/DNSServiceRegistration/RegistrationController.m +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: RegistrationController.m,v $ -Revision 1.13 2003/08/12 19:55:07 cheshire -Update to APSL 2.0 - - */ - -#import "RegistrationController.h" - -#include - -void reg_reply ( - int errorCode, - void *context - ) -{ - // registration reply - printf("Got a reply from the server with error %d\n", errorCode); - return; -} - -void -MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info ) -{ - DNSServiceDiscovery_handleReply(msg); -} - -@implementation RegistrationController - -- (void)registerDefaults -{ - NSMutableDictionary *regDict = [NSMutableDictionary dictionary]; - - NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_ssh._tcp.", @"_tftp._tcp.", @"_http._tcp.", @"_printer._tcp.", @"_afpovertcp._tcp.", nil]; - NSArray *nameArray = [NSArray arrayWithObjects:@"My ftp Server", @"My Computer", @"Testing Boot Image", @"A Web Server", @"SteveÕs Printer", @"Company AppleShare Server", nil]; - NSArray *portArray = [NSArray arrayWithObjects:@"21", @"22", @"69", @"80", @"515", @"548", nil]; - NSArray *domainArray = [NSArray arrayWithObjects:@"", @"", @"", @"", @"", @"", nil]; - NSArray *textArray = [NSArray arrayWithObjects:@"", @"", @"image=mybootimage", @"path=/index.html", @"rn=lpt1", @"Vol=Public", nil]; - - [regDict setObject:typeArray forKey:@"SrvTypeKeys"]; - [regDict setObject:nameArray forKey:@"SrvNameKeys"]; - [regDict setObject:portArray forKey:@"SrvPortKeys"]; - [regDict setObject:domainArray forKey:@"SrvDomainKeys"]; - [regDict setObject:textArray forKey:@"SrvTextKeys"]; - - [[NSUserDefaults standardUserDefaults] registerDefaults:regDict]; -} - -- (id)init -{ - srvtypeKeys = [[NSMutableArray array] retain]; //Define arrays for Type, Domain, and Name - srvnameKeys = [[NSMutableArray array] retain]; - srvportKeys = [[NSMutableArray array] retain]; - srvdomainKeys = [[NSMutableArray array] retain]; - srvtextKeys = [[NSMutableArray array] retain]; - - registeredDict = [[NSMutableDictionary alloc] init]; - - [self registerDefaults]; - return [super init]; -} - -- (void)awakeFromNib //BrowserController startup procedure -{ - [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]]; - [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]]; - [srvportKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvPortKeys"]]; - [srvdomainKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvDomainKeys"]]; - [srvtextKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTextKeys"]]; - - [serviceDisplayTable reloadData]; //Reload (redraw) data in fields - -} - - - - - (IBAction)registerService:(id)sender -{ - int selectedRow = [serviceDisplayTable selectedRow]; - CFRunLoopSourceRef rls; - uint16_t registerPort; - CFMachPortRef cfMachPort; - CFMachPortContext context; - Boolean shouldFreeInfo; - dns_service_discovery_ref dns_client; - mach_port_t port; - - if (selectedRow < 0) { - return; - } - - context.version = 1; - context.info = 0; - context.retain = NULL; - context.release = NULL; - context.copyDescription = NULL; - - registerPort = [[srvportKeys objectAtIndex:selectedRow] intValue]; - - dns_client = DNSServiceRegistrationCreate - ( - [[srvnameKeys objectAtIndex:selectedRow] UTF8String], - [[srvtypeKeys objectAtIndex:selectedRow] UTF8String], - [[srvdomainKeys objectAtIndex:selectedRow] UTF8String], - registerPort, - [[srvtextKeys objectAtIndex:selectedRow] UTF8String], - reg_reply, - nil - ); - - port = DNSServiceDiscoveryMachPort(dns_client); - - if (port) { - - //printf("port is %d\n", port); - - cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo ); - - rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFRelease(rls); - [registeredDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)dns_client] forKey:[srvtypeKeys objectAtIndex:selectedRow]]; - } else { - printf("Could not obtain client port\n"); - } - -} - -- (IBAction)unregisterService:(id)sender -{ - int selectedRow = [serviceDisplayTable selectedRow]; - NSString *key = [srvtypeKeys objectAtIndex:selectedRow]; - - NSNumber *refPtr = [registeredDict objectForKey:key]; - dns_service_discovery_ref ref = (dns_service_discovery_ref)[refPtr unsignedIntValue]; - - if (ref) { - DNSServiceDiscoveryDeallocate(ref); - [registeredDict removeObjectForKey:key]; - } -} - --(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row -{ - if (row<0) return; -} - -- (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods -{ - return [srvtypeKeys count]; -} - -- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex -{ - if (theColumn == typeColumn) { - return [srvtypeKeys objectAtIndex:rowIndex]; - } - if (theColumn == nameColumn) { - return [srvnameKeys objectAtIndex:rowIndex]; - } - if (theColumn == portColumn) { - return [srvportKeys objectAtIndex:rowIndex]; - } - if (theColumn == domainColumn) { - return [srvdomainKeys objectAtIndex:rowIndex]; - } - if (theColumn == textColumn) { - return [srvtextKeys objectAtIndex:rowIndex]; - } - - return(0); -} //End of mandatory TableView methods - -- (IBAction)removeSelected:(id)sender -{ - // remove the selected row and force a refresh - - int selectedRow = [serviceDisplayTable selectedRow]; - - if (selectedRow) { - - [srvtypeKeys removeObjectAtIndex:selectedRow]; - [srvnameKeys removeObjectAtIndex:selectedRow]; - [srvportKeys removeObjectAtIndex:selectedRow]; - [srvdomainKeys removeObjectAtIndex:selectedRow]; - [srvtextKeys removeObjectAtIndex:selectedRow]; - - [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; - - [serviceDisplayTable reloadData]; - } -} - -- (IBAction)addNewService:(id)sender -{ - // add new entries from the edit fields to the arrays for the defaults - - if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length] && [[serviceDomainField stringValue] length]&& [[servicePortField stringValue] length]) { - [srvtypeKeys addObject:[serviceTypeField stringValue]]; - [srvnameKeys addObject:[serviceNameField stringValue]]; - [srvportKeys addObject:[servicePortField stringValue]]; - [srvdomainKeys addObject:[serviceDomainField stringValue]]; - [srvtextKeys addObject:[serviceTextField stringValue]]; - - [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvportKeys forKey:@"SrvPortKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvdomainKeys forKey:@"SrvDomainKeys"]; - [[NSUserDefaults standardUserDefaults] setObject:srvtextKeys forKey:@"SrvTextKeys"]; - - [serviceDisplayTable reloadData]; - } else { - NSBeep(); - } - -} - - - -@end diff --git a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m deleted file mode 100644 index 4aae5ed..0000000 --- a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.m +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: HAAutomounter.m,v $ -Revision 1.4 2003/08/12 19:55:08 cheshire -Update to APSL 2.0 - - */ - -#import "HAAutomounter.h" - -#import - -#include -#include "arpa/inet.h" -#include - -@implementation HAAutomounter - -- (id)init -{ - self = [super init]; - - browser = [[NSNetServiceBrowser alloc] init]; - - [browser setDelegate:self]; - - [browser searchForServicesOfType:@"_mountme._tcp." inDomain:@""]; - - [browser scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode]; - - return self; -} - -- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing -{ - if (resolver) { - [resolver release]; - } - - resolver = [[NSNetService alloc] initWithDomain:[aNetService domain] type:[aNetService type] name:[aNetService name]]; - [resolver setDelegate:self]; - [resolver scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode]; - - [resolver resolve]; -} - - -- (void)netServiceDidResolveAddress:(NSNetService *)sender; -{ - if ([[sender addresses] count]) { - - // URL mount the volume - NSData *addr = [[sender addresses] objectAtIndex:0]; - struct sockaddr_in *address = CFDataGetBytePtr((CFDataRef)addr); - NSString *ipAddr = [NSString stringWithCString:inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))]; - int port = ((struct sockaddr_in *)address)->sin_port; - NSArray *txtArray = [[sender protocolSpecificInformation] componentsSeparatedByString:@","]; - - if ([txtArray count] == 3) { - NSString *user = [txtArray objectAtIndex:0]; - NSString *password = [txtArray objectAtIndex:1]; - NSString *share = [txtArray objectAtIndex:2]; - - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%@@%@:%d/%@", user, password, ipAddr, port, share]]]; - } else { - NSLog(@"incompatible format for txt record, s/b user,password,share"); - } - - } else { - NSLog(@"No address %@", sender); - } -} - -- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing -{ - // unmount the volume -} - - -@end diff --git a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj b/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj deleted file mode 100644 index 7d654f8..0000000 --- a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.pbproj/project.pbxproj +++ /dev/null @@ -1,296 +0,0 @@ -// !$*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/CFSocket.c b/mDNSMacOSX/CFSocket.c index e4c230d..388cdf2 100644 --- a/mDNSMacOSX/CFSocket.c +++ b/mDNSMacOSX/CFSocket.c @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copright (c) 2002-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,29 +25,149 @@ Change History (most recent first): $Log: CFSocket.c,v $ -Revision 1.115.2.5 2005/01/28 05:02:06 cheshire - SUPan: Replace IP TTL 255 check with local-subnet check +Revision 1.157 2004/06/08 18:54:48 ksekar +: mDNSResponder leaks after exploring in Printer Setup Utility + +Revision 1.156 2004/06/05 00:04:26 cheshire +: wide-area domains should be returned in reg. domain enumeration + +Revision 1.155 2004/06/04 08:58:30 ksekar +: Keychain integration for secure dynamic update + +Revision 1.154 2004/05/31 22:22:28 ksekar +: wide-area domains should be returned in +reg. domain enumeration + +Revision 1.153 2004/05/26 17:06:33 cheshire +: Don't rely on CFSocketInvalidate() to remove RunLoopSource + +Revision 1.152 2004/05/18 23:51:26 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers -Revision 1.115.2.4 2004/04/23 00:34:06 cheshire -: mDNSResponder messages on wake +Revision 1.151 2004/05/17 21:46:34 cheshire +: When interface is turned off, browse "remove" events are delivered with interface index zero Take care to correctly update InterfaceIDs when a dormant interface comes back to life -Revision 1.115.2.3 2004/04/08 23:18:11 cheshire - When interface turned off, browse "remove" events delivered with interface index zero -Refinement from Bob Bradley: Should use "mDNS *const m" instead of referencing mDNSStorage directly +Revision 1.150 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.149 2004/05/13 03:55:14 ksekar +Fixed list traversal bug in FoundDefSearchDomain. + +Revision 1.148 2004/05/12 22:03:08 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.147 2004/05/12 02:03:25 ksekar +Non-local domains will only be browsed by default, and show up in +_browse domain enumeration, if they contain an _browse._dns-sd ptr record. + +Revision 1.146 2004/04/27 02:49:15 cheshire +: mDNSResponder leaks sockets on bind() error + +Revision 1.145 2004/04/21 03:08:03 cheshire +Rename 'alias' to more descriptive name 'primary' -Revision 1.115.2.2 2004/04/08 00:42:37 cheshire +Revision 1.144 2004/04/21 03:04:35 cheshire +Minor cleanup for clarity + +Revision 1.143 2004/04/21 03:03:30 cheshire +Preparation work: AddInterfaceToList() should return pointer to structure it creates + +Revision 1.142 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.141 2004/04/21 02:20:47 cheshire +Rename interface field 'CurrentlyActive' to more descriptive 'Exists' + +Revision 1.140 2004/04/14 23:09:29 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.139 2004/04/09 17:40:26 cheshire +Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field + +Revision 1.138 2004/04/09 16:37:16 cheshire +Suggestion from Bob Bradley: +Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers + +Revision 1.137 2004/04/08 00:59:55 cheshire When interface turned off, browse "remove" events delivered with interface index zero -Unify use of the InterfaceID field, and make code that walks the list respect the CurrentlyActive flag +Unify use of the InterfaceID field, and make code that walks the list respect the 'Exists' flag -Revision 1.115.2.1 2004/04/07 01:08:15 cheshire +Revision 1.136 2004/04/07 01:08:57 cheshire When interface turned off, browse "remove" events delivered with interface index zero +Revision 1.135 2004/03/19 01:01:03 ksekar +Fixed config file parsing to chop newline + +Revision 1.134 2004/03/13 01:57:34 ksekar +: DynDNS: Dynamic update of service records + +Revision 1.133 2004/02/02 22:46:56 cheshire +Move "CFRelease(dict);" inside the "if (dict)" check + +Revision 1.132 2004/01/28 02:30:08 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.131 2004/01/27 22:57:48 cheshire +: Need separate socket for issuing unicast queries + +Revision 1.130 2004/01/27 22:28:40 cheshire +: Time to prune obsolete code for listening on port 53 +Additional lingering port 53 code deleted + +Revision 1.129 2004/01/27 20:15:23 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.128 2004/01/24 23:58:17 cheshire +Change to use mDNSVal16() instead of shifting and ORing + +Revision 1.127 2004/01/24 04:59:16 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.126 2004/01/23 23:23:15 ksekar +Added TCP support for truncated unicast messages. + +Revision 1.125 2004/01/22 03:43:09 cheshire +Export constants like mDNSInterface_LocalOnly so that the client layers can use them + +Revision 1.124 2004/01/21 21:53:19 cheshire +: Don't try to receive unicast responses if we're not the first to bind to the UDP port + +Revision 1.123 2004/01/20 03:18:25 cheshire +Removed "LogMsg("Hey There!");" that evidently got checked in my mistake + +Revision 1.122 2003/12/17 20:43:59 cheshire +: Syslog messages saying "sendto failed" + +Revision 1.121 2003/12/13 03:05:28 ksekar +: DynDNS: Unicast query of service records + +Revision 1.120 2003/12/08 21:00:46 rpantos +Changes to support mDNSResponder on Linux. + +Revision 1.119 2003/12/03 02:35:15 cheshire +Also report value of m->timenow when logging sendto() failure + +Revision 1.118 2003/11/14 20:59:09 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.117 2003/11/08 22:18:29 cheshire +: Don't need to show process ID in *every* mDNSResponder syslog message + +Revision 1.116 2003/09/23 16:39:49 cheshire +When LogAllOperations is set, also report registration and deregistration of interfaces + Revision 1.115 2003/09/10 00:45:55 cheshire Don't log "sendto failed" errors during the first two minutes of startup Revision 1.114 2003/08/27 02:55:13 cheshire -: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down) +: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down) Revision 1.113 2003/08/19 22:20:00 cheshire Don't use IPv6 on interfaces that have a routable IPv4 address configured @@ -102,7 +224,7 @@ Revision 1.99 2003/08/05 20:13:52 cheshire 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 @@ -195,7 +317,7 @@ it should say "%lu", etc.) and improve error logging (use strerror() to include textual error message as well as numeric error in log messages). Revision 1.73 2003/05/21 17:56:29 ksekar -Bug #: : mDNSResponder doesn't watch for IPv6 address changes +: mDNSResponder doesn't watch for IPv6 address changes Revision 1.72 2003/05/14 18:48:41 cheshire mDNSResponder should be smarter about reconfigurations @@ -230,12 +352,12 @@ 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 @@ -253,25 +375,25 @@ 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 + mDNSResponder socket problems Revision 1.58 2003/03/06 01:43:04 cheshire -Bug #: 3189097 Additional debugging code in mDNSResponder + 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 + 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 + Additional debugging code in mDNSResponder Revision 1.55 2003/02/21 01:54:09 cheshire -Bug #: 3099194 mDNSResponder needs performance improvements + 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 + Xserve RAID needs to do interface-specific registrations Reviewed by: Josh Graessley, Bob Bradley Revision 1.53 2003/01/29 02:21:23 cheshire @@ -285,16 +407,16 @@ 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 + computer name changes not handled properly + service name changes are not properly handled + 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 + mDNSResponder is monitoring ServiceEntities instead of InterfaceEntities Revision 1.47 2002/09/21 20:44:51 zarzycki Added APSL info @@ -321,14 +443,6 @@ Minor code tidying // 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 @@ -336,15 +450,14 @@ Minor code tidying // 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 +#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 "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform #include -#include // For select() and close() -#include // For va_list support +#include // For select() and close() +#include // For va_list support #include #include #include @@ -353,15 +466,19 @@ Minor code tidying #include #include #include +#include // platform support for UTC time +#include // for inet_aton -#include // For IP_RECVTTL +#include // For IP_RECVTTL #ifndef IP_RECVTTL -#define IP_RECVTTL 24 /* bool; receive reception TTL w/dgram */ +#define IP_RECVTTL 24 // bool; receive reception TTL w/dgram #endif -#include // For n_long, required by below -#include // For IPTOS_LOWDELAY etc. -#include // For IN6_IFF_NOTREADY etc. +#include // For n_long, required by below +#include // For IPTOS_LOWDELAY etc. +#include // For IN6_IFF_NOTREADY etc. + +#include // Code contributed by Dave Heller: // Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will @@ -377,10 +494,40 @@ Minor code tidying #include #include +typedef struct AuthRecordListElem + { + struct AuthRecordListElem *next; + AuthRecord ar; + } AuthRecordListElem; + +typedef struct SearchListElem + { + struct SearchListElem *next; + domainname domain; + int flag; + DNSQuestion browseQ; + DNSQuestion registerQ; + AuthRecordListElem *AuthRecs; + } SearchListElem; + + // *************************************************************************** // Globals static mDNSu32 clockdivisor = 0; +static mDNSBool DNSConfigInitialized = mDNSfalse; +#define MAX_SEARCH_DOMAINS 32 + +// for domain enumeration and default browsing +static SearchListElem *SearchList = NULL; // where we search for _browse domains +static DNSQuestion DefBrowseDomainQ; // our local enumeration query for _browse domains +static DNameListElem *DefBrowseList = NULL; // cache of answers to above query (where we search for empty string browses) + +#define CONFIG_FILE "/etc/mDNSResponder.conf" +#define LH_KEYCHAIN_DESC "Lighthouse Shared Secret" +#define LH_KEYCHAIN_SERVICE "Lighthouse" +#define SYS_KEYCHAIN_PATH "/Library/Keychains/System.keychain" +#define LH_SUFFIX "members.mac.com." // *************************************************************************** // Macros @@ -395,56 +542,6 @@ static mDNSu32 clockdivisor = 0; // *************************************************************************** // 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; @@ -473,53 +570,59 @@ mDNSlocal int myIfIndexToName(u_short index, char* name) mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index) { NetworkInterfaceInfoOSX *i; - if (index == (uint32_t)~0) return((mDNSInterfaceID)~0); + if (index == (uint32_t)~0) return(mDNSInterface_LocalOnly); if (index) for (i = m->p->InterfaceList; i; i = i->next) - if (i->ifinfo.InterfaceID && i->scope_id == index) // Don't get tricked by inactive interfaces - return(i->ifinfo.InterfaceID); + // Don't get tricked by inactive interfaces with no InterfaceID set + if (i->ifinfo.InterfaceID && 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 == mDNSInterface_LocalOnly) return((mDNSu32)~0); if (id) for (i = m->p->InterfaceList; i; i = i->next) - // Don't use i->ifinfo.InterfaceID here because we want to find inactive interfaces where that's not set - if ((mDNSInterfaceID)i == id) - return i->scope_id; + // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces, which have no InterfaceID set + if ((mDNSInterfaceID)i == id) return(i->scope_id); return 0; } +// NOTE: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket" +// NOTE: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface" mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort) + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) { #pragma unused(m) + + // Note: For this platform we've adopted the convention that InterfaceIDs are secretly pointers + // to the NetworkInterfaceInfoOSX structure that holds the active sockets. The mDNSCore code + // doesn't know that and doesn't need to know that -- it just treats InterfaceIDs as opaque identifiers. NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID; + char *ifa_name = info ? info->ifa_name : "unicast"; struct sockaddr_storage to; - int s, err; - - if (!InterfaceID) { LogMsg("mDNSPlatformSendUDP ERROR! Cannot send from zero InterfaceID"); return mStatus_BadParamErr; } + int s = -1, err; 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; + 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; + s = info ? info->ss.sktv4 : m->p->unicastsockets.sktv4; } 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; + 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 ? info->scope_id : 0; + s = info ? info->ss.sktv6 : m->p->unicastsockets.sktv6; } else { @@ -527,24 +630,12 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *co 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); + InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), 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]); + InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort)); // 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 @@ -553,14 +644,14 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *co 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 + // 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 + // Don't report EHOSTUNREACH in the first three minutes after boot // This is because mDNSResponder intentionally starts up early in the boot process (See ) // but this means that sometimes it starts before configd has finished setting up the multicast routing entries. - if (errno == EHOSTUNREACH && (mDNSu32)(m->timenow) < (mDNSu32)(mDNSPlatformOneSecond * 120)) return(err); - LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %s/%ld to %#a:%d skt %d error %d errno %d (%s)", - InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1], s, err, errno, strerror(errno)); + if (errno == EHOSTUNREACH && (mDNSu32)(m->timenow) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(err); + LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %s/%ld to %#a:%d skt %d error %d errno %d (%s) %lu", + InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); return(err); } @@ -577,7 +668,7 @@ mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, 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 + *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; @@ -648,13 +739,16 @@ mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, return(n); } +// On entry, context points to our CFSocketSet +// If ss->info is NULL, we received this packet on our anonymous unicast socket +// If ss->info is non-NULL, we received this packet on port 5353 on the indicated interface 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; - mDNSInterfaceID InterfaceID = info->ifinfo.InterfaceID; + const CFSocketSet *ss = (const CFSocketSet *)context; + mDNS *const m = ss->m; + const mDNSInterfaceID InterfaceID = ss->info ? ss->info->ifinfo.InterfaceID : mDNSNULL; DNSMessage packet; struct sockaddr_storage from; size_t fromlen = sizeof(from); @@ -662,26 +756,20 @@ mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBack int err, s1 = -1, skt = CFSocketGetNative(cfs); int count = 0; - (void)address; // Parameter not used - (void)data; // Parameter not used + (void)address; // Parameter not used + (void)data; // Parameter not used - if (CallBackType != kCFSocketReadCallBack) LogMsg("myCFSocketCallBack: Why is CallBackType %d not kCFSocketReadCallBack?", CallBackType); + 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 (cfs == ss->cfsv4) s1 = ss->sktv4; + else if (cfs == ss->cfsv6) s1 = ss->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); + LogMsg("myCFSocketCallBack: cfsv4 %p, sktv4 %d", ss->cfsv4, ss->sktv4); + LogMsg("myCFSocketCallBack: cfsv6 %p, sktv6 %d", ss->cfsv6, ss->sktv6); } mDNSu8 ttl; @@ -708,33 +796,22 @@ mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBack return; } - if (mDNSAddrIsDNSMulticast(&destAddr)) - { - // 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); - } + // 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 (!ss->info) + verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on unicast socket", &senderAddr, &destAddr); + else if (!strcmp(ss->info->ifa_name, packetifname)) + verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s", + &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifa_name); else { - verbosedebugf("myCFSocketCallBack got a unicast from %#a to %#a on interface %#a/%s packetifname %s)", - &senderAddr, &destAddr, &info->ifinfo.ip, info->ifa_name, packetifname); - // Note: For unicast packets, try to find the matching mDNSCore interface object - // (though we may not be able to, for unicast packets received over something like a PPP link) - NetworkInterfaceInfo *intf = m->HostInterfaces; - while (intf && strcmp(intf->ifname, packetifname)) intf = intf->next; - if (intf) InterfaceID = intf->InterfaceID; + verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s (Ignored -- really arrived on interface %s)", + &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifa_name, packetifname); + return; } if (err < (int)sizeof(DNSMessageHeader)) { debugf("myCFSocketCallBack packet length (%d) too short", err); return; } @@ -772,7 +849,177 @@ mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBack 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 + } + } + +// TCP socket support for unicast DNS and Dynamic DNS Update + +typedef struct + { + TCPConnectionCallback callback; + void *context; + int connected; + } tcpInfo_t; + +mDNSlocal void tcpCFSocketCallback(CFSocketRef cfs, CFSocketCallBackType CallbackType, CFDataRef address, + const void *data, void *context) + { + #pragma unused(CallbackType, address, data) + mDNSBool connect = mDNSfalse; + + tcpInfo_t *info = context; + if (!info->connected) + { + connect = mDNStrue; + info->connected = mDNStrue; // prevent connected flag from being set in future callbacks } + info->callback(CFSocketGetNative(cfs), info->context, connect); + // NOTE: the callback may call CloseConnection here, which frees the context structure! + } + +mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context, int *descriptor) + { + int sd, on = 1; // "on" for setsockopt + struct sockaddr_in saddr; + CFSocketContext cfContext = { 0, NULL, 0, 0, 0 }; + tcpInfo_t *info; + CFSocketRef sr; + CFRunLoopSourceRef rls; + CFOptionFlags srFlags; + + (void)InterfaceID; //!!!KRS use this if non-zero!!! + + *descriptor = 0; + if (dst->type != mDNSAddrType_IPv4) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - attempt to connect to an IPv6 address: opperation not supported"); + return mStatus_UnknownErr; + } + + sd = socket(AF_INET, SOCK_STREAM, 0); + if (sd < 0) + { + LogMsg("ERROR: socket; %s", strerror(errno)); + return mStatus_UnknownErr; + } + // set non-blocking + if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) + { + LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno)); + return mStatus_UnknownErr; + } + + // receive interface identifiers + if (setsockopt(sd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)) < 0) + { + LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); + return mStatus_UnknownErr; + } + // set up CF wrapper, add to Run Loop + info = mallocL("mDNSPlatformTCPConnect", sizeof(tcpInfo_t)); + info->callback = callback; + info->context = context; + cfContext.info = info; + sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketReadCallBack | kCFSocketConnectCallBack, + tcpCFSocketCallback, &cfContext); + if (!sr) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - CFSocketRefCreateWithNative failed"); + freeL("mDNSPlatformTCPConnect", info); + return mStatus_UnknownErr; + } + + // prevent closing of native socket + srFlags = CFSocketGetSocketFlags(sr); + CFSocketSetSocketFlags(sr, srFlags & (~kCFSocketCloseOnInvalidate)); + + rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sr, 0); + if (!rls) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - CFSocketCreateRunLoopSource failed"); + freeL("mDNSPlatformTCPConnect", info); + return mStatus_UnknownErr; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + + // initiate connection wth peer + bzero(&saddr, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = dstport.NotAnInteger; + memcpy(&saddr.sin_addr, &dst->ip.v4.NotAnInteger, sizeof(saddr.sin_addr)); + if (connect(sd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) + { + if (errno == EINPROGRESS) + { + info->connected = 0; + *descriptor= sd; + return mStatus_ConnectionPending; + } + LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: %s", strerror(errno)); + freeL("mDNSPlatformTCPConnect", info); + CFSocketInvalidate(sr); + return mStatus_ConnectionFailed; + } + info->connected = 1; + *descriptor = sd; + return mStatus_ConnectionEstablished; + } + +mDNSexport void mDNSPlatformTCPCloseConnection(int sd) + { + CFSocketContext cfContext; + tcpInfo_t *info; + CFSocketRef sr; + + // get the CFSocket for the descriptor, if it exists + sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, NULL, NULL, NULL); + if (!sr) + { + LogMsg("ERROR: mDNSPlatformTCPCloseConnection - attempt to close a socket that was not properly created"); + return; + } + CFSocketGetContext(sr, &cfContext); + if (!cfContext.info) + { + LogMsg("ERROR: mDNSPlatformTCPCloseConnection - could not retreive tcpInfo from socket context"); + CFRelease(sr); + return; + } + CFRelease(sr); // this only releases the copy we allocated with CreateWithNative above + + info = cfContext.info; + CFSocketInvalidate(sr); + CFRelease(sr); + close(sd); + freeL("mDNSPlatformTCPCloseConnection", info); + } + +mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) + { + int nread = recv(sd, buf, buflen, 0); + if (nread < 0) + { + if (errno == EAGAIN) return 0; // no data available (call would block) + LogMsg("ERROR: mDNSPlatformReadTCP - recv: %s", strerror(errno)); + return -1; + } + return nread; + } + +mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len) + { + int nsent = send(sd, msg, len, 0); + + if (nsent < 0) + { + if (errno == EAGAIN) return 0; // blocked + LogMsg("ERROR: mDNSPlatformWriteTCP - sendL %s", strerror(errno)); + return -1; + } + return nsent; } // This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel @@ -798,60 +1045,70 @@ mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) } } -mDNSlocal mStatus SetupSocket(NetworkInterfaceInfoOSX *i, mDNSIPPort port, int *s, CFSocketRef *c) +// If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface +// If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries +mDNSlocal mStatus SetupSocket(CFSocketSet *cp, mDNSIPPort port, const mDNSAddr *ifaddr, u_short sa_family) { + int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; + CFSocketRef *c = (sa_family == AF_INET) ? &cp->cfsv4 : &cp->cfsv6; + CFRunLoopSourceRef *r = (sa_family == AF_INET) ? &cp->rlsv4 : &cp->rlsv6; const int on = 1; const int twofivefive = 255; + mStatus err = mStatus_NoError; + char *errstr = mDNSNULL; 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); + int skt = socket(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); } + // ... with a shared UDP port, if it's for multicast receiving + if (port.NotAnInteger) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; } - if (i->sa_family == AF_INET) + if (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); } + if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; } // 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); } + if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; } // 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); } + // Add multicast group membership on this interface, if it's for multicast receiving + if (port.NotAnInteger) + { + struct in_addr addr = { ifaddr->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) { errstr = "setsockopt - IP_ADD_MEMBERSHIP"; goto fail; } + + // Specify outgoing interface too + err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)); + if (err < 0) { errstr = "setsockopt - IP_MULTICAST_IF"; goto fail; } + } // 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); } + if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; } // 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); } + if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; } // 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); } + if (err < 0) { errstr = "setsockopt - IP_TOS"; goto fail; } // And start listening for packets struct sockaddr_in listening_sockaddr; @@ -859,83 +1116,84 @@ mDNSlocal mStatus SetupSocket(NetworkInterfaceInfoOSX *i, mDNSIPPort port, int * 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); - } + if (err) { errstr = "bind"; goto fail; } } - else if (i->sa_family == AF_INET6) + else if (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); } + if (err < 0) { errstr = "setsockopt - IPV6_PKTINFO"; goto fail; } // 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); } + if (err < 0) { errstr = "setsockopt - IPV6_HOPLIMIT"; goto fail; } // 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); } + if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; } - // 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); } + if (port.NotAnInteger) + { + // Add multicast group membership on this interface, if it's for multicast receiving + int interface_id = if_nametoindex(cp->info->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) { errstr = "setsockopt - IPV6_JOIN_GROUP"; goto fail; } + + // Specify outgoing interface too + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interface_id, sizeof(interface_id)); + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_IF"; goto fail; } + } // 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); } + if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; } // 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); } + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; } // 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); } + if (err < 0) { errstr = "setsockopt - IPV6_TCLASS"; goto fail; } #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); } + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; } // 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_len = sizeof(listening_sockaddr6); listening_sockaddr6.sin6_family = AF_INET6; listening_sockaddr6.sin6_port = port.NotAnInteger; - listening_sockaddr6.sin6_flowinfo = 0; + 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; + 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); } + if (err) { errstr = "bind"; goto fail; } } 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 }; + CFSocketContext myCFSocketContext = { 0, cp, NULL, NULL, NULL }; *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext); - CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFRelease(rls); + *r = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), *r, kCFRunLoopDefaultMode); return(err); + +fail: + LogMsg("%s error %ld errno %d (%s)", errstr, err, errno, strerror(errno)); + close(skt); + return(err); } mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) @@ -962,7 +1220,7 @@ mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) } } -mDNSlocal mStatus AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa) +mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa) { mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); mDNSAddr ip; @@ -972,49 +1230,44 @@ mDNSlocal mStatus AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa) 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); + (*p)->Exists = mDNStrue; + return(*p); } debugf("AddInterfaceToList: Making new interface %u with address %#a", scope_id, &ip); NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i)); - if (!i) return(-1); + if (!i) return(mDNSNULL); i->ifa_name = (char *)mallocL("NetworkInterfaceInfoOSX name", strlen(ifa->ifa_name) + 1); - if (!i->ifa_name) { freeL("NetworkInterfaceInfoOSX", i); return(-1); } + if (!i->ifa_name) { freeL("NetworkInterfaceInfoOSX", i); return(mDNSNULL); } strcpy(i->ifa_name, ifa->ifa_name); + bzero(&i->ifinfo.uDNS_info, sizeof(uDNS_NetworkInterfaceInfo)); i->ifinfo.InterfaceID = mDNSNULL; i->ifinfo.ip = ip; - SetupAddr(&i->ifinfo.mask, ifa->ifa_netmask); - strncpy(i->ifinfo.ifname, ifa->ifa_name, sizeof(i->ifinfo.ifname)); - i->ifinfo.ifname[sizeof(i->ifinfo.ifname)-1] = 0; i->ifinfo.Advertise = m->AdvertiseLocalAddresses; - i->ifinfo.TxAndRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList - + i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList + i->next = mDNSNULL; - i->m = m; + i->Exists = mDNStrue; 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); + i->Multicast = (ifa->ifa_flags & IFF_MULTICAST) && !(ifa->ifa_flags & IFF_POINTOPOINT); + + i->ss.m = m; + i->ss.info = i; + i->ss.sktv4 = i->ss.sktv6 = -1; + i->ss.cfsv4 = i->ss.cfsv6 = NULL; + i->ss.rlsv4 = i->ss.rlsv6 = NULL; + *p = i; - return(0); + return(i); } 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->Exists && 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); @@ -1049,6 +1302,7 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) debugf("Updating m->hostlabel to %#s", hostlabel.c); m->p->userhostlabel = m->hostlabel = hostlabel; mDNS_GenerateFQDN(m); + if (mDNS_DNSRegistered(m)) mDNS_GenerateGlobalFQDN(m); } while (ifa) @@ -1066,6 +1320,9 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) 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_MULTICAST)) + debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface not IFF_MULTICAST", + 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); @@ -1073,43 +1330,42 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) 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_UP) + if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) { - if (ifa->ifa_flags & IFF_LOOPBACK) - theLoopback = ifa; - else + 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))) { - AddInterfaceToList(m, ifa); - if (ifa->ifa_addr->sa_family == AF_INET) - foundav4 = mDNStrue; + 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 + // 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. + // Now the list is complete, set the McastTxRx 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, @@ -1119,13 +1375,13 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) // so we are willing to make that sacrifice. NetworkInterfaceInfoOSX *i; for (i = m->p->InterfaceList; i; i = i->next) - if (i->CurrentlyActive) + if (i->Exists) { - mDNSBool txrx = ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); - if (i->ifinfo.TxAndRx != txrx) + mDNSBool txrx = i->Multicast && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); + if (i->ifinfo.McastTxRx != txrx) { - i->ifinfo.TxAndRx = txrx; - i->CurrentlyActive = 2; // State change; need to deregister and reregister this interface + i->ifinfo.McastTxRx = txrx; + i->Exists = 2; // State change; need to deregister and reregister this interface } } @@ -1137,8 +1393,7 @@ mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, char { NetworkInterfaceInfoOSX *i; for (i = m->p->InterfaceList; i; i = i->next) - if (!strcmp(i->ifa_name, ifname) && - i->CurrentlyActive && + if (i->Exists && !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); @@ -1149,46 +1404,45 @@ mDNSlocal void SetupActiveInterfaces(mDNS *const m) { NetworkInterfaceInfoOSX *i; for (i = m->p->InterfaceList; i; i = i->next) - if (i->CurrentlyActive) + if (i->Exists) { - 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) + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); + if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifa_name); + + if (n->InterfaceID && n->InterfaceID != (mDNSInterfaceID)primary) // Sanity check { - LogMsg("SetupActiveInterfaces ERROR! n->InterfaceID %p != alias %p", n->InterfaceID, alias); + LogMsg("SetupActiveInterfaces ERROR! n->InterfaceID %p != primary %p", n->InterfaceID, primary); n->InterfaceID = mDNSNULL; } - + if (!n->InterfaceID) { - n->InterfaceID = (mDNSInterfaceID)alias; + // NOTE: If n->InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface, + // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. + // If n->InterfaceID is NOT set, then we haven't registered it and we should not try to deregister it + n->InterfaceID = (mDNSInterfaceID)primary; 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)" : ""); + LogOperation("SetupActiveInterfaces: Registered %s(%lu) InterfaceID %p %#a%s", + i->ifa_name, i->scope_id, primary, &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); + + if (!n->McastTxRx) + debugf("SetupActiveInterfaces: No Tx/Rx on %s(%lu) InterfaceID %p %#a", i->ifa_name, i->scope_id, primary, &n->ip); else { - if (i->sa_family == AF_INET && alias->sktv4 == -1) + if (i->sa_family == AF_INET && primary->ss.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); + mStatus err = SetupSocket(&primary->ss, MulticastDNSPort, &i->ifinfo.ip, AF_INET); + if (err == 0) debugf("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a", primary->ss.sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); + else LogMsg("SetupActiveInterfaces: v4 socket%2d %s(%lu) InterfaceID %p %#a FAILED", primary->ss.sktv4, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); } - if (i->sa_family == AF_INET6 && alias->sktv6 == -1) + if (i->sa_family == AF_INET6 && primary->ss.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); + mStatus err = SetupSocket(&primary->ss, MulticastDNSPort, &i->ifinfo.ip, AF_INET6); + if (err == 0) debugf("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a", primary->ss.sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); + else LogMsg("SetupActiveInterfaces: v6 socket%2d %s(%lu) InterfaceID %p %#a FAILED", primary->ss.sktv6, i->ifa_name, i->scope_id, n->InterfaceID, &n->ip); } } } @@ -1198,24 +1452,26 @@ mDNSlocal void MarkAllInterfacesInactive(mDNS *const m) { NetworkInterfaceInfoOSX *i; for (i = m->p->InterfaceList; i; i = i->next) - i->CurrentlyActive = mDNSfalse; + i->Exists = mDNSfalse; } -mDNSlocal mDNSu32 NumCacheRecordsForInterfaceID(mDNS *const m, mDNSInterfaceID id) +mDNSlocal void CloseSocketSet(CFSocketSet *ss) { - mDNSu32 slot, used = 0; - CacheRecord *rr; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) - if (rr->resrec.InterfaceID == id) used++; - return(used); + // 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 (ss->cfsv4) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), ss->rlsv4, kCFRunLoopDefaultMode); CFRelease(ss->rlsv4); CFSocketInvalidate(ss->cfsv4); CFRelease(ss->cfsv4); } + if (ss->cfsv6) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), ss->rlsv6, kCFRunLoopDefaultMode); CFRelease(ss->rlsv6); CFSocketInvalidate(ss->cfsv6); CFRelease(ss->cfsv6); } + ss->sktv4 = ss->sktv6 = -1; + ss->cfsv4 = ss->cfsv6 = NULL; + ss->rlsv4 = ss->rlsv6 = NULL; } 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 also have to deregister it if the primary 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 @@ -1224,15 +1480,18 @@ mDNSlocal void ClearInactiveInterfaces(mDNS *const m) for (i = m->p->InterfaceList; i; i = i->next) { // 1. If this interface is no longer active, or its InterfaceID is changing, deregister it - NetworkInterfaceInfoOSX *alias = (NetworkInterfaceInfoOSX *)(i->ifinfo.InterfaceID); - NetworkInterfaceInfoOSX *newalias = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); - if (!newalias) newalias = i; - if (i->ifinfo.InterfaceID && (!i->CurrentlyActive || (alias && !alias->CurrentlyActive) || i->CurrentlyActive == 2 || newalias != alias)) - { - debugf("ClearInactiveInterfaces: Deregistering %#a", &i->ifinfo.ip); - mDNS_DeregisterInterface(m, &i->ifinfo); - i->ifinfo.InterfaceID = mDNSNULL; - } + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); + if (i->ifinfo.InterfaceID) + if (i->Exists == 0 || i->Exists == 2 || i->ifinfo.InterfaceID != (mDNSInterfaceID)primary) + { + LogOperation("ClearInactiveInterfaces: Deregistering %s(%lu) InterfaceID %p %#a%s", + i->ifa_name, i->scope_id, i->ifinfo.InterfaceID, &i->ifinfo.ip, i->ifinfo.InterfaceActive ? " (Primary)" : ""); + mDNS_DeregisterInterface(m, &i->ifinfo); + i->ifinfo.InterfaceID = mDNSNULL; + // NOTE: If n->InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface, + // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. + // If n->InterfaceID is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it. + } } // Second pass: @@ -1243,21 +1502,9 @@ mDNSlocal void ClearInactiveInterfaces(mDNS *const m) 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; - + CloseSocketSet(&i->ss); // 3. If no longer active, delete interface from list and free memory - if (!i->CurrentlyActive && NumCacheRecordsForInterfaceID(m, (mDNSInterfaceID)i) == 0) + if (!i->Exists && NumCacheRecordsForInterfaceID(m, (mDNSInterfaceID)i) == 0) { debugf("ClearInactiveInterfaces: Deleting %#a", &i->ifinfo.ip); *p = i->next; @@ -1269,10 +1516,273 @@ mDNSlocal void ClearInactiveInterfaces(mDNS *const m) } } + +mDNSlocal mStatus RegisterNameServers(mDNS *const m, CFDictionaryRef dict) + { + int i, count; + CFArrayRef values; + char buf[256]; + mDNSv4Addr saddr; + CFStringRef s; + + + mDNS_DeregisterDNSList(m); // deregister orig list + values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses); + if (!values) return mStatus_NoError; + + count = CFArrayGetCount(values); + for (i = 0; i < count; i++) + { + s = CFArrayGetValueAtIndex(values, i); + if (!s) { LogMsg("ERROR: RegisterNameServers - CFArrayGetValueAtIndex"); break; } + if (!CFStringGetCString(s, buf, 256, kCFStringEncodingASCII)) + { + LogMsg("ERROR: RegisterNameServers - CFStringGetCString"); + continue; + } + if (!inet_aton(buf, (struct in_addr *)saddr.b)) + { + LogMsg("ERROR: RegisterNameServers - invalid address string %s", buf); + continue; + } + mDNS_RegisterDNS(m, &saddr); + } + return mStatus_NoError; + } + +mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + (void)m; // unused + AuthRecordListElem *elem = rr->RecordContext; + if (result == mStatus_MemFree) freeL("FreeARElemCallback", elem); + } + +mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + SearchListElem *slElem = question->QuestionContext; + AuthRecordListElem *arElem, *ptr, *prev; + AuthRecord *dereg; + char *name; + mStatus err; + + if (AddRecord) + { + arElem = mallocL("FoundDomain - arElem", sizeof(AuthRecordListElem)); + if (!arElem) { LogMsg("ERROR: malloc"); return; } + mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, arElem); + if (question == &slElem->browseQ) name = "_browse._dns-sd._udp.local."; + else name = "_register._dns-sd._udp.local."; + MakeDomainNameFromDNSNameString(&arElem->ar.resrec.name, name); + strcpy(arElem->ar.resrec.rdata->u.name.c, answer->rdata->u.name.c); + err = mDNS_Register(m, &arElem->ar); + if (err) + { + LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); + freeL("FoundDomain - arElem", arElem); + return; + } + arElem->next = slElem->AuthRecs; + slElem->AuthRecs = arElem; + } + else + { + ptr = slElem->AuthRecs; + prev = NULL; + while (ptr) + { + if (SameDomainName(&ptr->ar.resrec.name, &answer->name) && SameDomainName(&ptr->ar.resrec.rdata->u.name, &answer->rdata->u.name)) + { + debugf("Deregistering PTR %s -> %s", ptr->ar.resrec.name.c, ptr->ar.resrec.rdata->u.name.c); + dereg = &ptr->ar; + if (prev) prev->next = ptr->next; + else slElem->AuthRecs = ptr->next; + ptr = ptr->next; + err = mDNS_Deregister(m, dereg); + if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err); + } + else + { + prev = ptr; + ptr = ptr->next; + } + } + } + } + +mDNSlocal mStatus RegisterSearchDomains(mDNS *const m, CFDictionaryRef dict) + { + int i, count; + CFArrayRef values; + domainname domain; + char buf[MAX_ESCAPED_DOMAIN_NAME]; + CFStringRef s; + SearchListElem *new, *ptr, *prev, *freeSLPtr; + AuthRecordListElem *arList; + mStatus err; + + // step 1: mark each elem for removal (-1) + for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag = -1; + + values = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains); + if (values) + { + count = CFArrayGetCount(values); + for (i = 0; i < count; i++) + { + s = CFArrayGetValueAtIndex(values, i); + if (!s) { LogMsg("ERROR: RegisterNameServers - CFArrayGetValueAtIndex"); break; } + if (!CFStringGetCString(s, buf, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingASCII)) + { + LogMsg("ERROR: RegisterNameServers - CFStringGetCString"); + continue; + } + if (!MakeDomainNameFromDNSNameString(&domain, buf)) + { + LogMsg("ERROR: RegisterNameServers - invalid search domain %s", buf); + continue; + } + // if domain is in list, mark as pre-existent (0) + for (ptr = SearchList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->domain, &domain)) { ptr->flag = 0; break; } + + // if domain not in list, add to list, mark as add (1) + if (!ptr) + { + new = mallocL("RegisterSearchDomains - SearchListElem", sizeof(SearchListElem)); + if (!new) { LogMsg("ERROR: RegisterSearchDomains - malloc"); return mStatus_UnknownErr; } + bzero(new, sizeof(SearchListElem)); + strcpy(new->domain.c, domain.c); + new->flag = 1; // add + new->next = SearchList; + SearchList = new; + } + } + } + // delete elems marked for removal, do queries for elems marked add + prev = NULL; + ptr = SearchList; + while (ptr) + { + if (ptr->flag == -1) // remove + { + mDNS_StopQuery(m, &ptr->browseQ); + mDNS_StopQuery(m, &ptr->registerQ); + // deregister records generated from answers to the query + arList = ptr->AuthRecs; + ptr->AuthRecs = NULL; + while (arList) + { + AuthRecord *dereg = &arList->ar; + arList = arList->next; + debugf("Deregistering PTR %s -> %s", dereg->resrec.name.c, dereg->resrec.rdata->u.name.c); + err = mDNS_Deregister(m, dereg); + if (err) LogMsg("ERROR: RegisterSearchDomains mDNS_Deregister returned %d", err); + } + + // remove elem from list, delete + if (prev) prev->next = ptr->next; + else SearchList = ptr->next; + freeSLPtr = ptr; + ptr = ptr->next; + freeL("RegisterNameServers - freeSLPtr", freeSLPtr); + continue; + } + + if (ptr->flag == 1) // add + { + err = mDNS_GetDomains(m, &ptr->browseQ, mDNS_DomainTypeBrowse, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + if (err) LogMsg("ERROR: RegisterNameServers - mDNS_DomainTypeBrowse, %d", err); + + err = mDNS_GetDomains(m, &ptr->registerQ, mDNS_DomainTypeRegistration, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + if (err) LogMsg("ERROR: RegisterNameServers - mDNS_DomainTypeRegistration, %d", err); + ptr->flag = 0; + } + + if (ptr->flag) { LogMsg("RegisterNameServers - unknown flag %d. Skipping.", ptr->flag); } + + prev = ptr; + ptr = ptr->next; + } + + return mStatus_NoError; + } + +// key must be kSCPropNetDNSServerAddresses or kSCPropNetDNSSearchDomains +mDNSlocal mStatus RegisterDNSConfig(mDNS *const m, CFDictionaryRef dict, const CFStringRef key) + { + if (key == kSCPropNetDNSSearchDomains) return RegisterSearchDomains(m, dict); + if (key == kSCPropNetDNSServerAddresses) return RegisterNameServers(m, dict); + LogMsg("ERROR: RegisterDNSConfig - bad key"); return mStatus_UnknownErr; + } + + +mDNSlocal void DNSConfigChanged(SCDynamicStoreRef session, CFArrayRef changes, void *context) + { + mDNS *m = context; + CFDictionaryRef dict; + CFStringRef key; + + if (DNSConfigInitialized && (!changes || CFArrayGetCount(changes) == 0)) return; + + //!!!KRS fixme - we need a list of registerd servers. this wholesale + // dereg doesn't work if there's an error and we bail out before registering the new list + + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); + if (!key) { LogMsg("ERROR: DNSConfigChanged - SCDynamicStoreKeyCreateNetworkGlobalEntity"); return; } + dict = SCDynamicStoreCopyValue(session, key); + CFRelease(key); + if (dict) + { + RegisterDNSConfig(m, dict, kSCPropNetDNSServerAddresses); + RegisterDNSConfig(m, dict, kSCPropNetDNSSearchDomains); + CFRelease(dict); + } + if (mDNS_DNSRegistered(m)) mDNS_GenerateGlobalFQDN(m); + // no-op if label & domain are unchanged + } + +mDNSlocal mStatus WatchForDNSChanges(mDNS *const m) + { + CFStringRef key; + CFMutableArrayRef keyList; + CFRunLoopSourceRef rls; + SCDynamicStoreRef session; + SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; + + session = SCDynamicStoreCreate(NULL, CFSTR("trackDNS"), DNSConfigChanged, &context); + if (!session) { LogMsg("ERROR: WatchForDNSChanges - SCDynamicStoreCreate"); return mStatus_UnknownErr; } + + keyList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!keyList) { LogMsg("ERROR: WatchForDNSChanges - CFArrayCreateMutable"); return mStatus_UnknownErr; } + + // create a pattern that matches the global DNS dictionary key + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); + if (!key) { LogMsg("ERROR: WatchForDNSChanges - SCDynamicStoreKeyCreateNetworkGlobalEntity"); return mStatus_UnknownErr; } + + CFArrayAppendValue(keyList, key); + CFRelease(key); + + // set the keys for our DynamicStore session + SCDynamicStoreSetNotificationKeys(session, keyList, NULL); + CFRelease(keyList); + + // create a CFRunLoopSource for our DynamicStore session + rls = SCDynamicStoreCreateRunLoopSource(NULL, session, 0); + if (!rls) { LogMsg("ERROR: WatchForDNSChanges - SCDynamicStoreCreateRunLoopSource"); return mStatus_UnknownErr; } + + // add the run loop source to our current run loop + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); + CFRelease(rls); + + // get initial configuration + DNSConfigChanged(session, NULL, m); + return mStatus_NoError; + } + mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) { - (void)store; // Parameter not used - (void)changedKeys; // Parameter not used + (void)store; // Parameter not used + (void)changedKeys; // Parameter not used debugf("*** Network Configuration Change ***"); mDNS *const m = (mDNS *const)context; @@ -1339,7 +1849,7 @@ exit: 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 + (void)service; // Parameter not used switch(messageType) { case kIOMessageCanSystemPowerOff: debugf("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 @@ -1367,10 +1877,130 @@ mDNSlocal mStatus WatchForPowerChanges(mDNS *const m) return(-1); } +mDNSexport mDNSBool haveSecInfo = mDNSfalse; // this must go away once we have full keychain integration +mDNSlocal void GetAuthInfoFromKeychainItem(mDNS *m, SecKeychainItemRef item) + { + OSStatus err; + mDNSu32 infoTag = kSecAccountItemAttr; + mDNSu32 infoFmt = 0; // string + SecKeychainAttributeInfo info; + SecKeychainAttributeList *authAttrList = NULL; + void *data; + mDNSu32 dataLen; + + mStatus regErr; + char accountName[MAX_ESCAPED_DOMAIN_NAME]; + domainname zone; + AuthRecord *rrReg, *rrBrowse; + + info.count = 1; + info.tag = &infoTag; + info.format = &infoFmt; + + err = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &authAttrList, &dataLen, &data); + if (err) { LogMsg("SecKeychainItemCopyAttributesAndData returned error %d", err); return; } + + // copy account name + if (!authAttrList->count || authAttrList->attr->tag != kSecAccountItemAttr) + { LogMsg("Received bad authAttrList"); return; } + + if (authAttrList->attr->length + strlen(LH_SUFFIX) > MAX_ESCAPED_DOMAIN_NAME) + { LogMsg("Account name too long (%d bytes)", authAttrList->attr->length); return; } + memcpy(accountName, authAttrList->attr->data, authAttrList->attr->length); + accountName[authAttrList->attr->length] = '\0'; + + zone.c[0] = '\0'; + if (!AppendLiteralLabelString(&zone, accountName) || + !AppendDNSNameString(&zone, LH_SUFFIX)) + { LogMsg("InitAuthInfo - bad account name"); return; } + + mDNS_UpdateDomainRequiresAuthentication(m, &zone, &zone, data, dataLen, mDNStrue); + if(m->uDNS_info.NameRegDomain) { debugf("Overwriting config file options with KeyChain values"); } + + if (!ConvertDomainNameToCString(&zone, m->uDNS_info.NameRegDomain) || + !ConvertDomainNameToCString(&zone, m->uDNS_info.ServiceRegDomain)) + { LogMsg("Couldn't set keychain username in uDNS global info"); } + + mDNS_GenerateGlobalFQDN(m); + // normally we'd query the zone for _register/_browse domains, but to reduce server load we manually generate the records + + haveSecInfo = mDNStrue; + //!!!KRS need to do better bookkeeping once we support multiple users + rrReg = mallocL("AuthRecord", sizeof(AuthRecord)); + rrBrowse = mallocL("AuthRecord", sizeof(AuthRecord)); + if (!rrReg || !rrBrowse) { LogMsg("ERROR: Malloc"); return; } + + // set up _browse + mDNS_SetupResourceRecord(rrBrowse, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); + MakeDomainNameFromDNSNameString(&rrBrowse->resrec.name, "_browse._dns-sd._udp.local."); + strcpy(rrBrowse->resrec.rdata->u.name.c, zone.c); + + // set up _register + mDNS_SetupResourceRecord(rrReg, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); + MakeDomainNameFromDNSNameString(&rrReg->resrec.name, "_register._dns-sd._udp.local."); + strcpy(rrReg->resrec.rdata->u.name.c, zone.c); + + regErr = mDNS_Register(m, rrReg); + if (regErr) LogMsg("Registration of local-only reg domain %s failed", zone.c); + + regErr = mDNS_Register(m, rrBrowse); + if (regErr) LogMsg("Registration of local-only browse domain %s failed", zone.c); + SecKeychainItemFreeContent(authAttrList, data); + } + +mDNSlocal void InitAuthInfo(mDNS *m); + +mDNSlocal OSStatus KeychainCallback(SecKeychainEvent event, SecKeychainCallbackInfo *info, void *context) + { + (void)event; + (void)info; + // unused + + debugf("SecKeychainAddCallback received event %d", event); + InitAuthInfo((mDNS *)context); // keychain events happen rarely - just rebuild the list + return 0; + } + +mDNSexport void InitAuthInfo(mDNS *m) + { + OSStatus err; + + SecKeychainSearchRef searchRef = NULL; + SecKeychainRef sysKeychain = NULL; + SecKeychainAttribute searchAttrs[] = { { kSecDescriptionItemAttr, strlen(LH_KEYCHAIN_DESC), LH_KEYCHAIN_DESC }, + { kSecServiceItemAttr, strlen(LH_KEYCHAIN_SERVICE), LH_KEYCHAIN_SERVICE } }; + SecKeychainAttributeList searchList = { sizeof(searchAttrs) / sizeof(*searchAttrs), searchAttrs }; + SecKeychainItemRef item; + + // clear any previous entries + mDNS_ClearAuthenticationList(m); + + err = SecKeychainOpen(SYS_KEYCHAIN_PATH, &sysKeychain); + if (err) { LogMsg("ERROR: InitAuthInfo - couldn't open system keychain - %d", err); goto release_refs; } + err = SecKeychainSetDomainDefault(kSecPreferencesDomainSystem, sysKeychain); + if (err) { LogMsg("ERROR: InitAuthInfo - couldn't set domain default for system keychain - %d", err); goto release_refs; } + + err = SecKeychainSearchCreateFromAttributes(sysKeychain, kSecGenericPasswordItemClass, &searchList, &searchRef); + if (err) { LogMsg("ERROR: InitAuthInfo - SecKeychainSearchCreateFromAttributes %d", err); goto release_refs; } + + while (!SecKeychainSearchCopyNext(searchRef, &item)) + { + GetAuthInfoFromKeychainItem(m, item); + CFRelease(item); + } + err = SecKeychainAddCallback(KeychainCallback, kSecAddEventMask | kSecDeleteEventMask | kSecUpdateEventMask | kSecPasswordChangedEventMask, m); + if (err && err != errSecDuplicateCallback) { LogMsg("SecKeychainAddCallback returned error %d", err); } + + release_refs: + + if (searchRef) CFRelease(searchRef); + if (sysKeychain) CFRelease(sysKeychain); + } + CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; -CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; +CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; mDNSexport mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) { @@ -1392,12 +2022,192 @@ mDNSexport mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) return(major); } -mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) +// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. +// If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- +// we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. +mDNSlocal mDNSBool mDNSPlatformInit_ReceiveUnicast(void) { + int err; + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + struct sockaddr_in s5353; + s5353.sin_family = AF_INET; + s5353.sin_port = MulticastDNSPort.NotAnInteger; + s5353.sin_addr.s_addr = 0; + err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); + close(s); + if (err) debugf("No unicast UDP responses"); + else debugf("Unicast UDP responses okay"); + return(err == 0); + } + + +//!!!KRS this should be less order-dependent as we support more configuration options +mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f) + { + char buf[1024]; + int len; + + if (!fgets(buf, 1024, f)) { LogMsg("Option %s not set", option); return mDNSfalse; } + len = strlen(option); + if (!strncmp(buf, option, len)) + { + strcpy(dst, buf + len + 1); + len = strlen(dst); + if ( len && dst[len-1] == '\n') dst[len-1] = '\0'; // chop newline + return mDNStrue; + } + LogMsg("Malformatted config file - %s not set", option); + return mDNSfalse; + } + + + +mDNSlocal void ReadRegDomainFromConfig(mDNS *const m) + { + FILE *f; + uDNS_GlobalInfo *u = &m->uDNS_info;; + char key[MAX_ESCAPED_DOMAIN_NAME]; + domainname key_d, name_d, service_d; + char secret[1024]; + int slen; mStatus err; - m->hostlabel.c[0] = 0; + // read registration domain (for dynamic updates) from config file + // !!!KRS these must go away once we can learn the reg domain from the network or prefs + if (m->uDNS_info.NameRegDomain[0] || m->uDNS_info.ServiceRegDomain[0]) + { debugf("Options from config already set via keychain. Ignoring config file."); return; } + + f = fopen(CONFIG_FILE, "r"); + if (!f) + { + if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened."); + return; + } + + if (!GetConfigOption(u->NameRegDomain, "name-reg", f)) goto end; + if (!GetConfigOption(u->ServiceRegDomain, "service-reg", f)) goto end; + if (!GetConfigOption(key, "key-name", f)) goto end; + if (!GetConfigOption(secret, "secret-64", f)) { LogMsg("ERROR: config file contains key without secret"); goto end; } + + // we don't actually need this in domain-name format - just convert it to error check + if (!MakeDomainNameFromDNSNameString(&service_d, u->ServiceRegDomain)) + { LogMsg("ERROR: config file contains bad service reg domain %s", u->ServiceRegDomain); u->ServiceRegDomain[0] = '\0'; } + + if (!MakeDomainNameFromDNSNameString(&name_d, u->NameRegDomain)) + { LogMsg("ERROR: config file contains bad name reg domain %s", u->NameRegDomain); u->NameRegDomain[0] = '\0'; } + + if (!MakeDomainNameFromDNSNameString(&key_d, key)) + { LogMsg("ERROR: config file contains bad key %s", key); key[0] = '\0'; } + if (key[0]) + { + slen = strlen(secret); + if (u->ServiceRegDomain[0]) + { + err = mDNS_UpdateDomainRequiresAuthentication(m, &service_d, &key_d, secret, slen, mDNStrue); + if (err) LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication returned %d for domain ", err, u->ServiceRegDomain); + } + if (u->NameRegDomain[0]) + { + err = mDNS_UpdateDomainRequiresAuthentication(m, &name_d, &key_d, secret, slen, mDNStrue); + if (err) LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication returned %d for domain ", err, u->NameRegDomain); + } + } + + end: + fclose(f); + } + +mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void) + { + return mDNS_CopyDNameList(DefBrowseList); + } + +mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void) + { + static DNameListElem tmp; + static mDNSBool init = mDNSfalse; + + if (!init) + { + MakeDomainNameFromDNSNameString(&tmp.name, "local."); + tmp.next = NULL; + init = mDNStrue; + } + return mDNS_CopyDNameList(&tmp); + } + + +mDNSlocal void FoundDefBrowseDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + DNameListElem *ptr, *prev, *new; + (void)m; // unused; + (void)question; // unused + + if (AddRecord) + { + new = mallocL("FoundDefBrowseDomain", sizeof(DNameListElem)); + if (!new) { LogMsg("ERROR: malloc"); return; } + strcpy(new->name.c, answer->rdata->u.name.c); + new->next = DefBrowseList; + DefBrowseList = new; + return; + } + else + { + ptr = DefBrowseList; + prev = NULL; + while (ptr) + { + if (SameDomainName(&ptr->name, &answer->rdata->u.name)) + { + if (prev) prev->next = ptr->next; + else DefBrowseList = ptr->next; + freeL("FoundDefBrowseDomain", ptr); + return; + } + prev = ptr; + ptr = ptr->next; + } + LogMsg("FoundDefBrowseDomain: Got remove event for domain %s not in list", answer->rdata->u.name.c); + } + } + +// Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows: +// 1) query for _browse._dns-sd._udp.local on LocalOnly interface +// (.local manually generated via explicit callback) +// 2) for each search domain (from prefs pane), query for _browse._dns-sd._udp.. +// 3) for each result from (2), register LocalOnly PTR record_browse._dns-sd._udp.local. -> +// 4) result above should generate a callback from question in (1). result added to global list +// 5) global list delivered to client via GetSearchDomainList() +// 6) client calls to enumerate domains now go over LocalOnly interface +// (!!!KRS may add outgoing interface in addition) + +mDNSlocal mStatus InitDNSConfig(mDNS *const m) + { + mStatus err; + AuthRecord local; + DNSConfigInitialized = mDNStrue; + + // start query for domains to be used in default (empty string domain) browses + err = mDNS_GetDomains(m, &DefBrowseDomainQ, mDNS_DomainTypeBrowse, NULL, mDNSInterface_LocalOnly, FoundDefBrowseDomain, NULL); + + // provide .local automatically + mDNS_SetupResourceRecord(&local, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); + MakeDomainNameFromDNSNameString(&local.resrec.name, "_browse._dns-sd._udp.local."); + MakeDomainNameFromDNSNameString(&local.resrec.rdata->u.name, "local."); + // other fields ignored + FoundDefBrowseDomain(m, &DefBrowseDomainQ, &local.resrec, 1); + + return mStatus_NoError; + } + +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 }; @@ -1406,7 +2216,8 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) HINFO_HWstring = HINFO_HWstring_buffer; char HINFO_SWstring[256] = ""; - if (mDNSMacOSXSystemBuildNumber(HINFO_SWstring) < 7) m->KnownBugs = mDNS_KnownBug_PhantomInterfaces; + if (mDNSMacOSXSystemBuildNumber(HINFO_SWstring) < 7) m->KnownBugs |= mDNS_KnownBug_PhantomInterfaces; + if (mDNSPlatformInit_ReceiveUnicast()) m->CanReceiveUnicast = mDNStrue; mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); @@ -1418,6 +2229,15 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) mDNSPlatformMemCopy(HINFO_SWstring, &m->HISoftware.c[1], slen); } + m->p->unicastsockets.m = m; + m->p->unicastsockets.info = NULL; + m->p->unicastsockets.sktv4 = m->p->unicastsockets.sktv6 = -1; + m->p->unicastsockets.cfsv4 = m->p->unicastsockets.cfsv6 = NULL; + m->p->unicastsockets.rlsv4 = m->p->unicastsockets.rlsv6 = NULL; + + err = SetupSocket(&m->p->unicastsockets, zeroIPPort, &zeroAddr, AF_INET); + err = SetupSocket(&m->p->unicastsockets, zeroIPPort, &zeroAddr, AF_INET6); + m->p->InterfaceList = mDNSNULL; m->p->userhostlabel.c[0] = 0; UpdateInterfaceList(m); @@ -1427,12 +2247,24 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) if (err) return(err); err = WatchForPowerChanges(m); - return(err); + if (err) return err; + + err = WatchForDNSChanges(m); + + InitDNSConfig(m); + + m->uDNS_info.ServiceRegDomain[0] = '\0'; + m->uDNS_info.NameRegDomain[0] = '\0'; + InitAuthInfo(m); + ReadRegDomainFromConfig(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); @@ -1464,6 +2296,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) MarkAllInterfacesInactive(m); ClearInactiveInterfaces(m); + CloseSocketSet(&m->p->unicastsockets); } mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; @@ -1502,6 +2335,11 @@ mDNSexport mDNSs32 mDNSPlatformTimeNow(void) return((mDNSs32)(mach_absolute_time() / clockdivisor)); } +mDNSexport mDNSs32 mDNSPlatformUTC(void) + { + return time(NULL); + } + // 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; } diff --git a/mDNSMacOSX/CFSocketPuma.c b/mDNSMacOSX/CFSocketPuma.c index e0cf87c..3a7e266 100644 --- a/mDNSMacOSX/CFSocketPuma.c +++ b/mDNSMacOSX/CFSocketPuma.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSMacOSX/DNSServiceDiscoveryDefines.h b/mDNSMacOSX/DNSServiceDiscoveryDefines.h index b6d3a9a..bd5b11b 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryDefines.h +++ b/mDNSMacOSX/DNSServiceDiscoveryDefines.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSMacOSX/DNSServiceDiscoveryReply.defs b/mDNSMacOSX/DNSServiceDiscoveryReply.defs index 942fb6b..6ae6004 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryReply.defs +++ b/mDNSMacOSX/DNSServiceDiscoveryReply.defs @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSMacOSX/DNSServiceDiscoveryRequest.defs b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs index eb400b1..a4596af 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryRequest.defs +++ b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSMacOSX/SampleUDSClient.c b/mDNSMacOSX/SampleUDSClient.c deleted file mode 100755 index 7838fc1..0000000 --- a/mDNSMacOSX/SampleUDSClient.c +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: SampleUDSClient.c,v $ -Revision 1.7 2003/08/18 18:50:15 cheshire -Can now give "-lo" as first parameter, to test "local only" mode - -Revision 1.6 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 - - */ - -#include -#include -#include // include Mach API to ensure no conflicts exist -#include -#include -#include -#include -#include -#define BIND_8_COMPAT 1 -#include -// T_SRV is not defined in older versions of nameser.h -#ifndef T_SRV -#define T_SRV 33 -#endif - -// constants -#define MAX_DOMAIN_LABEL 63 -#define MAX_DOMAIN_NAME 255 -#define MAX_CSTRING 2044 - - -// data structure defs -typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; - -typedef struct { u_char c[ 64]; } domainlabel; -typedef struct { u_char c[256]; } domainname; - - -typedef struct - { - uint16_t priority; - uint16_t weight; - uint16_t port; - domainname target; - } srv_rdata; - - -// private function prototypes -static void sighdlr(int signo); -static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc); -static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc); -//static void MyCallbackWrapper(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *i, void *context); -static void print_rdata(int type, int len, const u_char *rdata); -static void query_cb(const DNSServiceRef DNSServiceRef, const DNSServiceFlags flags, const u_int32_t interfaceIndex, const DNSServiceErrorType errorCode, const char *name, const u_int16_t rrtype, const u_int16_t rrclass, const u_int16_t rdlen, const void *rdata, const u_int32_t ttl, void *context); -static void resolve_cb(const DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context); -static void my_enum_cb( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context); -static void my_regecordcb(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context); -static void browse_cb(DNSServiceRef sdr, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err, const char *serviceName, const char *regtype, const char *domain, void *context); - - -// globals -static DNSServiceRef sdr = NULL; -static uint32_t InterfaceIndex = 0; - -static void regservice_cb(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) - { - #pragma unused (sdRef, flags, errorCode, context) - printf("regservice_cb %s %s %s\n", name, regtype, domain); - } - -int main (int argc, char * argv[]) { - int err, t, i; - char *name, *type, *domain; - DNSServiceFlags flags; - DNSRecordRef recordrefs[10]; - char host[256]; - int ipaddr = 12345; // random IP address - - char full[1024]; - - // First parameter "-lo" means "local only" - if (!strcmp(argv[1], "-lo")) { InterfaceIndex = -1; argv++; argc--; } - - if (signal(SIGINT, sighdlr) == SIG_ERR) fprintf(stderr, "ERROR - can't catch interupt!\n"); - if (argc < 2) exit(1); - - if (!strcmp(argv[1], "-regrecord")) - { - err = DNSServiceCreateConnection(&sdr); - if (err) - { - printf("DNSServiceCreateConnection returned %d\n", err); - exit(1); - } - printf("registering 10 address records...\n"); - for (i = 0; i < 10; i++) - { - sprintf(host, "testhost-%d.local.", i); - ipaddr++; - err = DNSServiceRegisterRecord(sdr, &recordrefs[i], kDNSServiceFlagsUnique, InterfaceIndex, - host, 1, 1, 4, &ipaddr, 60, my_regecordcb, NULL); - if (err) - { - printf("DNSServiceRegisterRecord returned error %d\n", err); - exit(1); - } - } - printf("processing results...\n"); - for (i = 0; i < 10; i++) DNSServiceProcessResult(sdr); - printf("deregistering half of the records\n"); - for (i = 0; i < 10; i++) - { - if (i % 2) - { - err = DNSServiceRemoveRecord(sdr, recordrefs[i], 0); - if (err) - { - printf("DNSServiceRemoveRecord returned error %d\n" ,err); - exit(1); - } - } - } - printf("sleeping 10...\n"); - sleep(10); - printf("deregistering all remaining records\n");; - DNSServiceRefDeallocate(sdr); - printf("done. sleeping 10..\n"); - sleep(10); - exit(1); - } - - if (!strcmp(argv[1], "-browse")) - { - if (argc < 3) exit(1); - err = DNSServiceBrowse(&sdr, 0, InterfaceIndex, argv[2], NULL /*"local."*/, browse_cb, NULL); - if (err) - { - printf("DNSServiceBrowse returned error %d\n", err); - exit(1); - } - while(1) DNSServiceProcessResult(sdr); - } - - if (!strcmp(argv[1], "-enum")) - { - if (!strcmp(argv[2], "browse")) flags = kDNSServiceFlagsBrowseDomains; - else if (!strcmp(argv[2], "register")) flags = kDNSServiceFlagsRegistrationDomains; - else exit(1); - - err = DNSServiceEnumerateDomains(&sdr, flags, InterfaceIndex, my_enum_cb, NULL); - if (err) - { - printf("EnumerateDomains returned error %d\n", err); - exit(1); - } - while(1) DNSServiceProcessResult(sdr); - } - if (!strcmp(argv[1], "-query")) - { - t = atol(argv[5]); - err = DNSServiceConstructFullName(full, argv[2], argv[3], argv[4]); - if (err) exit(1); - printf("resolving fullname %s type %d\n", full, t); - err = DNSServiceQueryRecord(&sdr, 0, 0, full, t, 1, query_cb, NULL); - while (1) DNSServiceProcessResult(sdr); - } - - if (!strcmp(argv[1], "-regservice")) - { - char *regtype = "_http._tcp"; - char txtstring[] = "\x0DMy Txt Record"; - if (argc > 2) name = argv[2]; - else name = NULL; - if (argc > 3) regtype = argv[3]; - uint16_t PortAsNumber = 123; - if (argc > 4) PortAsNumber = atoi(argv[4]); - Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; - err = DNSServiceRegister(&sdr, 0, InterfaceIndex, name, regtype, "local.", NULL, registerPort.NotAnInteger, sizeof(txtstring)-1, txtstring, regservice_cb, NULL); - if (err) - { - printf("DNSServiceRegister returned error %d\n", err); - exit(1); - } - while (1) DNSServiceProcessResult(sdr); - } - if (!strcmp(argv[1], "-resolve")) - { - name = argv[2]; - type = argv[3]; - domain = argv[4]; - err = DNSServiceResolve(&sdr, 0, InterfaceIndex, name, type, domain, resolve_cb, NULL); - if (err) - { - printf("DNSServiceResolve returned error %d\n", err); - exit(1); - } - while(1) DNSServiceProcessResult(sdr); - } - exit(1); - } - - - -// callbacks - -// wrapper to make callbacks fit CFRunLoop callback signature -/* -static void MyCallbackWrapper(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *i, void *context) - { - (void)sr; - (void)t; - (void)dr; - (void)i; - - DNSServiceRef *sdr = context; - DNSServiceDiscoveryProcessResult(*sdr); - } -*/ - -static void browse_cb(DNSServiceRef sdr, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err, const char *serviceName, const char *regtype, const char *domain, void *context) - { - #pragma unused(sdr, ifi, context) - - if (err) - { - printf("Callback: error %d\n", err); - return; - } - printf("BrowseCB: %s %s %s %s (%s)\n", serviceName, regtype, domain, (flags & kDNSServiceFlagsMoreComing ? "(more coming)" : ""), flags & kDNSServiceFlagsAdd ? "(ADD)" : "(REMOVE)"); - - } - -static void my_enum_cb( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context) - { - #pragma unused(sdRef, context) - char *type; - if (flags == kDNSServiceFlagsAdd) type = "add"; - else if (flags == kDNSServiceFlagsRemove) type = "remove"; - else if (flags == (kDNSServiceFlagsAdd | kDNSServiceFlagsDefault)) type = "add default"; - else type = "unknown"; - - - if (errorCode) printf("EnumerateDomainsCB: error code %d\n", errorCode); - else printf("%s domain %s on interface %d\n", type, replyDomain, interfaceIndex); - } - -static void query_cb(const DNSServiceRef DNSServiceRef, const DNSServiceFlags flags, const u_int32_t interfaceIndex, const DNSServiceErrorType errorCode, const char *name, const u_int16_t rrtype, const u_int16_t rrclass, const u_int16_t rdlen, const void *rdata, const u_int32_t ttl, void *context) - { - (void)DNSServiceRef; - (void)flags; - (void)interfaceIndex; - (void)rrclass; - (void)ttl; - (void)context; - - if (errorCode) - { - printf("query callback: error==%d\n", errorCode); - return; - } - printf("query callback - name = %s, rdata=\n", name); - print_rdata(rrtype, rdlen, rdata); - } - -static void resolve_cb(const DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context) - { - int i; - - #pragma unused(sdRef, flags, interfaceIndex, errorCode, context, txtRecord) - printf("Resolved %s to %s:%d (%d bytes txt data)\n", fullname, hosttarget, port, txtLen); - printf("TXT Data:\n"); - for (i = 0; i < txtLen; i++) - if (txtRecord[i] >= ' ') printf("%c", txtRecord[i]); - } - - - -static void my_regecordcb(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context) - { - #pragma unused (sdRef, RecordRef, flags, context) - if (errorCode) printf("regrecord CB received error %d\n", errorCode); - else printf("regrecord callback - no errors\n"); - } - - -// resource record data interpretation routines -static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) - { - const u_char * src = label->c; // Domain label we're reading - const u_char len = *src++; // Read length of this (non-null) label - const u_char *const end = src + len; // Work out where the label ends - if (len > MAX_DOMAIN_LABEL) return(NULL); // If illegal label, abort - while (src < end) // While we have characters in the label - { - u_char c = *src++; - if (esc) - { - if (c == '.') // If character is a dot, - *ptr++ = esc; // Output escape character - else if (c <= ' ') // If non-printing ascii, - { // Output decimal escape sequence - *ptr++ = esc; - *ptr++ = (char) ('0' + (c / 100) ); - *ptr++ = (char) ('0' + (c / 10) % 10); - c = (u_char)('0' + (c ) % 10); - } - } - *ptr++ = (char)c; // Copy the character - } - *ptr = 0; // Null-terminate the string - return(ptr); // and return - } - -static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) - { - const u_char *src = name->c; // Domain name we're reading - const u_char *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid - - if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot - - while (*src) // While more characters in the domain name - { - if (src + 1 + *src >= max) return(NULL); - ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); - if (!ptr) return(NULL); - src += 1 + *src; - *ptr++ = '.'; // Write the dot after the label - } - - *ptr++ = 0; // Null-terminate the string - return(ptr); // and return - } - -// print arbitrary rdata in a readable manned -static void print_rdata(int type, int len, const u_char *rdata) - { - int i; - srv_rdata *srv; - char targetstr[MAX_CSTRING]; - struct in_addr in; - - switch (type) - { - case T_TXT: - // print all the alphanumeric and punctuation characters - for (i = 0; i < len; i++) - if (rdata[i] >= 32 && rdata[i] <= 127) printf("%c", rdata[i]); - printf("\n"); - return; - case T_SRV: - srv = (srv_rdata *)rdata; - ConvertDomainNameToCString_withescape(&srv->target, targetstr, 0); - printf("pri=%d, w=%d, port=%d, target=%s\n", srv->priority, srv->weight, srv->port, targetstr); - return; - case T_A: - assert(len == 4); - memcpy(&in, rdata, sizeof(in)); - printf("%s\n", inet_ntoa(in)); - return; - case T_PTR: - ConvertDomainNameToCString_withescape((domainname *)rdata, targetstr, 0); - printf("%s\n", targetstr); - return; - default: - printf("ERROR: I dont know how to print RData of type %d\n", type); - return; - } - } - - - - -// signal handlers, setup/teardown, etc. -static void sighdlr(int signo) - { - assert(signo == SIGINT); - fprintf(stderr, "Received sigint - deallocating serviceref and exiting\n"); - if (sdr) - DNSServiceRefDeallocate(sdr); - exit(1); - } - - - - - - - diff --git a/mDNSMacOSX/SamplemDNSClient.c b/mDNSMacOSX/SamplemDNSClient.c index f905bd7..430d0e6 100644 --- a/mDNSMacOSX/SamplemDNSClient.c +++ b/mDNSMacOSX/SamplemDNSClient.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -36,6 +38,23 @@ Change History (most recent first): $Log: SamplemDNSClient.c,v $ +Revision 1.44 2004/05/28 02:20:06 cheshire +If we allow dot or empty string for domain when resolving a service, +it should be a synonym for "local" + +Revision 1.43 2004/02/03 22:07:50 cheshire +: Widen columns to display non-local domains better + +Revision 1.42 2003/12/03 11:39:17 cheshire + Browse output misaligned in mDNS command-line tool +(Also fix it to add leading space when the hours part of the time is only one digit) + +Revision 1.41 2003/10/30 22:52:57 cheshire + Browse output misaligned in mDNS command-line tool + +Revision 1.40 2003/09/26 01:07:06 cheshire +Added test case to test fix for + Revision 1.39 2003/08/18 19:05:45 cheshire UpdateRecord not working right Added "newrdlength" field to hold new length of updated rdata @@ -127,7 +146,7 @@ static void printtimestamp(void) 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); + printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec/1000); } #define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \ @@ -159,9 +178,9 @@ static void browse_reply(DNSServiceBrowserReplyResultType resultType, { 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"); + if (num_printed++ == 0) printf("Timestamp A/R Flags %-24s %-24s %s\n", "Domain", "Service Type", "Instance Name"); printtimestamp(); - printf("%s%6X %-8s %-20s %s\n", op, flags, replyDomain, replyType, replyName); + printf("%s%6X %-24s %-24s %s\n", op, flags, replyDomain, replyType, replyName); } static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context) @@ -307,7 +326,7 @@ int main(int argc, char **argv) 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"); + operation = getopt(argc, (char * const *)argv, "EFBLRAUNTMI"); if (operation == -1) goto Fail; switch (operation) @@ -321,7 +340,7 @@ int main(int argc, char **argv) break; case 'B': if (argc < optind+1) goto Fail; - dom = (argc < optind+2) ? "" : argv[optind+1]; + dom = (argc < optind+2) ? "local" : 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); @@ -329,7 +348,7 @@ int main(int argc, char **argv) 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 + if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local" 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; @@ -397,6 +416,16 @@ int main(int argc, char **argv) break; } + case 'I': { + pid_t pid = getpid(); + Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; + static const char TXT[] = "\x09" "Test Data"; + printf("Registering Service Test._testtxt._tcp.local.\n"); + client = DNSServiceRegistrationCreate("", "_testtxt._tcp.", "", registerPort.NotAnInteger, "", reg_reply, nil); + if (client) DNSServiceRegistrationUpdateRecord(client, 0, 1+TXT[0], &TXT[0], 120); + break; + } + default: goto Exit; } @@ -424,5 +453,6 @@ Fail: fprintf(stderr, "%s -N (Test adding a large NULL record)\n", argv[0]); fprintf(stderr, "%s -T (Test creating a large TXT record)\n", argv[0]); fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", argv[0]); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", argv[0]); return 0; } diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 9710517..9cb6a47 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -36,32 +38,143 @@ Change History (most recent first): $Log: daemon.c,v $ -Revision 1.134.2.8 2005/01/28 04:03:24 cheshire - SUPan: Current method of doing subtypes causes name collisions -Summary: Pulled in ConstructServiceName, CountSubTypes and AllocateSubTypes from Tiger version. +Revision 1.175 2004/06/10 20:23:21 cheshire +Also list interfaces in SIGINFO output + +Revision 1.174 2004/06/08 18:54:48 ksekar +: mDNSResponder leaks after exploring in Printer Setup Utility + +Revision 1.173 2004/06/08 17:35:12 cheshire + Detect and report if mDNSResponder uses too much CPU + +Revision 1.172 2004/06/05 00:04:26 cheshire +: wide-area domains should be returned in reg. domain enumeration + +Revision 1.171 2004/06/04 08:58:30 ksekar +: Keychain integration for secure dynamic update + +Revision 1.170 2004/05/30 20:01:50 ksekar +: wide-area default registrations should be in +.local too - fixed service registration when clients pass an explicit +domain (broken by previous checkin) + +Revision 1.169 2004/05/30 01:30:16 ksekar +: wide-area default registrations should be in +.local too + +Revision 1.168 2004/05/18 23:51:26 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.167 2004/05/14 16:39:47 ksekar +Browse for iChat locally for now. + +Revision 1.166 2004/05/13 21:33:52 ksekar +Clean up non-local registration control via config file. Force iChat +registrations to be local for now. -Revision 1.134.2.7 2004/06/18 17:28:19 cheshire - Current method of doing subtypes causes name collisions +Revision 1.165 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for -Revision 1.134.2.6 2004/04/06 19:50:36 cheshire +Revision 1.164 2004/05/12 22:03:08 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.163 2004/05/12 02:03:25 ksekar +Non-local domains will only be browsed by default, and show up in +_browse domain enumeration, if they contain an _browse._dns-sd ptr record. + +Revision 1.162 2004/04/14 23:09:29 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.161 2004/04/07 01:20:04 cheshire +Hash slot value should be unsigned + +Revision 1.160 2004/04/06 19:51:24 cheshire mDNSResponder will not launch if "nobody" user doesn't exist. After more discussion, we've decided to use userid -2 if "nobody" user doesn't exist. -Revision 1.134.2.5 2004/04/03 01:29:07 cheshire +Revision 1.159 2004/04/03 01:36:55 cheshire mDNSResponder will not launch if "nobody" user doesn't exist. If "nobody" user doesn't exist, log a message and continue as "root" -Revision 1.134.2.4 2004/04/02 21:50:21 cheshire +Revision 1.158 2004/04/02 21:39:05 cheshire Fix errors in comments -Revision 1.134.2.3 2003/12/12 01:21:30 cheshire - mDNSResponder should not run as root +Revision 1.157 2004/03/19 18:49:10 ksekar +Increased size check in freeL() to account for LargeCacheRecord +structs larger than 8k + +Revision 1.156 2004/03/19 18:19:19 ksekar +Fixed daemon.c to compile with malloc debugging turned on. + +Revision 1.155 2004/03/13 01:57:34 ksekar +: DynDNS: Dynamic update of service records + +Revision 1.154 2004/03/12 08:42:47 cheshire +: Should not allow empty string for resolve domain + +Revision 1.153 2004/03/12 08:08:51 cheshire +Update comments + +Revision 1.152 2004/02/05 19:39:29 cheshire +Move creation of /var/run/mDNSResponder.pid to uds_daemon.c, +so that all platforms get this functionality + +Revision 1.151 2004/02/03 22:35:34 cheshire +: Should not allow empty string for resolve domain -Revision 1.134.2.2 2003/12/05 00:03:35 cheshire - Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256 +Revision 1.150 2004/01/28 21:14:23 cheshire +Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode) -Revision 1.134.2.1 2003/12/03 11:00:09 cheshire -Update "mDNSResponderVersion" mechanism to allow dots so we can do mDNSResponder-58.1 for SUPan +Revision 1.149 2004/01/28 02:30:08 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.148 2004/01/25 00:03:20 cheshire +Change to use mDNSVal16() instead of private PORT_AS_NUM() macro + +Revision 1.147 2004/01/19 19:51:46 cheshire +Fix compiler error (mixed declarations and code) on some versions of Linux + +Revision 1.146 2003/12/08 21:00:46 rpantos +Changes to support mDNSResponder on Linux. + +Revision 1.145 2003/12/05 22:08:07 cheshire +Update version string to "mDNSResponder-61", including new mechanism to allow dots (e.g. 58.1) + +Revision 1.144 2003/11/19 23:21:08 ksekar +: config change handler not called for dns-sd services + +Revision 1.143 2003/11/14 21:18:32 cheshire +: Security: Crashing bug in mDNSResponder +Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. + +Revision 1.142 2003/11/08 22:18:29 cheshire +: Don't need to show process ID in *every* mDNSResponder syslog message + +Revision 1.141 2003/11/07 02:30:57 cheshire +Also check per-slot cache use counts in SIGINFO state log + +Revision 1.140 2003/10/21 19:58:26 cheshire + Syslog messages should show TTL as signed (for overdue records) + +Revision 1.139 2003/10/21 00:10:18 rpantos +: mDNSResponder should not run as root + +Revision 1.138 2003/10/07 20:16:58 cheshire +Shorten syslog message a bit + +Revision 1.137 2003/09/23 02:12:43 cheshire +Also include port number in list of services registered via new UDS API + +Revision 1.136 2003/09/23 02:07:25 cheshire +Include port number in DNSServiceRegistration START/STOP messages + +Revision 1.135 2003/09/23 01:34:02 cheshire +In SIGINFO state log, show remaining TTL on cache records, and port number on ServiceRegistrations Revision 1.134 2003/08/21 20:01:37 cheshire Traffic reduction: Detect long-lived Resolve() calls, and report them in syslog @@ -109,8 +222,7 @@ 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. + Completed support for Unix-domain socket based API. Revision 1.120 2003/07/18 00:30:00 cheshire Remove mDNSResponder version from packet header and use HINFO record instead @@ -139,7 +251,7 @@ standard error file descriptors to /dev/null just like any other well behaved daemon Revision 1.112 2003/06/25 23:42:19 ksekar -Bug #: : Feature: New Rendezvous APIs (#7875) +: 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. @@ -185,7 +297,7 @@ Revision 1.101 2003/05/22 00:26:55 cheshire Modify error message to explain that this is technically legal, but may indicate a bug. Revision 1.100 2003/05/21 21:02:24 ksekar -Bug #: : Service should be prefixed +: Service should be prefixed Changed kmDNSBootstrapName to "com.apple.mDNSResponderRestart" since we're changing the main Mach message port to "com.apple.mDNSResponder. @@ -231,12 +343,11 @@ Add $Log header #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 -extern mDNSs32 CountSubTypes(char *regtype); -extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p); +#include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h -#include +#include "GenLinkedList.h" -#define ENABLE_UDS 1 +#include //************************************************************************************************************* // Macros @@ -247,14 +358,18 @@ extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p); #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) +// convenience definition +#define _UNUSED __attribute__ ((unused)) + //************************************************************************************************************* // Globals +#define LOCAL_DEFAULT_REG 1 // empty string means register in the local domain +#define DEFAULT_REG_DOMAIN "apple.com." // used if the above flag is turned off 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; @@ -272,12 +387,6 @@ static mach_port_t server_priv_port = MACH_PORT_NULL; static int restarting_via_mach_init = 0; -#if MDNS_DEBUGMSGS -int debug_mode = 1; -#else -int debug_mode = 0; -#endif - //************************************************************************************************************* // Active client list structures @@ -299,11 +408,18 @@ struct DNSServiceBrowserResult_struct }; typedef struct DNSServiceBrowser_struct DNSServiceBrowser; + +typedef struct DNSServiceBrowserQuestion + { + struct DNSServiceBrowserQuestion *next; + DNSQuestion q; + } DNSServiceBrowserQuestion; + struct DNSServiceBrowser_struct { DNSServiceBrowser *next; mach_port_t ClientMachPort; - DNSQuestion q; + DNSServiceBrowserQuestion *qlist; DNSServiceBrowserResult *results; mDNSs32 lastsuccess; }; @@ -318,15 +434,30 @@ struct DNSServiceResolver_struct mDNSs32 ReportTime; }; + +typedef struct ExtraRecordRef + { + ExtraResourceRecord *localRef; // extra added to .local service + ExtraResourceRecord *globalRef; // extra added to default global service (may be NULL) + struct ExtraRecordRef *next; + } ExtraRecordRef; + typedef struct DNSServiceRegistration_struct DNSServiceRegistration; struct DNSServiceRegistration_struct { DNSServiceRegistration *next; mach_port_t ClientMachPort; mDNSBool autoname; - mDNSBool autorename; + mDNSBool autorenameLS; + mDNSBool autorenameGS; + mDNSBool deallocate; // gs and ls (below) will receive separate MemFree callbacks, + // the latter of which must deallocate the wrapper structure. + // ls MemFree callback: if (!gs) free wrapper; else set deallocate flag + // gs callback: if (deallocate) free wrapper; else free (gs), gs = NULL domainlabel name; - ServiceRecordSet s; + ExtraRecordRef *ExtraRefList; + ServiceRecordSet *gs; // default "global" (wide area) service (may be NULL) + ServiceRecordSet ls; // .local service (also used if client passes an explicit domain) // Don't add any fields after ServiceRecordSet. // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object }; @@ -352,7 +483,7 @@ static void validatelists(mDNS *const m) AuthRecord *rr; CacheRecord *cr; DNSQuestion *q; - mDNSs32 slot; + mDNSu32 slot; for (e = DNSServiceDomainEnumerationList; e; e=e->next) if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0) @@ -371,12 +502,12 @@ static void validatelists(mDNS *const m) 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); + if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMsg("!!!! ResourceRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.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); + if (rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMsg("!!!! DuplicateRecords list: %p is garbage (%X) !!!!", rr, rr->resrec.RecordType); for (q = m->Questions; q; q=q->next) if (q->ThisQInterval == (mDNSs32)~0) @@ -384,8 +515,8 @@ static void validatelists(mDNS *const m) 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); + if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF) + LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->resrec.RecordType); } void *mallocL(char *msg, unsigned int size) @@ -417,7 +548,7 @@ void freeL(char *msg, void *x) 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) + if (mem[1] > 24000) { 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); @@ -432,25 +563,46 @@ void freeL(char *msg, void *x) //************************************************************************************************************* // Client Death Detection -mDNSlocal void FreeDNSServiceRegistration(DNSServiceRegistration *x) +mDNSlocal void FreeSRS(ServiceRecordSet *s) { - while (x->s.Extras) + while (s->Extras) { - ExtraResourceRecord *extras = x->s.Extras; - x->s.Extras = x->s.Extras->next; + ExtraResourceRecord *extras = s->Extras; + s->Extras = 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 (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage) + freeL("TXT RData", s->RR_TXT.resrec.rdata); - if (x->s.SubTypes) freeL("ServiceSubTypes", x->s.SubTypes); + if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes); + } + +mDNSlocal void FreeDNSServiceRegistration(ServiceRecordSet *srs) + { + DNSServiceRegistration *x = srs->ServiceContext; + ExtraRecordRef *ref, *fptr; - freeL("DNSServiceRegistration", x); + FreeSRS(srs); + if (srs == x->gs) + { + freeL("DNSServiceRegistration GlobalService", srs); + x->gs = NULL; + } + else x->deallocate = mDNStrue; + + if (x->deallocate && !x->gs) + { + ref = x->ExtraRefList; + while (ref) + { fptr = ref; ref = ref->next; freeL("ExtraRecordRef", fptr); } + 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 @@ -479,12 +631,19 @@ mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next; if (*b) { - DNSServiceBrowser *x = *b; + DNSServiceBrowser *x = *b; + DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist; *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 (qptr) + { + if (m && m != x) + LogMsg("%5d: DNSServiceBrowser(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x); + else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, qptr->q.qname.c); + mDNS_StopBrowse(&mDNSStorage, &qptr->q); + freePtr = qptr; + qptr = qptr->next; + freeL("DNSServiceBrowserQuestion", freePtr); + } while (x->results) { DNSServiceBrowserResult *r = x->results; @@ -513,16 +672,30 @@ mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) { DNSServiceRegistration *x = *r; *r = (*r)->next; - x->autorename = mDNSfalse; + x->autorenameLS = mDNSfalse; + x->autorenameGS = 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); + { + LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls), m, x); + if (x->gs) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs), m, x); + } + else + { + LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->ls.RR_SRV.resrec.name.c, SRS_PORT(&x->ls)); + if (x->gs) LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, x->gs->RR_SRV.resrec.name.c, SRS_PORT(x->gs)); + } // 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); + if (x->gs && mDNS_DeregisterService(&mDNSStorage, x->gs)) + { + // Deregister returned an error, so we free immediately + FreeSRS(x->gs); + x->gs = NULL; + } + if (mDNS_DeregisterService(&mDNSStorage, &x->ls)) + FreeDNSServiceRegistration(&x->ls); return; } @@ -537,15 +710,25 @@ mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, DNSServiceBrowser *b = DNSServiceBrowserList; DNSServiceResolver *l = DNSServiceResolverList; DNSServiceRegistration *r = DNSServiceRegistrationList; + DNSServiceBrowserQuestion *qptr; + 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); + if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg); + else if (b) + { + for (qptr = b->qlist; qptr; qptr = qptr->next) + LogMsg("%5d: Browser(%##s) %s%s", c, qptr->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->ls.RR_SRV.resrec.name.c, reason, msg); + if (r->gs) LogMsg("%5d: Registration(%##s) %s%s", c, r->gs->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); } @@ -556,14 +739,20 @@ mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c) DNSServiceBrowser *b = DNSServiceBrowserList; DNSServiceResolver *l = DNSServiceResolverList; DNSServiceRegistration *r = DNSServiceRegistrationList; + DNSServiceBrowserQuestion *qptr; + 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 (b) + { + for (qptr = b->qlist; qptr; qptr = qptr->next) + LogMsg("%5d: Browser(%##s) already exists!", c, qptr->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); + if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->ls.RR_SRV.resrec.name.c); return(e || b || l || r); } @@ -579,7 +768,7 @@ mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIn 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 ); + mach_port_destroy(mach_task_self(), deathMessage->not_port); } } @@ -661,8 +850,8 @@ mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port { 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); + err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, FoundDomain, x); + if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, FoundDomain, x); if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; } // Succeeded: Wrap up and return @@ -717,43 +906,70 @@ mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unuseds (void)unusedserver; // Unused mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; + DNameListElem *SearchDomains = NULL, *sdPtr; + DNSServiceBrowserQuestion *qptr; + 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; - t.c[0] = 0; - mDNSs32 NumSubTypes = CountSubTypes(regtype); - if (NumSubTypes < 0 || NumSubTypes > 1) { errormsg = "Bad Service SubType"; goto badparam; } - if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1)) - { errormsg = "Bad Service SubType"; goto badparam; } - if (!regtype[0] || !AppendDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; } - if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Illegal domain"; goto badparam; } + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; 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->qlist = NULL; 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; } - + //!!!KRS browse locally for ichat + if (!domain[0] && (!strcmp(regtype, "_ichat._tcp.") || !strcmp(regtype, "_presence._tcp."))) + domain = "local."; + + if (domain[0]) + { + // Start browser for an explicit domain + x->qlist = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion)); + x->qlist->next = NULL; + if (!x->qlist) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; } + + if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; } + LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, d.c); + err = mDNS_StartBrowse(&mDNSStorage, &x->qlist->q, &t, &d, mDNSInterface_Any, FoundInstance, x); + if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; } + } + else + { + // Start browser on all domains + SearchDomains = mDNSPlatformGetSearchDomainList(); + if (!SearchDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; } + for (sdPtr = SearchDomains; sdPtr; sdPtr = sdPtr->next) + { + qptr = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion)); + if (!qptr) { err = mStatus_UnknownErr; AbortClient(client, x); errormsg = "malloc"; goto fail; } + qptr->next = x->qlist; + x->qlist = qptr; + LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", client, t.c, sdPtr->name.c); + err = mDNS_StartBrowse(&mDNSStorage, &qptr->q, &t, &sdPtr->name, mDNSInterface_Any, FoundInstance, x); + if (err) { AbortClient(client, x); errormsg = "mDNS_StartBrowse"; goto fail; } + } + } // Succeeded: Wrap up and return EnableDeathNotificationForClient(client, x); + mDNS_FreeDNameList(SearchDomains); return(mStatus_NoError); - -badparam: + + badparam: err = mStatus_BadParamErr; fail: LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", client, regtype, domain, errormsg, err); + if (SearchDomains) mDNS_FreeDNameList(SearchDomains); return(err); } @@ -834,8 +1050,8 @@ mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) } 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]); + LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort, + x->i.name.c, &query->info->ip, mDNSVal16(query->info->port)); status = DNSServiceResolverReply_rpc(x->ClientMachPort, (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT); if (status == MACH_SEND_TIMED_OUT) @@ -857,7 +1073,7 @@ mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unuse 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 (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; } if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } // Allocate memory, and handle failure @@ -885,7 +1101,7 @@ mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unuse EnableDeathNotificationForClient(client, x); return(mStatus_NoError); -badparam: +badparam: err = mStatus_BadParamErr; fail: LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", client, name, regtype, domain, errormsg, err); @@ -902,7 +1118,7 @@ mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus re if (result == mStatus_NoError) { kern_return_t status; - LogOperation("%5d: DNSServiceRegistration(%##s) Name Registered", x->ClientMachPort, sr->RR_SRV.resrec.name.c); + LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT); if (status == MACH_SEND_TIMED_OUT) AbortBlockedClient(x->ClientMachPort, "registration success", x); @@ -910,7 +1126,7 @@ mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus re else if (result == mStatus_NameConflict) { - LogOperation("%5d: DNSServiceRegistration(%##s) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c); + LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); // 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) @@ -925,18 +1141,22 @@ mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus re AbortBlockedClient(x->ClientMachPort, "registration conflict", x); } } - + else if (result == mStatus_MemFree) { - if (x->autorename) + mDNSBool *autorename = (sr == &x->ls ? &x->autorenameLS : &x->autorenameGS); + if (*autorename) { debugf("RegCallback renaming %#s to %#s", x->name.c, mDNSStorage.nicelabel.c); - x->autorename = mDNSfalse; + *autorename = mDNSfalse; x->name = mDNSStorage.nicelabel; - mDNS_RenameAndReregisterService(m, &x->s, &x->name); + mDNS_RenameAndReregisterService(m, sr, &x->name); } else { + // SANITY CHECK: Should only get mStatus_MemFree as a result of calling mDNS_DeregisterService() + // and should only get it with x->autorename false if we've already removed the record from our + // list, but this check is just to make sure... DNSServiceRegistration **r = &DNSServiceRegistrationList; while (*r && *r != x) r = &(*r)->next; if (*r) @@ -944,14 +1164,18 @@ mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus re 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); + // END SANITY CHECK + LogOperation("%5d: DNSServiceRegistration(%##s, %u) Memory Free", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); + FreeDNSServiceRegistration(sr); } } else - LogMsg("%5d: DNSServiceRegistration(%##s) Unknown Result %ld", - x->ClientMachPort, sr->RR_SRV.resrec.name.c, result); + { + LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %ld", + x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr), result); + if (sr == x->gs) { freeL("RegCallback - ServiceRecordSet", x->gs); x->gs = NULL; } + } } mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainname *srv, mDNSIPPort port) @@ -965,23 +1189,35 @@ mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainn 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]); + LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.", + x->ClientMachPort, count, srv->c, mDNSVal16(port)); } -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) +// Pass NULL for x to allocate the structure (for local service). Call again w/ initialized x to add a global service. +mDNSlocal DNSServiceRegistration *RegisterService(mach_port_t client, DNSCString name, DNSCString regtype, DNSCString domain, + int notAnIntPort, DNSCString txtRecord, DNSServiceRegistration *x) { - // Check client parameter - (void)unusedserver; // Unused + ServiceRecordSet *srs = NULL; // record set to use in registration operation 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 - mDNSs32 NumSubTypes = CountSubTypes(regtype); - if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; } + + // 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; @@ -989,8 +1225,9 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t un 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 (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + + if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; } if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } mDNSIPPort port; @@ -1028,50 +1265,100 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t un 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; } - - AuthRecord *SubTypes = AllocateSubTypes(NumSubTypes, regtype); - if (NumSubTypes && !SubTypes) - { freeL("DNSServiceRegistration", x); err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - - // 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; + if (!x) + { + x = mallocL("DNSServiceRegistration", sizeof(*x) - sizeof(RDataBody) + size); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + bzero(x, sizeof(*x) - sizeof(RDataBody) + size); + // Set up object, and link into list + x->ClientMachPort = client; + x->autoname = (!name[0]); + x->name = n; + x->next = DNSServiceRegistrationList; + DNSServiceRegistrationList = x; + srs = &x->ls; + } + else + { + x->gs = mallocL("DNSServiceRegistration GlobalService", sizeof(ServiceRecordSet) - sizeof(RDataBody) + size); + if (!x->gs) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + srs = x->gs; + bzero(srs, sizeof(ServiceRecordSet) - sizeof(RDataBody) + size); + } + + 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 + } + } // Do the operation - LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\") START", x->ClientMachPort, name, regtype, domain); + LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START", + x->ClientMachPort, name, regtype, domain, mDNSVal16(port)); // 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 + err = mDNS_RegisterService(&mDNSStorage, srs, + &x->name, &t, &d, // Name, type, domain mDNSNULL, port, // Host and port txtinfo, data_len, // TXT data, length SubTypes, NumSubTypes, // Subtypes mDNSInterface_Any, // Interface ID RegCallback, x); // Callback and context - if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } + if (err) + { + if (srs == &x->ls) AbortClient(client, x); // don't abort client for global service + else FreeDNSServiceRegistration(x->gs); + errormsg = "mDNS_RegisterService"; + goto fail; + } + return x; + +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 NULL; + } +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"; + DNSServiceRegistration *x = NULL; + 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; } + + x = RegisterService(client, name, regtype, *domain ? domain : "local.", notAnIntPort, txtRecord, NULL); + if (!x) { err = mStatus_UnknownErr; goto fail; } + + //!!!KRS if we got a dynamic reg domain from the config file, use it for default (except for iChat) + if (!*domain && mDNSStorage.uDNS_info.ServiceRegDomain[0] && strcmp(regtype, "_presence._tcp.") && strcmp(regtype, "_ichat._tcp.")) + x = RegisterService(client, name, regtype, mDNSStorage.uDNS_info.ServiceRegDomain, notAnIntPort, txtRecord, x); + // 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); + client, name, regtype, domain, notAnIntPort, errormsg, err); + return mStatus_UnknownErr; } mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) @@ -1084,8 +1371,9 @@ mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) 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); + r->autorenameLS = mDNStrue; + mDNS_DeregisterService(&mDNSStorage, &r->ls); + if (r->gs) { mDNS_DeregisterService(&mDNSStorage, r->gs); r->autorenameGS = mDNStrue; } } udsserver_handle_configchange(); } @@ -1101,22 +1389,17 @@ mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) //************************************************************************************************************* // 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) + +mDNSlocal ExtraResourceRecord *AddExtraRecord(DNSServiceRegistration *x, ServiceRecordSet *srs, mach_port_t client, + int type, 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; + name = &srs->RR_SRV.resrec.name; - // Check other parameters - if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; } + (void)x; // unused + unsigned int size = sizeof(RDataBody); if (size < data_len) size = data_len; @@ -1133,56 +1416,79 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t // 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; } + client, srs->RR_SRV.resrec.name.c, type, data_len, extra); + err = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl); + + if (err) + { + freeL("Extra Resource Record", extra); + errormsg = "mDNS_AddRecordToService"; + goto fail; + } - // Succeeded: Wrap up and return - return(mStatus_NoError); + return extra; fail: LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, name->c, type, data_len, errormsg, err); - return(err); + return NULL; } -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) +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; } + + ExtraRecordRef *ref = mallocL("ExtraRecordRef", sizeof(ExtraRecordRef)); + if (!ref) { LogMsg("ERROR: malloc"); return mStatus_NoMemoryErr; } + + ref->localRef = AddExtraRecord(x, &x->ls, client, type, data, data_len, ttl); + if (!ref->localRef) { freeL("ExtraRecordRef", ref); *reference = (natural_t)NULL; return mStatus_UnknownErr; } + + if (x->gs) ref->globalRef = AddExtraRecord(x, x->gs, client, type, data, data_len, ttl); // return success even if global case fails + else ref->globalRef = NULL; + + // Succeeded: Wrap up and return + ref->next = x->ExtraRefList; + x->ExtraRefList = ref; + *reference = (natural_t)ref; + return mStatus_NoError; + +fail: + LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, x->name.c, type, data_len, errormsg, err); + return mStatus_UnknownErr; + } + +mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData) + { + (void)m; // Unused + if (OldRData != &rr->rdatastorage) + freeL("Old RData", OldRData); + } + +mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) + { + // Check client parameter + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + domainname *name = (domainname *)""; + + name = &srs->RR_SRV.resrec.name; + unsigned int size = sizeof(RDataBody); - if (size < data_len) + 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; } @@ -1192,16 +1498,89 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_por 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); + LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)", + client, srs->RR_SRV.resrec.name.c, data_len); + err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback); - if (err) { errormsg = "mDNS_Update"; goto fail; } + if (err) + { + errormsg = "mDNS_Update"; + freeL("RData", newrdata); + return err; + } + return(mStatus_NoError); + +fail: + LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%ld)", client, name->c, data_len, errormsg, err); + return(err); + } + + +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 + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + domainname *name = (domainname *)""; + AuthRecord *gRR, *lRR; + + (void)unusedserver; // unused + 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; } + + // Check other parameters + if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; } + + // Find the record we're updating. NULL reference means update the primary TXT record + if (!reference) + { + lRR = &x->ls.RR_TXT; + gRR = x->gs ? &x->gs->RR_TXT : NULL; + } + else + { + ExtraRecordRef *ref; + for (ref = x->ExtraRefList; ref; ref= ref->next) + if (ref == (ExtraRecordRef *)reference) break; + if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; } + lRR = &ref->localRef->r; + gRR = ref->globalRef ? &ref->globalRef->r : NULL; + } + + err = UpdateRecord(&x->ls, client, lRR, data, data_len, ttl); + if (err) goto fail; + + if (gRR) UpdateRecord(x->gs, client, gRR, data, data_len, ttl); // don't return error if global fails + return mStatus_NoError; + +fail: + LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err); + return(err); + } + +mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client) + { + domainname *name = &srs->RR_SRV.resrec.name; + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + // Do the operation + LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name.c); + + err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, 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: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err); + LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s", client, name->c, errormsg, err); return(err); } @@ -1212,27 +1591,33 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_por (void)unusedserver; // Unused mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; - domainname *name = (domainname *)""; + ExtraRecordRef *ref, *prev = NULL; 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; } + ref = x->ExtraRefList; + while (ref) + { + if (ref == (ExtraRecordRef *)ref) break; + prev = ref; + ref = ref->next; + } + + if (!ref) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; } + err = RemoveRecord(&x->ls, ref->localRef, client); + if (x->gs && ref->globalRef) RemoveRecord(x->gs, ref->globalRef, client); // don't return error if this fails - // 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); + // delete the ref struct + if (prev) prev->next = ref->next; + else x->ExtraRefList = ref->next; + ref->next = NULL; + freeL("ExtraRecordRef", ref); + return err; fail: - LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s (%ld)", client, name->c, reference, errormsg, err); + LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%ld)", client, reference, errormsg, err); return(err); } @@ -1400,10 +1785,10 @@ mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *i debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active); */ - LogMsg("%s stopping", mDNSResponderVersionString); + LogMsgIdent(mDNSResponderVersionString, "stopping"); debugf("ExitCallback: destroyBootstrapService"); - if (!debug_mode) + if (!mDNS_DebugMode) destroyBootstrapService(); debugf("ExitCallback: Aborting MIG clients"); @@ -1418,9 +1803,7 @@ mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *i debugf("ExitCallback: mDNS_Close"); mDNS_Close(&mDNSStorage); -#if ENABLE_UDS if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed"); -#endif exit(0); } @@ -1450,42 +1833,74 @@ mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *i DNSServiceBrowser *b; DNSServiceResolver *l; DNSServiceRegistration *r; - mDNSs32 slot; + NetworkInterfaceInfoOSX *i; + mDNSu32 slot; CacheRecord *rr; mDNSu32 CacheUsed = 0, CacheActive = 0; + mDNSs32 now = mDNSPlatformTimeNow(); - LogMsg("%s ---- BEGIN STATE LOG ----", mDNSResponderVersionString); + LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----"); for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + mDNSu32 SlotUsed = 0; for (rr = mDNSStorage.rrcache_hash[slot]; rr; rr=rr->next) { + mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond; CacheUsed++; + SlotUsed++; if (rr->CRActiveQuestion) CacheActive++; - LogMsg("%s %-5s%-6s%s", rr->CRActiveQuestion ? "Active: " : "Inactive:", DNSTypeName(rr->resrec.rrtype), + LogMsgNoIdent("%s%6ld %-6s%-6s%s", rr->CRActiveQuestion ? "*" : " ", remain, 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_used[slot] != SlotUsed) + LogMsgNoIdent("Cache use mismatch: rrcache_used[slot] is %lu, true count %lu", mDNSStorage.rrcache_used[slot], SlotUsed); + } if (mDNSStorage.rrcache_totalused != CacheUsed) - LogMsg("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage.rrcache_totalused, CacheUsed); + LogMsgNoIdent("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); + LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", mDNSStorage.rrcache_active, CacheActive); + LogMsgNoIdent("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); + LogMsgNoIdent("%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); - + { + DNSServiceBrowserQuestion *qptr; + for (qptr = b->qlist; qptr; qptr = qptr->next) + LogMsgNoIdent("%5d: ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c); + } for (l = DNSServiceResolverList; l; l=l->next) - LogMsg("%5d: ServiceResolve %##s", l->ClientMachPort, l->i.name.c); + LogMsgNoIdent("%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); + { + LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->ls.RR_SRV.resrec.name.c, mDNSVal16(r->ls.RR_SRV.resrec.rdata->u.srv.port)); + if (r->gs) LogMsgNoIdent("%5d: ServiceRegistration %##s %u", r->ClientMachPort, r->gs->RR_SRV.resrec.name.c, mDNSVal16(r->gs->RR_SRV.resrec.rdata->u.srv.port)); + } udsserver_info(); - LogMsg("%s ---- END STATE LOG ----", mDNSResponderVersionString); + for (i = mDNSStorage.p->InterfaceList; i; i = i->next) + { + if (!i->Exists) + LogMsgNoIdent("Interface: %s %5s(%lu) DORMANT", + i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id); + else + LogMsgNoIdent("Interface: %s %5s(%lu) %s %s %2d %s %2d InterfaceID %p %s %s %#a", + i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, + i->ifinfo.InterfaceActive ? "Active" : " ", + i->ifinfo.IPv4Available ? "v4" : " ", i->ss.sktv4, + i->ifinfo.IPv6Available ? "v6" : " ", i->ss.sktv6, + i->ifinfo.InterfaceID, + i->ifinfo.Advertise ? "Adv" : " ", + i->ifinfo.McastTxRx ? "TxRx" : " ", + &i->ifinfo.ip); + } + + LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----"); } mDNSlocal void HandleSIGINFO(int signal) @@ -1529,6 +1944,7 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) 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); @@ -1543,13 +1959,9 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) 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 (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); + err = udsserver_init(&mDNSStorage); 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); } @@ -1558,11 +1970,11 @@ 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(); - + 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 @@ -1611,7 +2023,7 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(void) if (l->ReportTime && now - l->ReportTime >= 0) { l->ReportTime = 0; - LogMsg("%5d: DNSServiceResolver(%##s) has remained active for over two minutes. " + LogMsg("%5d: DNSServiceResolver(%##s) active for over two minutes. " "This places considerable burden on the network.", l->ClientMachPort, l->i.name.c); } @@ -1622,11 +2034,10 @@ mDNSexport int main(int argc, char **argv) { int i; kern_return_t status; - FILE *fp; - + for (i=1; ipw_uid); + const struct passwd *pw = getpwnam("nobody"); + if (pw != NULL) + setuid(pw->pw_uid); else setuid(-2); // User "nobody" is -2; use that value if "nobody" does not appear in the password database @@ -1688,13 +2091,19 @@ mDNSexport int main(int argc, char **argv) // 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; + static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins + if (ticks > 1) + RepeatedBusy = 0; + else + { + ticks = 1; + if (++RepeatedBusy >= mDNSPlatformOneSecond * 10) + { LogMsg("Task Scheduling Error: Continuously busy for the last ten seconds"); RepeatedBusy = 0; } + } CFAbsoluteTime interval = (CFAbsoluteTime)ticks / (CFAbsoluteTime)mDNSPlatformOneSecond; // 3. Now do a blocking "CFRunLoopRunInMode" call so we sleep until @@ -1717,10 +2126,98 @@ mDNSexport int main(int argc, char **argv) mDNS_Close(&mDNSStorage); } - destroyBootstrapService(); + if (!mDNS_DebugMode) destroyBootstrapService(); return(status); } +// uds_daemon.c support routines ///////////////////////////////////////////// + +// We keep a list of client-supplied event sources in PosixEventSource records +struct CFSocketEventSource + { + udsEventCallback Callback; + void *Context; + int fd; + struct CFSocketEventSource *Next; + CFSocketRef SocketRef; + CFRunLoopSourceRef RLS; + }; +typedef struct CFSocketEventSource CFSocketEventSource; + +static GenLinkedList gEventSources; // linked list of CFSocketEventSource's + +static void cf_callback(CFSocketRef s _UNUSED, CFSocketCallBackType t _UNUSED, CFDataRef dr _UNUSED, const void *c _UNUSED, void *i) + // Called by CFSocket when data appears on socket + { + CFSocketEventSource *source = (CFSocketEventSource*) i; + source->Callback(source->Context); + } + +mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context) + // Arrange things so that callback is called with context when data appears on fd + { + CFSocketEventSource *newSource; + CFSocketContext cfContext = { 0, NULL, NULL, NULL, NULL }; + + if (gEventSources.LinkOffset == 0) + InitLinkedList(&gEventSources, offsetof(CFSocketEventSource, Next)); + + if (fd >= FD_SETSIZE || fd < 0) + return mStatus_UnsupportedErr; + if (callback == NULL) + return mStatus_BadParamErr; + + newSource = (CFSocketEventSource*) calloc(1, sizeof *newSource); + if (NULL == newSource) + return mStatus_NoMemoryErr; + + newSource->Callback = callback; + newSource->Context = context; + newSource->fd = fd; + + cfContext.info = newSource; + if ( NULL != (newSource->SocketRef = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, + cf_callback, &cfContext)) && + NULL != (newSource->RLS = CFSocketCreateRunLoopSource(kCFAllocatorDefault, newSource->SocketRef, 0))) + { + CFRunLoopAddSource(CFRunLoopGetCurrent(), newSource->RLS, kCFRunLoopDefaultMode); + AddToTail(&gEventSources, newSource); + } + else + { + if (newSource->SocketRef) + { + CFSocketInvalidate(newSource->SocketRef); // automatically closes socket + CFRelease(newSource->SocketRef); + } + return mStatus_NoMemoryErr; + } + + return mStatus_NoError; + } + +mStatus udsSupportRemoveFDFromEventLoop(int fd) + // Reverse what was done in udsSupportAddFDToEventLoop(). + { + CFSocketEventSource *iSource; + + for (iSource=(CFSocketEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + { + if (fd == iSource->fd) + { + RemoveFromList(&gEventSources, iSource); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), iSource->RLS, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(iSource->RLS); + CFRelease(iSource->RLS); + CFSocketInvalidate(iSource->SocketRef); + CFRelease(iSource->SocketRef); + free(iSource); + return mStatus_NoError; + } + } + return mStatus_NoSuchNameErr; + } + // For convenience when using the "strings" command, this is the last thing in the file mDNSexport const char mDNSResponderVersionString[] = STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; diff --git a/mDNSMacOSX/dnssd_clientstub.c b/mDNSMacOSX/dnssd_clientstub.c deleted file mode 100755 index 7277eda..0000000 --- a/mDNSMacOSX/dnssd_clientstub.c +++ /dev/null @@ -1,998 +0,0 @@ -/* - * 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 deleted file mode 100644 index 186f4d3..0000000 --- a/mDNSMacOSX/dnssd_ipc.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 deleted file mode 100644 index 2b7b323..0000000 --- a/mDNSMacOSX/dnssd_ipc.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - - Change History (most recent first): - -$Log: dnssd_ipc.h,v $ -Revision 1.6 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 - - */ - -#ifndef DNSSD_IPC_H -#define DNSSD_IPC_H - -#include "dns_sd.h" -#include -#include -#include -#include -#include -#include -#include - -//#define UDSDEBUG // verbose debug output - -// General UDS constants -#define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder" -#define LISTENQ 100 -#define TXT_RECORD_INDEX -1 // record index for default text record -#define MAX_CTLPATH 256 // longest legal control path length - -// IPC data encoding constants and types -#define VERSION 1 -#define IPC_FLAGS_NOREPLY 1 // set flag if no asynchronous replies are to be sent to client -#define IPC_FLAGS_REUSE_SOCKET 2 // set flag if synchronous errors are to be sent via the primary socket - // (if not set, first string in message buffer must be path to error socket - - -typedef enum - { - connection = 1, // connected socket via DNSServiceConnect() - reg_record_request, // reg/remove record only valid for connected sockets - remove_record_request, - enumeration_request, - reg_service_request, - browse_request, - resolve_request, - query_request, - reconfirm_record_request, - add_record_request, - update_record_request - } request_op_t; - -typedef enum - { - enumeration_reply = 64, - reg_service_reply, - browse_reply, - resolve_reply, - query_reply, - reg_record_reply - } reply_op_t; - - -typedef struct ipc_msg_hdr_struct ipc_msg_hdr; - - -// client stub callback to process message from server and deliver results to -// client application - -typedef void (*process_reply_callback) - ( - DNSServiceRef sdr, - ipc_msg_hdr *hdr, - char *msg - ); - -// allow 64-bit client to interoperate w/ 32-bit daemon -typedef union - { - void *context; - uint32_t ptr64[2]; - } client_context_t; - - -typedef struct ipc_msg_hdr_struct - { - uint32_t version; - uint32_t datalen; - uint32_t flags; - union - { - request_op_t request_op; - reply_op_t reply_op; - } op; - client_context_t client_context; // context passed from client, returned by server in corresponding reply - int reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a - // socket connected by DNSServiceConnect(). Must be unique in the scope of the connection, such that and - // index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord()) - } ipc_msg_hdr_struct; - - - - -// routines to write to and extract data from message buffers. -// caller responsible for bounds checking. -// ptr is the address of the pointer to the start of the field. -// it is advanced to point to the next field, or the end of the message - - -void put_flags(const DNSServiceFlags flags, char **ptr); -DNSServiceFlags get_flags(char **ptr); - -void put_long(const uint32_t l, char **ptr); -uint32_t get_long(char **ptr); - -void put_error_code(const DNSServiceErrorType, char **ptr); -DNSServiceErrorType get_error_code(char **ptr); - -int put_string(const char *str, char **ptr); -int get_string(char **ptr, char *buffer, int buflen); - -void put_rdata(const int rdlen, const char *rdata, char **ptr); -char *get_rdata(char **ptr, int rdlen); // return value is rdata pointed to by *ptr - - // rdata is not copied from buffer. - -void put_short(uint16_t s, char **ptr); -uint16_t get_short(char **ptr); - - - -#endif // DNSSD_IPC_H - - - - - - - - - - - diff --git a/mDNSMacOSX/mDNSMacOSX.c b/mDNSMacOSX/mDNSMacOSX.c deleted file mode 100644 index 4276c86..0000000 --- a/mDNSMacOSX/mDNSMacOSX.c +++ /dev/null @@ -1,1494 +0,0 @@ -/* - * 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.c,v $ -Revision 1.115.2.4 2004/04/23 00:34:06 cheshire -: mDNSResponder messages on wake -Take care to correctly update InterfaceIDs when a dormant interface comes back to life - -Revision 1.115.2.3 2004/04/08 23:18:11 cheshire - When interface turned off, browse "remove" events delivered with interface index zero -Refinement from Bob Bradley: Should use "mDNS *const m" instead of referencing mDNSStorage directly - -Revision 1.115.2.2 2004/04/08 00:42:37 cheshire - When interface turned off, browse "remove" events delivered with interface index zero -Unify use of the InterfaceID field, and make code that walks the list respect the CurrentlyActive flag - -Revision 1.115.2.1 2004/04/07 01:08:15 cheshire - When interface turned off, browse "remove" events delivered with interface index zero - -Revision 1.115 2003/09/10 00:45:55 cheshire - Don't log "sendto failed" errors during the first two minutes of startup - -Revision 1.114 2003/08/27 02:55:13 cheshire -: Bug: Don't report mDNSPlatformSendUDP sendto errno 64 (Host is down) - -Revision 1.113 2003/08/19 22:20:00 cheshire - Don't use IPv6 on interfaces that have a routable IPv4 address configured -More minor refinements - -Revision 1.112 2003/08/19 03:04:43 cheshire - Don't use IPv6 on interfaces that have a routable IPv4 address configured - -Revision 1.111 2003/08/18 22:53:37 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() - -Revision 1.110 2003/08/16 03:39:00 cheshire - InterfaceID -1 indicates "local only" - -Revision 1.109 2003/08/15 02:19:49 cheshire - syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 -Also limit number of messages to at most 100 - -Revision 1.108 2003/08/12 22:24:52 cheshire - syslog messages: myCFSocketCallBack recvfrom skt 6 error -1 errno 35 -This message indicates a kernel bug, but still we don't want to flood syslog. -Do a sleep(1) after writing this log message, to limit the rate. - -Revision 1.107 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 - -Revision 1.106 2003/08/12 13:48:32 cheshire -Add comment explaining clockdivisor calculation - -Revision 1.105 2003/08/12 13:44:14 cheshire - mDNSResponder *VERY* unhappy if time goes backwards -Use mach_absolute_time() (which is guaranteed to always go forwards, resetting only on reboot) -instead of gettimeofday() (which can jump back if the user manually changes their time/date) - -Revision 1.104 2003/08/12 13:12:07 cheshire -Textual search/replace: Indicate local functions using "mDNSlocal" instead of "static" - -Revision 1.103 2003/08/08 18:36:04 cheshire - Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug - -Revision 1.102 2003/08/06 00:14:52 cheshire - Need to check IP TTL on responses -Also add corresponding checks in the IPv6 code path - -Revision 1.101 2003/08/05 22:20:16 cheshire - Need to check IP TTL on responses - -Revision 1.100 2003/08/05 21:18:50 cheshire - mDNSResponder should ignore 6to4 -Only use interfaces that are marked as multicast-capable (IFF_MULTICAST) - -Revision 1.99 2003/08/05 20:13:52 cheshire - mDNSResponder using IPv6 interfaces before they are ready -Ignore interfaces with the IN6_IFF_NOTREADY flag set - -Revision 1.98 2003/07/20 03:38:51 ksekar -Bug #: 3320722 -Completed support for Unix-domain socket based API. - -Revision 1.97 2003/07/19 03:15:16 cheshire -Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h, -and add the obvious trivial implementations to each platform support layer - -Revision 1.96 2003/07/18 00:30:00 cheshire - Remove mDNSResponder version from packet header and use HINFO record instead - -Revision 1.95 2003/07/12 03:15:20 cheshire - After SCDynamicStore notification, mDNSResponder updates -m->hostlabel even if user hasn't actually actually changed their dot-local hostname - -Revision 1.94 2003/07/03 00:51:54 cheshire - When select() and recvmgs() disagree, get more info from kernel about the socket state - -Revision 1.93 2003/07/03 00:09:14 cheshire - New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call -Additional refinement suggested by Josh: Use info->scope_id instead of if_nametoindex(info->ifa_name); - -Revision 1.92 2003/07/02 21:19:51 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.91 2003/06/24 01:53:51 cheshire -Minor update to comments - -Revision 1.90 2003/06/24 01:51:47 cheshire - Oops: Double-dispose of sockets -Don't need to close sockets: CFSocketInvalidate() does that for us - -Revision 1.89 2003/06/21 18:12:47 cheshire - Rendezvous cannot handle interfaces whose total name is >3 chars -One-line change: should say "IF_NAMESIZE", not sizeof(ifname) - -Revision 1.88 2003/06/12 23:38:37 cheshire - mDNSResponder doesn't detect some configuration changes -Also check that scope_id matches before concluding that two interfaces are the same - -Revision 1.87 2003/06/10 01:14:11 cheshire - New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call - -Revision 1.86 2003/05/28 02:41:52 cheshire - Time to remove Mac OS 9 UDP Port 53 legacy support - -Revision 1.85 2003/05/28 02:39:47 cheshire -Minor change to debugging messages - -Revision 1.84 2003/05/27 22:29:40 cheshire -Remove out-dated comment - -Revision 1.83 2003/05/26 03:21:29 cheshire -Tidy up address structure naming: -mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr) -mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4 -mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6 - -Revision 1.82 2003/05/26 03:01:27 cheshire - sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead - -Revision 1.81 2003/05/24 02:06:42 cheshire - IPv6 Multicast Loopback doesn't work -Tried setting IPV6_MULTICAST_LOOP; it doesn't help. -However, it is probably wise to have the code explicitly set this socket -option anyway, in case the default changes in later versions of Unix. - -Revision 1.80 2003/05/24 02:02:24 cheshire - if_indextoname consumes a lot of CPU -Fix error in myIfIndexToName; was returning prematurely - -Revision 1.79 2003/05/23 23:07:44 cheshire - Must not write to stderr when running as daemon - -Revision 1.78 2003/05/23 01:19:04 cheshire - mDNSResponder needs to signal type of service to AirPort -Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate - -Revision 1.77 2003/05/23 01:12:05 cheshire -Minor code tidying - -Revision 1.76 2003/05/22 01:26:01 cheshire -Tidy up log messages - -Revision 1.75 2003/05/22 00:07:09 cheshire - myCFSocketCallBack recvfrom(5) error 1, errno 35 -Extra logging to determine whether there is a bug in CFSocket - -Revision 1.74 2003/05/21 20:20:12 cheshire -Fix warnings (mainly printf format string warnings, like using "%d" where -it should say "%lu", etc.) and improve error logging (use strerror() -to include textual error message as well as numeric error in log messages). - -Revision 1.73 2003/05/21 17:56:29 ksekar -Bug #: : mDNSResponder doesn't watch for IPv6 address changes - -Revision 1.72 2003/05/14 18:48:41 cheshire - mDNSResponder should be smarter about reconfigurations -More minor refinements: -CFSocket.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory -mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away - -Revision 1.71 2003/05/14 07:08:37 cheshire - mDNSResponder should be smarter about reconfigurations -Previously, when there was any network configuration change, mDNSResponder -would tear down the entire list of active interfaces and start again. -That was very disruptive, and caused the entire cache to be flushed, -and caused lots of extra network traffic. Now it only removes interfaces -that have really gone, and only adds new ones that weren't there before. - -Revision 1.70 2003/05/07 18:30:24 cheshire -Fix signed/unsigned comparison warning - -Revision 1.69 2003/05/06 20:14:44 cheshire -Change "tp" to "tv" - -Revision 1.68 2003/05/06 00:00:49 cheshire - Rationalize naming of domainname manipulation functions - -Revision 1.67 2003/04/29 00:43:44 cheshire -Fix compiler warnings - -Revision 1.66 2003/04/26 02:41:58 cheshire - Change timenow from a local variable to a structure member - -Revision 1.65 2003/04/26 02:34:01 cheshire -Add missing mDNSexport - -Revision 1.64 2003/04/15 16:48:06 jgraessl -Bug #: 3228833 -Modified code in CFSocket notifier function to read all packets on the socket -instead of reading only one packet every time the notifier was called. - -Revision 1.63 2003/04/15 16:33:50 jgraessl -Bug #: 3221880 -Switched to our own copy of if_indextoname to improve performance. - -Revision 1.62 2003/03/28 01:55:44 cheshire -Minor improvements to debugging messages - -Revision 1.61 2003/03/27 03:30:56 cheshire - Name conflicts not handled properly, resulting in memory corruption, and eventual crash -Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback -Fixes: -1. Make mDNS_DeregisterInterface() safe to call from a callback -2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead - (it never really needed to deregister the interface at all) - -Revision 1.60 2003/03/15 04:40:38 cheshire -Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID" - -Revision 1.59 2003/03/11 01:23:26 cheshire -Bug #: 3194246 mDNSResponder socket problems - -Revision 1.58 2003/03/06 01:43:04 cheshire -Bug #: 3189097 Additional debugging code in mDNSResponder -Improve "LIST_ALL_INTERFACES" output - -Revision 1.57 2003/03/05 22:36:27 cheshire -Bug #: 3186338 Loopback doesn't work with mDNSResponder-27 -Temporary workaround: Skip loopback interface *only* if we found at least one v4 interface to use - -Revision 1.56 2003/03/05 01:50:38 cheshire -Bug #: 3189097 Additional debugging code in mDNSResponder - -Revision 1.55 2003/02/21 01:54:09 cheshire -Bug #: 3099194 mDNSResponder needs performance improvements -Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt") - -Revision 1.54 2003/02/20 06:48:35 cheshire -Bug #: 3169535 Xserve RAID needs to do interface-specific registrations -Reviewed by: Josh Graessley, Bob Bradley - -Revision 1.53 2003/01/29 02:21:23 cheshire -Return mStatus_Invalid if can't send packet because socket not available - -Revision 1.52 2003/01/28 19:39:43 jgraessl -Enabling AAAA over IPv4 support. - -Revision 1.51 2003/01/28 05:11:23 cheshire -Fixed backwards comparison in SearchForInterfaceByName - -Revision 1.50 2003/01/13 23:49:44 jgraessl -Merged changes for the following fixes in to top of tree: -3086540 computer name changes not handled properly -3124348 service name changes are not properly handled -3124352 announcements sent in pairs, failing chattiness test - -Revision 1.49 2002/12/23 22:13:30 jgraessl -Reviewed by: Stuart Cheshire -Initial IPv6 support for mDNSResponder. - -Revision 1.48 2002/11/22 01:37:52 cheshire -Bug #: 3108426 mDNSResponder is monitoring ServiceEntities instead of InterfaceEntities - -Revision 1.47 2002/09/21 20:44:51 zarzycki -Added APSL info - -Revision 1.46 2002/09/19 21:25:35 cheshire -mDNS_snprintf() doesn't need to be in a separate file - -Revision 1.45 2002/09/17 01:45:13 cheshire -Add LIST_ALL_INTERFACES symbol for debugging - -Revision 1.44 2002/09/17 01:36:23 cheshire -Move Puma support to CFSocketPuma.c - -Revision 1.43 2002/09/17 01:05:28 cheshire -Change mDNS_AdvertiseLocalAddresses to be an Init parameter instead of a global - -Revision 1.42 2002/09/16 23:13:50 cheshire -Minor code tidying - - */ - -// *************************************************************************** -// mDNS-CFSocket.c: -// Supporting routines to run mDNS on a CFRunLoop platform -// *************************************************************************** - -// Open Transport 2.7.x on Mac OS 9 used to send Multicast DNS queries to UDP port 53, -// before the Multicast DNS port was changed to 5353. For this reason, the mDNSResponder -// in earlier versions of Mac OS X 10.2 Jaguar used to set mDNS_AllowPort53 to 1 to allow -// it to also listen and answer queries on UDP port 53. Now that Transport 2.8 (included in -// the Classic subsystem of Mac OS X 10.2 Jaguar) has been corrected to issue Multicast DNS -// queries on UDP port 5353, this backwards-compatibility legacy support is no longer needed. -#define mDNS_AllowPort53 0 - -// For debugging, set LIST_ALL_INTERFACES to 1 to display all found interfaces, -// including ones that mDNSResponder chooses not to use. -#define LIST_ALL_INTERFACES 0 - -// For enabling AAAA records over IPv4. Setting this to 0 sends only -// A records over IPv4 and AAAA over IPv6. Setting this to 1 sends both -// AAAA and A records over both IPv4 and IPv6. -#define AAAA_OVER_V4 1 - -#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above -#include "mDNSPlatformFunctions.h" // Defines the interface to the supporting layer below -#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform - -#include -#include // For select() and close() -#include // For va_list support -#include -#include -#include -#include -#include -#include -#include -#include - -#include // For IP_RECVTTL -#ifndef IP_RECVTTL -#define IP_RECVTTL 24 /* bool; receive reception TTL w/dgram */ -#endif - -#include // For n_long, required by below -#include // For IPTOS_LOWDELAY etc. -#include // For IN6_IFF_NOTREADY etc. - -// Code contributed by Dave Heller: -// Define RUN_ON_PUMA_WITHOUT_IFADDRS to compile code that will -// work on Mac OS X 10.1, which does not have the getifaddrs call. -#define RUN_ON_PUMA_WITHOUT_IFADDRS 0 -#if RUN_ON_PUMA_WITHOUT_IFADDRS -#include "CFSocketPuma.c" -#else -#include -#endif - -#include -#include -#include - -// *************************************************************************** -// Globals - -static mDNSu32 clockdivisor = 0; - -// *************************************************************************** -// Macros - -#define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger) -#define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3]) - -#define mDNSAddressIsAllDNSLinkGroup(X) ( \ - ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup )) || \ - ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) ) - -// *************************************************************************** -// Functions - -// Note, this uses mDNS_vsnprintf instead of standard "vsnprintf", because mDNS_vsnprintf knows -// how to print special data types like IP addresses and length-prefixed domain names -#if MDNS_DEBUGMSGS -mDNSexport void debugf_(const char *format, ...) - { - unsigned char buffer[512]; - va_list ptr; - va_start(ptr,format); - buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; - va_end(ptr); - fprintf(stderr,"%s\n", buffer); - fflush(stderr); - } -#endif - -#if MDNS_DEBUGMSGS > 1 -mDNSexport void verbosedebugf_(const char *format, ...) - { - unsigned char buffer[512]; - va_list ptr; - va_start(ptr,format); - buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; - va_end(ptr); - fprintf(stderr,"%s\n", buffer); - fflush(stderr); - } -#endif - -mDNSexport void LogMsg(const char *format, ...) - { - unsigned char buffer[512]; - va_list ptr; - va_start(ptr,format); - buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; - va_end(ptr); - - extern int debug_mode; - if (debug_mode) // In debug_mode we write to stderr - { - fprintf(stderr,"%s\n", buffer); - fflush(stderr); - } - else // else, in production mode, we write to syslog - { - openlog("mDNSResponder", LOG_CONS | LOG_PERROR | LOG_PID, LOG_DAEMON); - syslog(LOG_ERR, "%s", buffer); - closelog(); - } - } - -mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh) - { - static struct ifaddrs *ifa = NULL; - - if (refresh && ifa) - { - freeifaddrs(ifa); - ifa = NULL; - } - - if (ifa == NULL) getifaddrs(&ifa); - - return ifa; - } - -mDNSlocal int myIfIndexToName(u_short index, char* name) - { - struct ifaddrs *ifa; - for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) - if (ifa->ifa_addr->sa_family == AF_LINK) - if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == index) - { strncpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; } - return -1; - } - -mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index) - { - NetworkInterfaceInfoOSX *i; - if (index == (uint32_t)~0) return((mDNSInterfaceID)~0); - if (index) - for (i = m->p->InterfaceList; i; i = i->next) - if (i->ifinfo.InterfaceID && i->scope_id == index) // Don't get tricked by inactive interfaces - 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) - // Don't use i->ifinfo.InterfaceID here because we want to find inactive interfaces where that's not set - if ((mDNSInterfaceID)i == id) - return i->scope_id; - return 0; - } - -mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, mDNSIPPort srcPort, const mDNSAddr *dst, mDNSIPPort dstPort) - { - #pragma unused(m) - NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID; - struct sockaddr_storage to; - int s, err; - - if (!InterfaceID) { LogMsg("mDNSPlatformSendUDP ERROR! Cannot send from zero InterfaceID"); return mStatus_BadParamErr; } - - if (dst->type == mDNSAddrType_IPv4) - { - struct sockaddr_in* sin_to = (struct sockaddr_in*)&to; - sin_to->sin_len = sizeof(*sin_to); - sin_to->sin_family = AF_INET; - sin_to->sin_port = dstPort.NotAnInteger; - sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger; - } - else if (dst->type == mDNSAddrType_IPv6) - { - struct sockaddr_in6* sin6_to = (struct sockaddr_in6*)&to; - sin6_to->sin6_len = sizeof(*sin6_to); - sin6_to->sin6_family = AF_INET6; - sin6_to->sin6_port = dstPort.NotAnInteger; - sin6_to->sin6_flowinfo = 0; - sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6; - sin6_to->sin6_scope_id = info->scope_id; - } - else - { - LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!\n"); - return mStatus_BadParamErr; - } - - if (srcPort.NotAnInteger == MulticastDNSPort.NotAnInteger) - { - if (dst->type == mDNSAddrType_IPv4) s = info->sktv4; - else if (dst->type == mDNSAddrType_IPv6) s = info->sktv6; - else s = -1; - } -#if mDNS_AllowPort53 - else if (srcPort.NotAnInteger == UnicastDNSPort.NotAnInteger && dst->type == mDNSAddrType_IPv4) - s = info->skt53; -#endif - else { LogMsg("Source port %d not allowed", (mDNSu16)srcPort.b[0]<<8 | srcPort.b[1]); return(-1); } - - if (s >= 0) - verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %X %s/%d to %#a:%d skt %d", - InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1], s); - else - verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %X %s/%d (socket of this type not available)", - InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1]); - - // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet - // If we don't have the corresponding type of socket available, then return mStatus_Invalid - if (s < 0) return(mStatus_Invalid); - - err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len); - if (err < 0) - { - // Don't report EHOSTDOWN (i.e. ARP failure) to unicast destinations - if (errno == EHOSTDOWN && !mDNSAddressIsAllDNSLinkGroup(dst)) return(err); - // Don't report EHOSTUNREACH in the first two minutes after boot - // This is because mDNSResponder intentionally starts up early in the boot process (See ) - // but this means that sometimes it starts before configd has finished setting up the multicast routing entries. - if (errno == EHOSTUNREACH && (mDNSu32)(m->timenow) < (mDNSu32)(mDNSPlatformOneSecond * 120)) return(err); - LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %s/%ld to %#a:%d skt %d error %d errno %d (%s)", - InterfaceID, info->ifa_name, dst->type, dst, (mDNSu16)dstPort.b[0]<<8 | dstPort.b[1], s, err, errno, strerror(errno)); - return(err); - } - - return(mStatus_NoError); - } - -mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, - struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) - { - static unsigned int numLogMessages = 0; - struct iovec databuffers = { (char *)buffer, max }; - struct msghdr msg; - ssize_t n; - struct cmsghdr *cmPtr; - char ancillary[1024]; - - *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be - - // Set up the message - msg.msg_name = (caddr_t)from; - msg.msg_namelen = *fromlen; - msg.msg_iov = &databuffers; - msg.msg_iovlen = 1; - msg.msg_control = (caddr_t)&ancillary; - msg.msg_controllen = sizeof(ancillary); - msg.msg_flags = 0; - - // Receive the data - n = recvmsg(s, &msg, 0); - if (n<0) - { - if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) returned error %d errno %d", s, n, errno); - return(-1); - } - if (msg.msg_controllen < (int)sizeof(struct cmsghdr)) - { - if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", - s, msg.msg_controllen, sizeof(struct cmsghdr)); - return(-1); - } - if (msg.msg_flags & MSG_CTRUNC) - { - if (numLogMessages++ < 100) LogMsg("CFSocket.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); - return(-1); - } - - *fromlen = msg.msg_namelen; - - // Parse each option out of the ancillary data. - for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) - { - // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) - { - dstaddr->type = mDNSAddrType_IPv4; - dstaddr->ip.v4.NotAnInteger = *(u_int32_t*)CMSG_DATA(cmPtr); - } - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) - { - struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); - if (sdl->sdl_nlen < IF_NAMESIZE) - { - mDNSPlatformMemCopy(sdl->sdl_data, ifname, sdl->sdl_nlen); - ifname[sdl->sdl_nlen] = 0; - // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); - } - } - if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL) - { - *ttl = *(u_char*)CMSG_DATA(cmPtr); - } - if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO) - { - struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr); - dstaddr->type = mDNSAddrType_IPv6; - dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr; - myIfIndexToName(ip6_info->ipi6_ifindex, ifname); - } - if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT) - { - *ttl = *(int*)CMSG_DATA(cmPtr); - } - } - - return(n); - } - -mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBackType, CFDataRef address, const void *data, void *context) - { - mDNSAddr senderAddr, destAddr; - mDNSIPPort senderPort, destPort = MulticastDNSPort; - NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)context; - mDNS *const m = info->m; - DNSMessage packet; - struct sockaddr_storage from; - size_t fromlen = sizeof(from); - char packetifname[IF_NAMESIZE] = ""; - int err, s1 = -1, skt = CFSocketGetNative(cfs); - int count = 0; - - (void)address; // Parameter not used - (void)data; // Parameter not used - - if (CallBackType != kCFSocketReadCallBack) LogMsg("myCFSocketCallBack: Why is CallBackType %d not kCFSocketReadCallBack?", CallBackType); - -#if mDNS_AllowPort53 - if (cfs == info->cfs53) { s1 = info->skt53; destPort = UnicastDNSPort; } - else -#endif - if (cfs == info->cfsv4) s1 = info->sktv4; - else if (cfs == info->cfsv6) s1 = info->sktv6; - - if (s1 < 0 || s1 != skt) - { - LogMsg("myCFSocketCallBack: s1 %d native socket %d, cfs %p", s1, skt, cfs); -#if mDNS_AllowPort53 - LogMsg("myCFSocketCallBack: cfs53 %p, skt53 %d", info->cfs53, info->skt53); -#endif - LogMsg("myCFSocketCallBack: cfsv4 %p, sktv4 %d", info->cfsv4, info->sktv4); - LogMsg("myCFSocketCallBack: cfsv6 %p, sktv6 %d", info->cfsv6, info->sktv6); - } - - mDNSu8 ttl; - while ((err = myrecvfrom(s1, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl)) >= 0) - { - count++; - if (from.ss_family == AF_INET) - { - struct sockaddr_in *sin = (struct sockaddr_in*)&from; - senderAddr.type = mDNSAddrType_IPv4; - senderAddr.ip.v4.NotAnInteger = sin->sin_addr.s_addr; - senderPort.NotAnInteger = sin->sin_port; - } - else if (from.ss_family == AF_INET6) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; - senderAddr.type = mDNSAddrType_IPv6; - senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; - senderPort.NotAnInteger = sin6->sin6_port; - } - else - { - LogMsg("myCFSocketCallBack from is unknown address family %d", from.ss_family); - return; - } - - // Even though we indicated a specific interface in the IP_ADD_MEMBERSHIP call, a weirdness of the - // sockets API means that even though this socket has only officially joined the multicast group - // on one specific interface, the kernel will still deliver multicast packets to it no matter which - // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug. - // To work around this weirdness, we use the IP_RECVIF option to find the name of the interface - // on which the packet arrived, and ignore the packet if it really arrived on some other interface. - if (strcmp(info->ifa_name, packetifname)) - { - verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s (Ignored -- really arrived on interface %s)", - &senderAddr, &destAddr, &info->ifinfo.ip, info->ifa_name, packetifname); - return; - } - else - verbosedebugf("myCFSocketCallBack got a packet from %#a to %#a on interface %#a/%s", - &senderAddr, &destAddr, &info->ifinfo.ip, info->ifa_name); - - if (err < (int)sizeof(DNSMessageHeader)) { debugf("myCFSocketCallBack packet length (%d) too short", err); return; } - - mDNSCoreReceive(m, &packet, (unsigned char*)&packet + err, &senderAddr, senderPort, &destAddr, destPort, info->ifinfo.InterfaceID, ttl); - } - - if (err < 0 && (errno != EWOULDBLOCK || count == 0)) - { - // Something is busted here. - // CFSocket says there is a packet, but myrecvfrom says there is not. - // Try calling select() to get another opinion. - // Find out about other socket parameter that can help understand why select() says the socket is ready for read - // All of this is racy, as data may have arrived after the call to select() - int save_errno = errno; - int so_error = -1; - int so_nread = -1; - int fionread = -1; - int solen = sizeof(int); - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(s1, &readfds); - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - int selectresult = select(s1+1, &readfds, NULL, NULL, &timeout); - if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1) - LogMsg("myCFSocketCallBack getsockopt(SO_ERROR) error %d", errno); - if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1) - LogMsg("myCFSocketCallBack getsockopt(SO_NREAD) error %d", errno); - if (ioctl(s1, FIONREAD, &fionread) == -1) - LogMsg("myCFSocketCallBack ioctl(FIONREAD) error %d", errno); - static unsigned int numLogMessages = 0; - if (numLogMessages++ < 100) - LogMsg("myCFSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d", - s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count); - sleep(1); // After logging this error, rate limit so we don't flood syslog - } - } - -// This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel -mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) - { - CFStringEncoding encoding = kCFStringEncodingUTF8; - CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); - if (cfs) - { - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); - } - } - -// This gets the text of the field currently labelled "Rendezvous Name" in the Sharing Prefs Control Panel -mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) - { - CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); - if (cfs) - { - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); - } - } - -mDNSlocal mStatus SetupSocket(NetworkInterfaceInfoOSX *i, mDNSIPPort port, int *s, CFSocketRef *c) - { - const int on = 1; - const int twofivefive = 255; - - if (*s >= 0) { LogMsg("SetupSocket ERROR: socket %d is already set", *s); return(-1); } - if (*c) { LogMsg("SetupSocket ERROR: CFSocketRef %p is already set", *c); return(-1); } - - // Open the socket... - int skt = socket(i->sa_family, SOCK_DGRAM, IPPROTO_UDP); - if (skt < 0) { LogMsg("socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } - - // ... with a shared UDP port - mStatus err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); - if (err < 0) { LogMsg("setsockopt - SO_REUSEPORT error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - if (i->sa_family == AF_INET) - { - // We want to receive destination addresses - err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); - if (err < 0) { LogMsg("setsockopt - IP_RECVDSTADDR error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // We want to receive interface identifiers - err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); - if (err < 0) { LogMsg("setsockopt - IP_RECVIF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // We want to receive packet TTL value so we can check it - err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); - // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it - - // Add multicast group membership on this interface - struct in_addr addr = { i->ifinfo.ip.ip.v4.NotAnInteger }; - struct ip_mreq imr; - imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; - imr.imr_interface = addr; - err = setsockopt(skt, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); - if (err < 0) { LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // Specify outgoing interface too - err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)); - if (err < 0) { LogMsg("setsockopt - IP_MULTICAST_IF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // Send unicast packets with TTL 255 - err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); - if (err < 0) { LogMsg("setsockopt - IP_TTL error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // And multicast packets with TTL 255 too - err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); - if (err < 0) { LogMsg("setsockopt - IP_MULTICAST_TTL error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate - const int ip_tosbits = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; - err = setsockopt(skt, IPPROTO_IP, IP_TOS, &ip_tosbits, sizeof(ip_tosbits)); - if (err < 0) { LogMsg("setsockopt - IP_TOS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // And start listening for packets - struct sockaddr_in listening_sockaddr; - listening_sockaddr.sin_family = AF_INET; - listening_sockaddr.sin_port = port.NotAnInteger; - listening_sockaddr.sin_addr.s_addr = 0; // Want to receive multicasts AND unicasts on this socket - err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); - if (err) - { - // If we fail to bind to port 53 (because we're not root), that's okay, just tidy up and silently continue - if (port.NotAnInteger == UnicastDNSPort.NotAnInteger) { close(skt); err = 0; } - else LogMsg("bind error %ld errno %d (%s)", err, errno, strerror(errno)); - return(err); - } - } - else if (i->sa_family == AF_INET6) - { - // We want to receive destination addresses and receive interface identifiers - err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); - if (err < 0) { LogMsg("setsockopt - IPV6_PKTINFO error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // We want to receive packet hop count value so we can check it - err = setsockopt(skt, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)); - if (err < 0) { LogMsg("setsockopt - IPV6_HOPLIMIT error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // We want to receive only IPv6 packets, without this option, we may - // get IPv4 addresses as mapped addresses. - err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); - if (err < 0) { LogMsg("setsockopt - IPV6_V6ONLY error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // Add multicast group membership on this interface - int interface_id = if_nametoindex(i->ifa_name); - struct ipv6_mreq i6mr; - i6mr.ipv6mr_interface = interface_id; - i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroupv6; - err = setsockopt(skt, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); - if (err < 0) { LogMsg("setsockopt - IPV6_JOIN_GROUP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // Specify outgoing interface too - err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interface_id, sizeof(interface_id)); - if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_IF error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // Send unicast packets with TTL 255 - err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive)); - if (err < 0) { LogMsg("setsockopt - IPV6_UNICAST_HOPS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // And multicast packets with TTL 255 too - err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive)); - if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_HOPS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // Note: IPV6_TCLASS appears not to be implemented on OS X right now (or indeed on ANY version of Unix?) - #ifdef IPV6_TCLASS - // Mark packets as high-throughput/low-delay (i.e. lowest reliability) to get maximum 802.11 multicast rate - int tclass = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; // This may not be right (since tclass is not implemented on OS X, I can't test it) - err = setsockopt(skt, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass)); - if (err < 0) { LogMsg("setsockopt - IPV6_TCLASS error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - #endif - - // Want to receive our own packets - err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); - if (err < 0) { LogMsg("setsockopt - IPV6_MULTICAST_LOOP error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - - // And start listening for packets - struct sockaddr_in6 listening_sockaddr6; - bzero(&listening_sockaddr6, sizeof(listening_sockaddr6)); - listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6); - listening_sockaddr6.sin6_family = AF_INET6; - listening_sockaddr6.sin6_port = port.NotAnInteger; - listening_sockaddr6.sin6_flowinfo = 0; -// listening_sockaddr6.sin6_addr = IN6ADDR_ANY_INIT; // Want to receive multicasts AND unicasts on this socket - listening_sockaddr6.sin6_scope_id = 0; - err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6)); - if (err) { LogMsg("bind error %ld errno %d (%s)", err, errno, strerror(errno)); return(err); } - } - - fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking - *s = skt; - CFSocketContext myCFSocketContext = { 0, i->ifinfo.InterfaceID, NULL, NULL, NULL }; - *c = CFSocketCreateWithNative(kCFAllocatorDefault, *s, kCFSocketReadCallBack, myCFSocketCallBack, &myCFSocketContext); - CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, *c, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); - CFRelease(rls); - - return(err); - } - -mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) - { - if (sa->sa_family == AF_INET) - { - struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa; - ip->type = mDNSAddrType_IPv4; - ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr; - return(0); - } - else if (sa->sa_family == AF_INET6) - { - struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; - ip->type = mDNSAddrType_IPv6; - if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0; - ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; - return(0); - } - else - { - LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); - return(-1); - } - } - -mDNSlocal mStatus AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa) - { - mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); - mDNSAddr ip; - SetupAddr(&ip, ifa->ifa_addr); - NetworkInterfaceInfoOSX **p; - for (p = &m->p->InterfaceList; *p; p = &(*p)->next) - if (scope_id == (*p)->scope_id && mDNSSameAddress(&ip, &(*p)->ifinfo.ip)) - { - debugf("AddInterfaceToList: Found existing interface %u with address %#a", scope_id, &ip); - (*p)->CurrentlyActive = mDNStrue; - return(0); - } - - debugf("AddInterfaceToList: Making new interface %u with address %#a", scope_id, &ip); - NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i)); - if (!i) return(-1); - i->ifa_name = (char *)mallocL("NetworkInterfaceInfoOSX name", strlen(ifa->ifa_name) + 1); - if (!i->ifa_name) { freeL("NetworkInterfaceInfoOSX", i); return(-1); } - strcpy(i->ifa_name, ifa->ifa_name); - - i->ifinfo.InterfaceID = mDNSNULL; - i->ifinfo.ip = ip; - i->ifinfo.Advertise = m->AdvertiseLocalAddresses; - i->ifinfo.TxAndRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList - - i->next = mDNSNULL; - i->m = m; - i->scope_id = scope_id; - i->CurrentlyActive = mDNStrue; - i->sa_family = ifa->ifa_addr->sa_family; - #if mDNS_AllowPort53 - i->skt53 = -1; - i->cfs53 = NULL; - #endif - i->sktv4 = -1; - i->cfsv4 = NULL; - i->sktv6 = -1; - i->cfsv6 = NULL; - - if (!i->ifa_name) return(-1); - *p = i; - return(0); - } - -mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id) - { - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->CurrentlyActive && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4) - if (!(i->ifinfo.ip.ip.v4.b[0] == 169 && i->ifinfo.ip.ip.v4.b[1] == 254)) - return(i); - return(mDNSNULL); - } - -mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) - { - mDNSBool foundav4 = mDNSfalse; - struct ifaddrs *ifa = myGetIfAddrs(1); - struct ifaddrs *theLoopback = NULL; - int err = (ifa != NULL) ? 0 : (errno != 0 ? errno : -1); - int InfoSocket = err ? -1 : socket(AF_INET6, SOCK_DGRAM, 0); - if (err) return(err); - - // Set up the nice label - m->nicelabel.c[0] = 0; - GetUserSpecifiedFriendlyComputerName(&m->nicelabel); - if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); - - // Set up the RFC 1034-compliant label - domainlabel hostlabel; - hostlabel.c[0] = 0; - GetUserSpecifiedRFC1034ComputerName(&hostlabel); - if (hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&hostlabel, "Macintosh"); - // If the user has changed their dot-local host name since the last time we checked, then update our local copy. - // If the user has not changed their dot-local host name, then leave ours alone (m->hostlabel may have gone through - // repeated conflict resolution to get to its current value, and if we reset it, we'll have to go through all that again.) - if (SameDomainLabel(m->p->userhostlabel.c, hostlabel.c)) - debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c); - else - { - debugf("Updating m->hostlabel to %#s", hostlabel.c); - m->p->userhostlabel = m->hostlabel = hostlabel; - mDNS_GenerateFQDN(m); - } - - while (ifa) - { -#if LIST_ALL_INTERFACES - if (ifa->ifa_addr->sa_family == AF_APPLETALK) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_APPLETALK", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - else if (ifa->ifa_addr->sa_family == AF_LINK) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d is AF_LINK", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - if (!(ifa->ifa_flags & IFF_UP)) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface not IFF_UP", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - if (ifa->ifa_flags & IFF_POINTOPOINT) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); - if (ifa->ifa_flags & IFF_LOOPBACK) - debugf("UpdateInterfaceList: %4s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK", - ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); -#endif - if ((ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) && - (ifa->ifa_flags & IFF_MULTICAST) && - (ifa->ifa_flags & IFF_UP) && !(ifa->ifa_flags & IFF_POINTOPOINT)) - { - int ifru_flags6 = 0; - if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - struct in6_ifreq ifr6; - bzero((char *)&ifr6, sizeof(ifr6)); - strncpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name)); - ifr6.ifr_addr = *sin6; - if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1) - ifru_flags6 = ifr6.ifr_ifru.ifru_flags6; - verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6); - } - if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) - { - if (ifa->ifa_flags & IFF_LOOPBACK) - theLoopback = ifa; - else - { - AddInterfaceToList(m, ifa); - if (ifa->ifa_addr->sa_family == AF_INET) - foundav4 = mDNStrue; - } - } - } - ifa = ifa->ifa_next; - } - -// Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work. -// In the interim, we skip loopback interface only if we found at least one v4 interface to use - if (!foundav4 && theLoopback) - AddInterfaceToList(m, theLoopback); - - // Now the list is complete, set the TxAndRx setting for each interface. - // We always send and receive using IPv4. - // To reduce traffic, we send and receive using IPv6 only on interfaces that have no routable IPv4 address. - // Having a routable IPv4 address assigned is a reasonable indicator of being on a large configured network, - // which means there's a good chance that most or all the other devices on that network should also have v4. - // By doing this we lose the ability to talk to true v6-only devices on that link, but we cut the packet rate in half. - // At this time, reducing the packet rate is more important than v6-only devices on a large configured network, - // so we are willing to make that sacrifice. - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->CurrentlyActive) - { - mDNSBool txrx = ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); - if (i->ifinfo.TxAndRx != txrx) - { - i->ifinfo.TxAndRx = txrx; - i->CurrentlyActive = 2; // State change; need to deregister and reregister this interface - } - } - - if (InfoSocket >= 0) close(InfoSocket); - return(err); - } - -mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, char *ifname, int type) - { - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (!strcmp(i->ifa_name, ifname) && - i->CurrentlyActive && - ((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) - if (i->CurrentlyActive) - { - 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 mDNSu32 NumCacheRecordsForInterfaceID(mDNS *const m, mDNSInterfaceID id) - { - mDNSu32 slot, used = 0; - CacheRecord *rr; - for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) - if (rr->resrec.InterfaceID == id) used++; - return(used); - } - -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 its InterfaceID is changing, deregister it - NetworkInterfaceInfoOSX *alias = (NetworkInterfaceInfoOSX *)(i->ifinfo.InterfaceID); - NetworkInterfaceInfoOSX *newalias = SearchForInterfaceByName(m, i->ifa_name, i->sa_family); - if (!newalias) newalias = i; - if (i->ifinfo.InterfaceID && (!i->CurrentlyActive || (alias && !alias->CurrentlyActive) || i->CurrentlyActive == 2 || newalias != alias)) - { - 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 && NumCacheRecordsForInterfaceID(m, (mDNSInterfaceID)i) == 0) - { - 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/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index b255cd0..ba07125 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,61 @@ Change History (most recent first): $Log: mDNSMacOSX.h,v $ +Revision 1.37 2004/06/04 08:58:30 ksekar +: Keychain integration for secure dynamic update + +Revision 1.36 2004/05/26 17:06:33 cheshire +: Don't rely on CFSocketInvalidate() to remove RunLoopSource + +Revision 1.35 2004/05/18 23:51:26 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.34 2004/05/12 22:03:09 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.33 2004/05/12 02:03:25 ksekar +Non-local domains will only be browsed by default, and show up in +_browse domain enumeration, if they contain an _browse._dns-sd ptr record. + +Revision 1.32 2004/04/21 02:20:47 cheshire +Rename interface field 'CurrentlyActive' to more descriptive 'Exists' + +Revision 1.31 2004/04/09 17:40:26 cheshire +Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing TxAndRx field + +Revision 1.30 2004/01/28 02:30:08 ksekar +Added default Search Domains to unicast browsing, controlled via +Networking sharing prefs pane. Stopped sending unicast messages on +every interface. Fixed unicast resolving via mach-port API. + +Revision 1.29 2004/01/27 22:57:48 cheshire +: Need separate socket for issuing unicast queries + +Revision 1.28 2004/01/27 20:15:23 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.27 2004/01/24 08:46:26 bradley +Added InterfaceID<->Index platform interfaces since they are now used by all platforms for the DNS-SD APIs. + +Revision 1.26 2003/12/08 21:00:46 rpantos +Changes to support mDNSResponder on Linux. + +Revision 1.25 2003/11/08 22:18:29 cheshire +: Don't need to show process ID in *every* mDNSResponder syslog message + +Revision 1.24 2003/11/08 22:13:00 cheshire +Move extern declarations inside '#ifdef __cplusplus extern "C" {' section + +Revision 1.23 2003/09/23 16:38:25 cheshire +When LogAllOperations is false, treat LogOperation() like debugf() +(i.e. show in debug builds), rather than unconditionally ignoring + +Revision 1.22 2003/09/23 02:12:43 cheshire +Also include port number in list of services registered via new UDS API + Revision 1.21 2003/08/19 22:20:00 cheshire Don't use IPv6 on interfaces that have a routable IPv4 address configured More minor refinements @@ -46,8 +103,7 @@ Revision 1.15 2003/08/05 00:32:28 cheshire Time to turn off MACOSX_MDNS_MALLOC_DEBUGGING Revision 1.14 2003/07/20 03:38:51 ksekar -Bug #: 3320722 -Completed support for Unix-domain socket based API. + Completed support for Unix-domain socket based API. Revision 1.13 2003/07/18 00:30:00 cheshire Remove mDNSResponder version from packet header and use HINFO record instead @@ -60,7 +116,7 @@ Revision 1.11 2003/07/02 21:19:51 cheshire Update copyright notices, etc., in source code comments Revision 1.10 2003/06/25 23:42:19 ksekar -Bug #: : Feature: New Rendezvous APIs (#7875) +: 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. @@ -80,13 +136,13 @@ 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 + Additional debugging code in mDNSResponder Revision 1.5 2003/03/05 01:50:38 cheshire -Bug #: 3189097 Additional debugging code in mDNSResponder + Additional debugging code in mDNSResponder Revision 1.4 2003/02/21 01:54:10 cheshire -Bug #: 3099194 mDNSResponder needs performance improvements + 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 @@ -111,30 +167,40 @@ Defines mDNS_PlatformSupport_struct for OS X #include #include #include +#include "mDNSClientAPI.h" // for domain name structure + typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX; + +typedef struct + { + mDNS *m; + NetworkInterfaceInfoOSX *info; + int sktv4; + CFSocketRef cfsv4; + CFRunLoopSourceRef rlsv4; + int sktv6; + CFSocketRef cfsv6; + CFRunLoopSourceRef rlsv6; + } CFSocketSet; + 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 + mDNSu32 Exists; // 1 = currently exists in getifaddrs list; 0 = doesn't + // 2 = exists, but McastTxRx 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; + mDNSBool Multicast; + CFSocketSet ss; }; struct mDNS_PlatformSupport_struct { NetworkInterfaceInfoOSX *InterfaceList; + CFSocketSet unicastsockets; domainlabel userhostlabel; SCDynamicStoreRef Store; CFRunLoopSourceRef StoreRLS; @@ -143,50 +209,12 @@ struct mDNS_PlatformSupport_struct 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/mDNSMacOSXPuma.c b/mDNSMacOSX/mDNSMacOSXPuma.c deleted file mode 100644 index a23179d..0000000 --- a/mDNSMacOSX/mDNSMacOSXPuma.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * 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: mDNSMacOSXPuma.c,v $ -Revision 1.4 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 - -Revision 1.3 2003/07/02 21:19:51 cheshire - Update copyright notices, etc., in source code comments - -Revision 1.2 2002/09/21 20:44:51 zarzycki -Added APSL info - -Revision 1.1 2002/09/17 01:36:23 cheshire -Move Puma support to CFSocketPuma.c - - */ - -#include -#include -#define ifaddrs ifa_info -#ifndef ifa_broadaddr -#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ -#endif -#include - -/* Our own header for the programs that need interface configuration info. - Include this file, instead of "unp.h". */ - -#define IFA_NAME 16 /* same as IFNAMSIZ in */ -#define IFA_HADDR 8 /* allow for 64-bit EUI-64 in future */ - -struct ifa_info { - char ifa_name[IFA_NAME]; /* interface name, null terminated */ - u_char ifa_haddr[IFA_HADDR]; /* hardware address */ - u_short ifa_hlen; /* #bytes in hardware address: 0, 6, 8 */ - short ifa_flags; /* IFF_xxx constants from */ - short ifa_myflags; /* our own IFI_xxx flags */ - struct sockaddr *ifa_addr; /* primary address */ - struct sockaddr *ifa_brdaddr;/* broadcast address */ - struct sockaddr *ifa_dstaddr;/* destination address */ - struct ifa_info *ifa_next; /* next of these structures */ -}; - -#define IFI_ALIAS 1 /* ifa_addr is an alias */ - - /* function prototypes */ -struct ifa_info *get_ifa_info(int, int); -struct ifa_info *Get_ifa_info(int, int); -void free_ifa_info(struct ifa_info *); - -#define HAVE_SOCKADDR_SA_LEN 1 - -struct ifa_info * -get_ifa_info(int family, int doaliases) -{ - struct ifa_info *ifi, *ifihead, **ifipnext; - int sockfd, len, lastlen, flags, myflags; - char *ptr, *buf, lastname[IFNAMSIZ], *cptr; - struct ifconf ifc; - struct ifreq *ifr, ifrcopy; - struct sockaddr_in *sinptr; - - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - - lastlen = 0; - len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ - for ( ; ; ) { - buf = (char *) malloc(len); - ifc.ifc_len = len; - ifc.ifc_buf = buf; - if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { - if (errno != EINVAL || lastlen != 0) - debugf("ioctl error"); - } else { - if (ifc.ifc_len == lastlen) - break; /* success, len has not changed */ - lastlen = ifc.ifc_len; - } - len += 10 * sizeof(struct ifreq); /* increment */ - free(buf); - } - ifihead = NULL; - ifipnext = &ifihead; - lastname[0] = 0; -/* end get_ifa_info1 */ - -/* include get_ifa_info2 */ - for (ptr = buf; ptr < buf + ifc.ifc_len; ) { - ifr = (struct ifreq *) ptr; - -#ifdef HAVE_SOCKADDR_SA_LEN - len = MAX(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); -#else - switch (ifr->ifr_addr.sa_family) { -#ifdef IPV6 - case AF_INET6: - len = sizeof(struct sockaddr_in6); - break; -#endif - case AF_INET: - default: - len = sizeof(struct sockaddr); - break; - } -#endif /* HAVE_SOCKADDR_SA_LEN */ - ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ - - if (ifr->ifr_addr.sa_family != family) - continue; /* ignore if not desired address family */ - - myflags = 0; - if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL) - *cptr = 0; /* replace colon will null */ - if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { - if (doaliases == 0) - continue; /* already processed this interface */ - myflags = IFI_ALIAS; - } - memcpy(lastname, ifr->ifr_name, IFNAMSIZ); - - ifrcopy = *ifr; - ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); - flags = ifrcopy.ifr_flags; - if ((flags & IFF_UP) == 0) - continue; /* ignore if interface not up */ - - ifi = (struct ifa_info *) calloc(1, sizeof(struct ifa_info)); - *ifipnext = ifi; /* prev points to this new one */ - ifipnext = &ifi->ifa_next; /* pointer to next one goes here */ - - ifi->ifa_flags = flags; /* IFF_xxx values */ - ifi->ifa_myflags = myflags; /* IFI_xxx values */ - memcpy(ifi->ifa_name, ifr->ifr_name, IFA_NAME); - ifi->ifa_name[IFA_NAME-1] = '\0'; -/* end get_ifa_info2 */ -/* include get_ifa_info3 */ - switch (ifr->ifr_addr.sa_family) { - case AF_INET: - sinptr = (struct sockaddr_in *) &ifr->ifr_addr; - if (ifi->ifa_addr == NULL) { - ifi->ifa_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_addr, sinptr, sizeof(struct sockaddr_in)); - -#ifdef SIOCGIFBRDADDR - if (flags & IFF_BROADCAST) { - ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; - ifi->ifa_brdaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_brdaddr, sinptr, sizeof(struct sockaddr_in)); - } -#endif - -#ifdef SIOCGIFDSTADDR - if (flags & IFF_POINTOPOINT) { - ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); - sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; - ifi->ifa_dstaddr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr_in)); - memcpy(ifi->ifa_dstaddr, sinptr, sizeof(struct sockaddr_in)); - } -#endif - } - break; - - default: - break; - } - } - free(buf); - return(ifihead); /* pointer to first structure in linked list */ -} -/* end get_ifa_info3 */ - -/* include free_ifa_info */ -mDNSlocal void freeifaddrs(struct ifa_info *ifihead) -{ - struct ifa_info *ifi, *ifinext; - - for (ifi = ifihead; ifi != NULL; ifi = ifinext) { - if (ifi->ifa_addr != NULL) - free(ifi->ifa_addr); - if (ifi->ifa_brdaddr != NULL) - free(ifi->ifa_brdaddr); - if (ifi->ifa_dstaddr != NULL) - free(ifi->ifa_dstaddr); - ifinext = ifi->ifa_next; /* can't fetch ifa_next after free() */ - free(ifi); /* the ifa_info{} itself */ - } -} -/* end free_ifa_info */ - -struct ifa_info * -Get_ifa_info(int family, int doaliases) -{ - struct ifa_info *ifi; - - if ( (ifi = get_ifa_info(family, doaliases)) == NULL) - debugf("get_ifa_info error"); - return(ifi); -} - -mDNSlocal int getifaddrs(struct ifa_info **ifalist) - { - *ifalist = get_ifa_info(PF_INET, false); - if( ifalist == nil ) - return -1; - else - return(0); - } diff --git a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj index aa4ee90..1a2786a 100644 --- a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj @@ -11,84 +11,11 @@ path = mDNSMacOSX.h; refType = 4; }; - 0017390704CC75C30CCA2C71 = { - fileEncoding = 4; - 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; - MACOSX_DEPLOYMENT_TARGET = 10.2; - 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; - }; - 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 = { fileEncoding = 4; isa = PBXFileReference; - path = dnssd_clientstub.c; + name = dnssd_clientstub.c; + path = ../mDNSShared/dnssd_clientstub.c; refType = 2; }; 00AD62A3032D799A0CCA2C71 = { @@ -101,11 +28,11 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; GCC_TREAT_WARNINGS_AS_ERRORS = YES; - HEADER_SEARCH_PATHS = "../mDNSShared \"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; + HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; LIBRARY_SEARCH_PATHS = ""; MACOSX_DEPLOYMENT_TARGET = 10.2; OPTIMIZATION_CFLAGS = "-O0"; - OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DMDNS_DEBUGMSGS=1 -DmDNSResponderVersion=$(MVERS) -DkDNSServiceFlagsRemove=0 -DkDNSServiceFlagsFinished=0"; + OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DMDNS_DEBUGMSGS=1 -DmDNSResponderVersion=$(MVERS)"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder.debug; @@ -128,7 +55,6 @@ F5E11B5F04A28126019798ED, F515E29604A37BB701CA296C, F515E29704A37BB801CA296C, - F515E29804A37BBB01CA296C, F515E29904A37BBB01CA296C, ); isa = PBXHeadersBuildPhase; @@ -149,6 +75,11 @@ 00AD62B0032D799A0CCA2C71, F5E11B5E04A28126019798ED, F525E72B04AA167A01F1CF4D, + DBAAFE2B057E8F4D0085CAD0, + DBAAFE2E057E8F660085CAD0, + 7F18A9FA0587CEF6001880B3, + 7F18A9FB0587CEF6001880B3, + 7F461DB7062DBF2900672BF3, 00AD62B1032D799A0CCA2C71, ); isa = PBXSourcesBuildPhase; @@ -197,6 +128,7 @@ 00AD62B4032D799A0CCA2C71, 00AD62B5032D799A0CCA2C71, 00AD62B6032D799A0CCA2C71, + 7F869687066EE02400D2A2DC, ); isa = PBXFrameworksBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -245,8 +177,8 @@ 00AD62BC032D7A160CCA2C71, 00AD62BD032D7A1B0CCA2C71, 00AD62BE032D7A1D0CCA2C71, - FF1B80C507AA1567000768F0, - FF1B80C607AA1569000768F0, + FFD41DDB0664169900F0C438, + FFD41DDC0664169B00F0C438, ); isa = PBXAggregateTarget; name = "Build All"; @@ -279,18 +211,6 @@ path = /System/Library/Frameworks/IOKit.framework; refType = 0; }; - 00DD152B04CC79700CCA2C71 = { - fileRef = 004EFB9604CC78130CCA2C71; - isa = PBXBuildFile; - settings = { - }; - }; - 00DD152C04CC79A50CCA2C71 = { - fileRef = F5E11B5A04A28126019798ED; - isa = PBXBuildFile; - settings = { - }; - }; //000 //001 //002 @@ -329,17 +249,16 @@ 08FB779FFE84155DC02AAC07, 00AD62A3032D799A0CCA2C71, 6575FC1C022EB76000000109, - 0044D34804CC73600CCA2C71, - FF518DC907AA0BA60096AE13, - FFEF84D207AA10DF00171646, + DB2CC4530662DD6800335AB3, + DB2CC4660662DF5C00335AB3, ); }; 08FB7794FE84155DC02AAC07 = { children = ( - FF518DCB07AA0C330096AE13, 08FB7795FE84155DC02AAC07, 6575FC1F022EB78C00000109, 6575FBFE022EAFA800000109, + DB2CC4420662DCE500335AB3, 08FB779DFE84155DC02AAC07, 19C28FBDFE9D53C911CA2CBB, ); @@ -349,6 +268,7 @@ }; 08FB7795FE84155DC02AAC07 = { children = ( + 7F461DB5062DBF2900672BF3, F525E72804AA167501F1CF4D, F5E11B5A04A28126019798ED, F5E11B5B04A28126019798ED, @@ -357,8 +277,13 @@ 6575FBEB022EAF7200000109, 654BE64F02B63B93000001D1, 654BE65002B63B93000001D1, - 654BE65202B63B93000001D1, + DBAAFE29057E8F4D0085CAD0, 000753D303367C1C0CCA2C71, + DBAAFE2C057E8F660085CAD0, + FF0E0B5D065ADC7600FE4D9C, + FF485D5105632E0000130380, + 7F18A9F60587CEF6001880B3, + 7F18A9F70587CEF6001880B3, ); isa = PBXGroup; name = "mDNS Server Sources"; @@ -367,10 +292,11 @@ }; 08FB779DFE84155DC02AAC07 = { children = ( + 7F869685066EE02400D2A2DC, 09AB6884FE841BABC02AAC07, 65713D46025A293200000109, 00CA213D02786FC30CCA2C71, - FFEF84D607AA115900171646, + DB2CC4680662DFF500335AB3, ); isa = PBXGroup; name = "External Frameworks and Libraries"; @@ -382,22 +308,23 @@ 08FB77A1FE84155DC02AAC07, 08FB77A3FE84155DC02AAC07, 08FB77A5FE84155DC02AAC07, + FF5A0AE705632EA600743C27, + FF0E0B5C065ADC3800FE4D9C, ); buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; GCC_TREAT_WARNINGS_AS_ERRORS = YES; - HEADER_SEARCH_PATHS = "../mDNSShared \"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; + HEADER_SEARCH_PATHS = "\"$(APPLE_INTERNAL_DEVELOPER_DIR)/Headers\""; INSTALL_PATH = /usr/sbin; LIBRARY_SEARCH_PATHS = ""; MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -DkDNSServiceFlagsRemove=0 -DkDNSServiceFlagsFinished=0"; + OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder; REZ_EXECUTABLE = YES; SECTORDER_FLAGS = ""; STRIPFLAGS = "-S"; - USE_GCC3_PFE_SUPPORT = NO; WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; }; dependencies = ( @@ -426,6 +353,11 @@ 6575FBED022EAF7200000109, F5E11B5C04A28126019798ED, F525E72904AA167501F1CF4D, + DBAAFE2A057E8F4D0085CAD0, + DBAAFE2D057E8F660085CAD0, + 7F18A9F80587CEF6001880B3, + 7F18A9F90587CEF6001880B3, + 7F461DB6062DBF2900672BF3, 6575FBEE022EAF7200000109, ); isa = PBXSourcesBuildPhase; @@ -437,6 +369,7 @@ 09AB6885FE841BABC02AAC07, 65713D66025A293200000109, 6585DD640279A3B7000001D1, + 7F869686066EE02400D2A2DC, ); isa = PBXFrameworksBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -485,9 +418,8 @@ 034768E2FF38A6DC11DB9C8B, 6575FC1D022EB76000000109, 00AD62B8032D799A0CCA2C71, - 0044D34F04CC73600CCA2C71, - FF72324E07AA0CB30012EE6F, - FFEF84D307AA10DF00171646, + DB2CC4670662DF5C00335AB3, + FFD41DDA0664157900F0C438, ); isa = PBXGroup; name = Products; @@ -517,13 +449,6 @@ path = ../mDNSCore/mDNSDebug.h; refType = 4; }; - 654BE65202B63B93000001D1 = { - fileEncoding = 4; - isa = PBXFileReference; - name = mDNSPlatformFunctions.h; - path = ../mDNSCore/mDNSPlatformFunctions.h; - refType = 4; - }; 65713D46025A293200000109 = { isa = PBXFrameworkReference; name = SystemConfiguration.framework; @@ -676,7 +601,7 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; INSTALL_PATH = /usr/bin; MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = "-no-cpp-precomp -DmDNSResponderVersion=$(MVERS) -DkDNSServiceFlagsRemove=0"; + OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNS; @@ -701,7 +626,6 @@ 6575FC1F022EB78C00000109 = { children = ( 6575FC20022EB7AA00000109, - 0017390704CC75C30CCA2C71, 004EFB9604CC78130CCA2C71, ); isa = PBXGroup; @@ -740,440 +664,408 @@ //652 //653 //654 -//F50 -//F51 -//F52 -//F53 -//F54 - F515E29604A37BB701CA296C = { - fileRef = 654BE64F02B63B93000001D1; - isa = PBXBuildFile; - settings = { - }; +//7F0 +//7F1 +//7F2 +//7F3 +//7F4 + 7F18A9F60587CEF6001880B3 = { + fileEncoding = 30; + isa = PBXFileReference; + name = DNSCommon.c; + path = ../mDNSCore/DNSCommon.c; + refType = 2; }; - F515E29704A37BB801CA296C = { - fileRef = 654BE65002B63B93000001D1; - isa = PBXBuildFile; - settings = { - }; + 7F18A9F70587CEF6001880B3 = { + fileEncoding = 30; + isa = PBXFileReference; + name = uDNS.c; + path = ../mDNSCore/uDNS.c; + refType = 2; }; - F515E29804A37BBB01CA296C = { - fileRef = 654BE65202B63B93000001D1; + 7F18A9F80587CEF6001880B3 = { + fileRef = 7F18A9F60587CEF6001880B3; isa = PBXBuildFile; settings = { }; }; - F515E29904A37BBB01CA296C = { - fileRef = 000753D303367C1C0CCA2C71; + 7F18A9F90587CEF6001880B3 = { + fileRef = 7F18A9F70587CEF6001880B3; isa = PBXBuildFile; settings = { }; }; - F525E72804AA167501F1CF4D = { - fileEncoding = 4; - indentWidth = 4; - isa = PBXFileReference; - path = uds_daemon.c; - refType = 4; - tabWidth = 4; - usesTabs = 0; - }; - F525E72904AA167501F1CF4D = { - fileRef = F525E72804AA167501F1CF4D; + 7F18A9FA0587CEF6001880B3 = { + fileRef = 7F18A9F60587CEF6001880B3; isa = PBXBuildFile; settings = { }; }; - F525E72B04AA167A01F1CF4D = { - fileRef = F525E72804AA167501F1CF4D; + 7F18A9FB0587CEF6001880B3 = { + fileRef = 7F18A9F70587CEF6001880B3; isa = PBXBuildFile; settings = { }; }; - F5E11B5A04A28126019798ED = { - fileEncoding = 4; - isa = PBXFileReference; - path = dnssd_ipc.c; - refType = 4; - }; - F5E11B5B04A28126019798ED = { - fileEncoding = 4; + 7F461DB5062DBF2900672BF3 = { + fileEncoding = 30; isa = PBXFileReference; - path = dnssd_ipc.h; - refType = 4; + name = DNSDigest.c; + path = ../mDNSCore/DNSDigest.c; + refType = 2; }; - F5E11B5C04A28126019798ED = { - fileRef = F5E11B5A04A28126019798ED; + 7F461DB6062DBF2900672BF3 = { + fileRef = 7F461DB5062DBF2900672BF3; isa = PBXBuildFile; settings = { }; }; - F5E11B5D04A28126019798ED = { - fileRef = F5E11B5B04A28126019798ED; + 7F461DB7062DBF2900672BF3 = { + fileRef = 7F461DB5062DBF2900672BF3; isa = PBXBuildFile; settings = { }; }; - F5E11B5E04A28126019798ED = { - fileRef = F5E11B5A04A28126019798ED; + 7F869685066EE02400D2A2DC = { + isa = PBXFrameworkReference; + name = Security.framework; + path = /System/Library/Frameworks/Security.framework; + refType = 0; + }; + 7F869686066EE02400D2A2DC = { + fileRef = 7F869685066EE02400D2A2DC; isa = PBXBuildFile; settings = { }; }; - F5E11B5F04A28126019798ED = { - fileRef = F5E11B5B04A28126019798ED; + 7F869687066EE02400D2A2DC = { + fileRef = 7F869685066EE02400D2A2DC; isa = PBXBuildFile; settings = { }; }; -//F50 -//F51 -//F52 -//F53 -//F54 -//FF0 -//FF1 -//FF2 -//FF3 -//FF4 - FF1B80C507AA1567000768F0 = { - isa = PBXTargetDependency; - target = FF518DC907AA0BA60096AE13; - }; - FF1B80C607AA1569000768F0 = { - isa = PBXTargetDependency; - target = FFEF84D207AA10DF00171646; - }; - FF518DC607AA0BA60096AE13 = { - buildActionMask = 2147483647; - files = ( - FF518DD907AA0C330096AE13, - FF518DDA07AA0C330096AE13, - FF518DDB07AA0C330096AE13, - FF518DDC07AA0C330096AE13, - FF518DDD07AA0C330096AE13, - FF518DDE07AA0C330096AE13, - FF518DDF07AA0C330096AE13, - FF518DE007AA0C330096AE13, - FF518DE207AA0C330096AE13, - FF518DE307AA0C330096AE13, - FF518DE407AA0C330096AE13, - FF518DE507AA0C330096AE13, - ); - isa = PBXSourcesBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - FF518DC707AA0BA60096AE13 = { - buildActionMask = 2147483647; - files = ( - ); - isa = PBXFrameworksBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - FF518DC807AA0BA60096AE13 = { - buildActionMask = 2147483647; - files = ( - ); - isa = PBXRezBuildPhase; - runOnlyForDeploymentPostprocessing = 0; - }; - FF518DC907AA0BA60096AE13 = { - buildPhases = ( - FF518DC607AA0BA60096AE13, - FF518DC707AA0BA60096AE13, - FF518DC807AA0BA60096AE13, - FF72324F07AA0CC20012EE6F, - FF72325007AA0CCA0012EE6F, - ); - buildSettings = { - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - INSTALL_PATH = System/Library/Java/Extensions; - JAVA_ARCHIVE_CLASSES = YES; - JAVA_ARCHIVE_COMPRESSION = YES; - JAVA_ARCHIVE_TYPE = JAR; - JAVA_COMPILER_DEBUGGING_SYMBOLS = NO; - JAVA_COMPILER_TARGET_VM_VERSION = 1.2; - JAVA_SOURCE_SUBDIR = .; - LIBRARY_STYLE = STATIC; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOL_FLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = dns_sd; - REZ_EXECUTABLE = YES; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; - }; - dependencies = ( - ); - isa = PBXLibraryTarget; - name = dns_sd.jar; - productInstallPath = System/Library/Java/Extensions; - productName = dns_sd.jar; - productReference = FF72324E07AA0CB30012EE6F; - }; - FF518DCB07AA0C330096AE13 = { +//7F0 +//7F1 +//7F2 +//7F3 +//7F4 +//DB0 +//DB1 +//DB2 +//DB3 +//DB4 + DB2CC4420662DCE500335AB3 = { children = ( - FF518DCC07AA0C330096AE13, - FF518DCD07AA0C330096AE13, - FF518DCE07AA0C330096AE13, - FF518DCF07AA0C330096AE13, - FF518DD007AA0C330096AE13, - FF518DD107AA0C330096AE13, - FF518DD207AA0C330096AE13, - FF518DD307AA0C330096AE13, - FF518DD407AA0C330096AE13, - FF518DD507AA0C330096AE13, - FF518DD607AA0C330096AE13, - FF518DD707AA0C330096AE13, - FF518DD807AA0C330096AE13, + DB2CC4430662DD1100335AB3, + DB2CC4440662DD1100335AB3, + DB2CC4450662DD1100335AB3, + DB2CC4460662DD1100335AB3, + DB2CC4470662DD1100335AB3, + DB2CC4480662DD1100335AB3, + DB2CC4490662DD1100335AB3, + DB2CC44A0662DD1100335AB3, + DB2CC44B0662DD1100335AB3, + DB2CC44C0662DD1100335AB3, + DB2CC44D0662DD1100335AB3, + DB2CC44E0662DD1100335AB3, + DB2CC44F0662DD1100335AB3, ); isa = PBXGroup; - name = Java; - path = ../mDNSShared/Java; - refType = 2; + name = "Java Support"; + refType = 4; }; - FF518DCC07AA0C330096AE13 = { + DB2CC4430662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = BaseListener.java; path = ../mDNSShared/Java/BaseListener.java; refType = 2; }; - FF518DCD07AA0C330096AE13 = { + DB2CC4440662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = BrowseListener.java; path = ../mDNSShared/Java/BrowseListener.java; refType = 2; }; - FF518DCE07AA0C330096AE13 = { + DB2CC4450662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = DNSRecord.java; path = ../mDNSShared/Java/DNSRecord.java; refType = 2; }; - FF518DCF07AA0C330096AE13 = { + DB2CC4460662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = DNSSD.java; path = ../mDNSShared/Java/DNSSD.java; refType = 2; }; - FF518DD007AA0C330096AE13 = { + DB2CC4470662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = DNSSDException.java; path = ../mDNSShared/Java/DNSSDException.java; refType = 2; }; - FF518DD107AA0C330096AE13 = { + DB2CC4480662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = DNSSDRegistration.java; path = ../mDNSShared/Java/DNSSDRegistration.java; refType = 2; }; - FF518DD207AA0C330096AE13 = { + DB2CC4490662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = DNSSDService.java; path = ../mDNSShared/Java/DNSSDService.java; refType = 2; }; - FF518DD307AA0C330096AE13 = { + DB2CC44A0662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = DomainListener.java; path = ../mDNSShared/Java/DomainListener.java; refType = 2; }; - FF518DD407AA0C330096AE13 = { + DB2CC44B0662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = JNISupport.c; path = ../mDNSShared/Java/JNISupport.c; refType = 2; }; - FF518DD507AA0C330096AE13 = { + DB2CC44C0662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = QueryListener.java; path = ../mDNSShared/Java/QueryListener.java; refType = 2; }; - FF518DD607AA0C330096AE13 = { + DB2CC44D0662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = RegisterListener.java; path = ../mDNSShared/Java/RegisterListener.java; refType = 2; }; - FF518DD707AA0C330096AE13 = { + DB2CC44E0662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = ResolveListener.java; path = ../mDNSShared/Java/ResolveListener.java; refType = 2; }; - FF518DD807AA0C330096AE13 = { + DB2CC44F0662DD1100335AB3 = { fileEncoding = 30; isa = PBXFileReference; name = TXTRecord.java; path = ../mDNSShared/Java/TXTRecord.java; refType = 2; }; - FF518DD907AA0C330096AE13 = { - fileRef = FF518DCC07AA0C330096AE13; + DB2CC4500662DD6800335AB3 = { + buildActionMask = 2147483647; + files = ( + DB2CC4560662DE4500335AB3, + DB2CC4570662DE4600335AB3, + DB2CC4580662DE4700335AB3, + DB2CC4590662DE4700335AB3, + DB2CC45A0662DE4800335AB3, + DB2CC45B0662DE4900335AB3, + DB2CC45C0662DE4900335AB3, + DB2CC45D0662DE4A00335AB3, + DB2CC45E0662DE4B00335AB3, + DB2CC45F0662DE4C00335AB3, + DB2CC4600662DE4C00335AB3, + DB2CC4610662DE4D00335AB3, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4510662DD6800335AB3 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXJavaArchiveBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4520662DD6800335AB3 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4530662DD6800335AB3 = { + buildPhases = ( + DB2CC4500662DD6800335AB3, + DB2CC4510662DD6800335AB3, + DB2CC4520662DD6800335AB3, + DB2CC4550662DE1700335AB3, + FFD41DDD06641B4200F0C438, + ); + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INSTALL_PATH = /System/Library/Java/Extensions; + JAVA_ARCHIVE_CLASSES = YES; + JAVA_ARCHIVE_COMPRESSION = YES; + JAVA_ARCHIVE_TYPE = JAR; + JAVA_COMPILER_DEBUGGING_SYMBOLS = NO; + JAVA_COMPILER_TARGET_VM_VERSION = 1.2; + JAVA_SOURCE_SUBDIR = .; + LIBRARY_STYLE = STATIC; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOL_FLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = dns_sd; + PURE_JAVA = YES; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + comments = "Multiplatform .jar file that implements Java interface to DNS-SD"; + dependencies = ( + ); + isa = PBXLibraryTarget; + name = dns_sd.jar; + productInstallPath = /System/Library/Java/Extensions; + productName = dns_sd.jar; + productReference = FFD41DDA0664157900F0C438; + }; + DB2CC4550662DE1700335AB3 = { + buildActionMask = 12; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "javah -force -classpath ${OBJROOT}/mDNSResponder.build/dns_sd.jar.build/JavaClasses/ -o ${OBJROOT}/mDNSResponder.build/dns_sd.jar.build/DNSSD.java.h com.apple.dnssd.AppleDNSSD com.apple.dnssd.AppleBrowser com.apple.dnssd.AppleResolver com.apple.dnssd.AppleRegistration com.apple.dnssd.AppleQuery com.apple.dnssd.AppleDomainEnum com.apple.dnssd.AppleService"; + }; + DB2CC4560662DE4500335AB3 = { + fileRef = DB2CC4430662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DDA07AA0C330096AE13 = { - fileRef = FF518DCD07AA0C330096AE13; + DB2CC4570662DE4600335AB3 = { + fileRef = DB2CC4440662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DDB07AA0C330096AE13 = { - fileRef = FF518DCE07AA0C330096AE13; + DB2CC4580662DE4700335AB3 = { + fileRef = DB2CC4450662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DDC07AA0C330096AE13 = { - fileRef = FF518DCF07AA0C330096AE13; + DB2CC4590662DE4700335AB3 = { + fileRef = DB2CC4460662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DDD07AA0C330096AE13 = { - fileRef = FF518DD007AA0C330096AE13; + DB2CC45A0662DE4800335AB3 = { + fileRef = DB2CC4470662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DDE07AA0C330096AE13 = { - fileRef = FF518DD107AA0C330096AE13; + DB2CC45B0662DE4900335AB3 = { + fileRef = DB2CC4480662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DDF07AA0C330096AE13 = { - fileRef = FF518DD207AA0C330096AE13; + DB2CC45C0662DE4900335AB3 = { + fileRef = DB2CC4490662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DE007AA0C330096AE13 = { - fileRef = FF518DD307AA0C330096AE13; + DB2CC45D0662DE4A00335AB3 = { + fileRef = DB2CC44A0662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DE207AA0C330096AE13 = { - fileRef = FF518DD507AA0C330096AE13; + DB2CC45E0662DE4B00335AB3 = { + fileRef = DB2CC44C0662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DE307AA0C330096AE13 = { - fileRef = FF518DD607AA0C330096AE13; + DB2CC45F0662DE4C00335AB3 = { + fileRef = DB2CC44D0662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DE407AA0C330096AE13 = { - fileRef = FF518DD707AA0C330096AE13; + DB2CC4600662DE4C00335AB3 = { + fileRef = DB2CC44E0662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF518DE507AA0C330096AE13 = { - fileRef = FF518DD807AA0C330096AE13; + DB2CC4610662DE4D00335AB3 = { + fileRef = DB2CC44F0662DD1100335AB3; isa = PBXBuildFile; settings = { }; }; - FF72324E07AA0CB30012EE6F = { - includeInIndex = 0; - isa = PBXZipArchiveReference; - path = dns_sd.jar; - refType = 3; - }; - FF72324F07AA0CC20012EE6F = { - buildActionMask = 2147483647; - files = ( - ); - generatedFileNames = ( - ); - isa = PBXShellScriptBuildPhase; - neededFileNames = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "javah -force -classpath ${OBJROOT}/mDNSResponder.build/dns_sd.jar.build/JavaClasses/ -o ${OBJROOT}/mDNSResponder.build/dns_sd.jar.build/DNSSD.java.h com.apple.dnssd.AppleDNSSD com.apple.dnssd.AppleBrowser com.apple.dnssd.AppleResolver com.apple.dnssd.AppleRegistration com.apple.dnssd.AppleQuery com.apple.dnssd.AppleDomainEnum com.apple.dnssd.AppleService"; - }; - FF72325007AA0CCA0012EE6F = { - buildActionMask = 2147483647; - files = ( - ); - generatedFileNames = ( - ); - isa = PBXShellScriptBuildPhase; - neededFileNames = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "rm -f ${BUILD_DIR}/dns_sd"; - }; - FFEF84CE07AA10DF00171646 = { + DB2CC4620662DF5C00335AB3 = { buildActionMask = 2147483647; files = ( ); isa = PBXHeadersBuildPhase; runOnlyForDeploymentPostprocessing = 0; }; - FFEF84CF07AA10DF00171646 = { + DB2CC4630662DF5C00335AB3 = { buildActionMask = 2147483647; files = ( - FFEF84D407AA113100171646, + DB2CC46A0662E00700335AB3, ); isa = PBXSourcesBuildPhase; runOnlyForDeploymentPostprocessing = 0; }; - FFEF84D007AA10DF00171646 = { + DB2CC4640662DF5C00335AB3 = { buildActionMask = 2147483647; files = ( - FFEF84D707AA115900171646, + DB2CC4690662DFF500335AB3, ); isa = PBXFrameworksBuildPhase; runOnlyForDeploymentPostprocessing = 0; }; - FFEF84D107AA10DF00171646 = { + DB2CC4650662DF5C00335AB3 = { buildActionMask = 2147483647; files = ( ); isa = PBXRezBuildPhase; runOnlyForDeploymentPostprocessing = 0; }; - FFEF84D207AA10DF00171646 = { + DB2CC4660662DF5C00335AB3 = { buildPhases = ( - FFEF84CE07AA10DF00171646, - FFEF84CF07AA10DF00171646, - FFEF84D007AA10DF00171646, - FFEF84D107AA10DF00171646, + DB2CC4620662DF5C00335AB3, + DB2CC4630662DF5C00335AB3, + DB2CC4640662DF5C00335AB3, + DB2CC4650662DF5C00335AB3, ); buildSettings = { + DEBUGGING_SYMBOLS = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - HEADER_SEARCH_PATHS = "../mDNSShared \"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/A/Headers\" \"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/1.3.1/Headers\" \"$(OBJROOT)/mDNSResponder.build/dns_sd.jar.build\""; + HEADER_SEARCH_PATHS = "\"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/A/Headers\" \"$(SYSTEM_LIBRARY_DIR)/Frameworks/JavaVM.framework/Versions/1.3.1/Headers\" \"$(OBJROOT)/mDNSResponder.build/dns_sd.jar.build\""; INSTALL_PATH = /usr/lib/java; LIBRARY_STYLE = DYNAMIC; OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; OTHER_LIBTOOL_FLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = libjdns_sd.jnilib; @@ -1183,40 +1075,246 @@ }; comments = "Platform-specific JNI library that bridges dns_sd.jar to ."; dependencies = ( - FFEF84D807AA116400171646, + FFD41DDF06641BBB00F0C438, ); isa = PBXLibraryTarget; name = libjdns_sd.jnilib; productInstallPath = /usr/lib/java; productName = libjdns_sd.jnilib; - productReference = FFEF84D307AA10DF00171646; + productReference = DB2CC4670662DF5C00335AB3; }; - FFEF84D307AA10DF00171646 = { + DB2CC4670662DF5C00335AB3 = { isa = PBXLibraryReference; path = libjdns_sd.jnilib; refType = 3; }; - FFEF84D407AA113100171646 = { - fileRef = FF518DD407AA0C330096AE13; - isa = PBXBuildFile; - settings = { - }; - }; - FFEF84D607AA115900171646 = { + DB2CC4680662DFF500335AB3 = { isa = PBXFrameworkReference; name = JavaVM.framework; path = /System/Library/Frameworks/JavaVM.framework; refType = 0; }; - FFEF84D707AA115900171646 = { - fileRef = FFEF84D607AA115900171646; + DB2CC4690662DFF500335AB3 = { + fileRef = DB2CC4680662DFF500335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC46A0662E00700335AB3 = { + fileRef = DB2CC44B0662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DBAAFE29057E8F4D0085CAD0 = { + fileEncoding = 30; + isa = PBXFileReference; + name = mDNSDebug.c; + path = ../mDNSShared/mDNSDebug.c; + refType = 2; + }; + DBAAFE2A057E8F4D0085CAD0 = { + fileRef = DBAAFE29057E8F4D0085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + DBAAFE2B057E8F4D0085CAD0 = { + fileRef = DBAAFE29057E8F4D0085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + DBAAFE2C057E8F660085CAD0 = { + fileEncoding = 30; + isa = PBXFileReference; + name = GenLinkedList.c; + path = ../mDNSShared/GenLinkedList.c; + refType = 2; + }; + DBAAFE2D057E8F660085CAD0 = { + fileRef = DBAAFE2C057E8F660085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + DBAAFE2E057E8F660085CAD0 = { + fileRef = DBAAFE2C057E8F660085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; +//DB0 +//DB1 +//DB2 +//DB3 +//DB4 +//F50 +//F51 +//F52 +//F53 +//F54 + F515E29604A37BB701CA296C = { + fileRef = 654BE64F02B63B93000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + F515E29704A37BB801CA296C = { + fileRef = 654BE65002B63B93000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + F515E29904A37BBB01CA296C = { + fileRef = 000753D303367C1C0CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + F525E72804AA167501F1CF4D = { + fileEncoding = 4; + isa = PBXFileReference; + name = uds_daemon.c; + path = ../mDNSShared/uds_daemon.c; + refType = 2; + }; + F525E72904AA167501F1CF4D = { + fileRef = F525E72804AA167501F1CF4D; + isa = PBXBuildFile; + settings = { + }; + }; + F525E72B04AA167A01F1CF4D = { + fileRef = F525E72804AA167501F1CF4D; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5A04A28126019798ED = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnssd_ipc.c; + path = ../mDNSShared/dnssd_ipc.c; + refType = 2; + }; + F5E11B5B04A28126019798ED = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnssd_ipc.h; + path = ../mDNSShared/dnssd_ipc.h; + refType = 2; + }; + F5E11B5C04A28126019798ED = { + fileRef = F5E11B5A04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5D04A28126019798ED = { + fileRef = F5E11B5B04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5E04A28126019798ED = { + fileRef = F5E11B5A04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5F04A28126019798ED = { + fileRef = F5E11B5B04A28126019798ED; isa = PBXBuildFile; settings = { }; }; - FFEF84D807AA116400171646 = { +//F50 +//F51 +//F52 +//F53 +//F54 +//FF0 +//FF1 +//FF2 +//FF3 +//FF4 + FF0E0B5C065ADC3800FE4D9C = { + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + FF0E0B5E065ADCA400FE4D9C, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FF0E0B5D065ADC7600FE4D9C = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNS.1; + path = ../mDNSShared/mDNS.1; + refType = 2; + }; + FF0E0B5E065ADCA400FE4D9C = { + fileRef = FF0E0B5D065ADC7600FE4D9C; + isa = PBXBuildFile; + settings = { + }; + }; + FF485D5105632E0000130380 = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNSResponder.8; + path = ../mDNSShared/mDNSResponder.8; + refType = 2; + }; + FF5A0AE705632EA600743C27 = { + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + FF5A0AE805632EAE00743C27, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FF5A0AE805632EAE00743C27 = { + fileRef = FF485D5105632E0000130380; + isa = PBXBuildFile; + settings = { + }; + }; + FFD41DDA0664157900F0C438 = { + includeInIndex = 0; + isa = PBXZipArchiveReference; + path = dns_sd.jar; + refType = 3; + }; + FFD41DDB0664169900F0C438 = { + isa = PBXTargetDependency; + target = DB2CC4530662DD6800335AB3; + }; + FFD41DDC0664169B00F0C438 = { + isa = PBXTargetDependency; + target = DB2CC4660662DF5C00335AB3; + }; + FFD41DDD06641B4200F0C438 = { + buildActionMask = 2147483647; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "rm -f ${BUILD_DIR}/dns_sd"; + }; + FFD41DDF06641BBB00F0C438 = { isa = PBXTargetDependency; - target = FF518DC907AA0BA60096AE13; + target = DB2CC4530662DD6800335AB3; }; }; rootObject = 08FB7793FE84155DC02AAC07; diff --git a/mDNSMacOSX/uds_daemon.c b/mDNSMacOSX/uds_daemon.c deleted file mode 100644 index b1d9eba..0000000 --- a/mDNSMacOSX/uds_daemon.c +++ /dev/null @@ -1,2282 +0,0 @@ -/* - * 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.2.4 2005/01/28 05:41:45 cheshire - SUPan: Service advertisement in qmaster can yield zombie ads - -Revision 1.22.2.3 2005/01/28 04:03:24 cheshire - SUPan: Current method of doing subtypes causes name collisions -Summary: Pulled in ConstructServiceName, CountSubTypes and AllocateSubTypes from Tiger version. - -Revision 1.22.2.2 2004/06/18 17:28:19 cheshire - Current method of doing subtypes causes name collisions - -Revision 1.22.2.1 2003/12/05 00:03:35 cheshire - Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256 - -Revision 1.22 2003/08/19 16:03:55 ksekar -Bug #: : ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord -Check termination_context for NULL before dereferencing. - -Revision 1.21 2003/08/19 05:39:43 cheshire - SIGINFO dump should include resolves started by DNSServiceQueryRecord - -Revision 1.20 2003/08/16 03:39:01 cheshire - InterfaceID -1 indicates "local only" - -Revision 1.19 2003/08/15 20:16:03 cheshire - mDNSResponder takes too much RPRVT -We want to avoid touching the rdata pages, so we don't page them in. -1. RDLength was stored with the rdata, which meant touching the page just to find the length. - Moved this from the RData to the ResourceRecord object. -2. To avoid unnecessarily touching the rdata just to compare it, - compute a hash of the rdata and store the hash in the ResourceRecord object. - -Revision 1.18 2003/08/15 00:38:00 ksekar -Bug #: : Bug: buffer overrun when reading long rdata from client - -Revision 1.17 2003/08/14 02:18:21 cheshire - Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord - -Revision 1.16 2003/08/13 23:58:52 ksekar -Bug #: : Bug: UDS Sub-type browsing works, but not sub-type registration -Fixed pointer increment error, moved subtype reading for-loop for easier error bailout. - -Revision 1.15 2003/08/13 17:30:33 ksekar -Bug #: : DNSServiceAddRecord doesn't work -Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords. - -Revision 1.14 2003/08/12 19:56:25 cheshire -Update to APSL 2.0 - - */ - -#include "mDNSClientAPI.h" -#include "mDNSMacOSX.h" -#include "dns_sd.h" -#include "dnssd_ipc.h" -#include -#include -#include -#include -#include - -// Types and Data Structures -// ---------------------------------------------------------------------- - -typedef enum - { - t_uninitialized, - t_morecoming, - t_complete, - t_error, - t_terminated - } transfer_state; - -typedef void (*req_termination_fn)(void *); - - -typedef struct registered_record_entry - { - int key; - AuthRecord *rr; - struct registered_record_entry *next; - } registered_record_entry; - -typedef struct registered_service - { - //struct registered_service *next; - int autoname; - int renameonconflict; - int rename_on_memfree; // set flag on config change when we deregister original name - domainlabel name; - ServiceRecordSet *srs; - struct request_state *request; - AuthRecord *subtypes; - } registered_service; - -typedef struct - { - mStatus err; - int nwritten; - int sd; - } undelivered_error_t; - -typedef struct request_state - { - // connection structures - CFRunLoopSourceRef rls; - CFSocketRef sr; - int sd; - - // 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 mStatus handle_add_request(request_state *rstate); -static mStatus 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 mStatus 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 mStatus 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 && 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) == 0) - { - mStatus err = 0; - int nwritten; - int errfd = socket(AF_LOCAL, SOCK_STREAM, 0); - if (errfd < 0) - { - my_perror("ERROR: socket"); - abort_request(rstate); - unlink_request(rstate); - return; - } - if (fcntl(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(errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) - { - my_perror("ERROR: connect"); - abort_request(rstate); - unlink_request(rstate); - } - - switch (rstate->hdr.op.request_op) - { - case reg_record_request: err = handle_regrecord_request (rstate); break; - case add_record_request: err = handle_add_request (rstate); break; - case update_record_request: err = handle_update_request (rstate); break; - case remove_record_request: err = handle_removerecord_request(rstate); break; - default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op); - } - - nwritten = send(errfd, &err, sizeof(err), 0); - // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a four-byte write for us. - // If not, we don't attempt to handle this failure, but we do log it. - if (nwritten < (int)sizeof(err)) - LogMsg("ERROR: failed to write error response back to caller: %d %d %s", nwritten, errno, strerror(errno)); - close(errfd); - reset_connected_rstate(rstate); // Reset ready to accept the next request on this pipe - } - else - { - 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 reconfirm_record_request: handle_reconfirm_request (rstate); break; - default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.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[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; - 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, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 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_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_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[MAX_ESCAPED_DOMAIN_NAME]; - 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); - } - -// If there's a comma followed by another character, -// FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character. -// Otherwise, it returns a pointer to the final nul at the end of the string -static char *FindFirstSubType(char *p) - { - while (*p) - { - if (p[0] == '\\' && p[1]) p += 2; - else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); } - else p++; - } - return(p); - } - -// If there's a comma followed by another character, -// FindNextSubType overwrites the comma with a nul and returns the pointer to the next character. -// If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL -// Otherwise, it returns a pointer to the final nul at the end of the string -static char *FindNextSubType(char *p) - { - while (*p) - { - if (p[0] == '\\' && p[1]) // If escape character - p += 2; // ignore following character - else if (p[0] == ',') // If we found a comma - { - if (p[1]) *p++ = 0; - return(p); - } - else if (p[0] == '.') - return(mDNSNULL); - else p++; - } - return(p); - } - -// Returns -1 if illegal subtype found -extern mDNSs32 CountSubTypes(char *regtype); -mDNSexport mDNSs32 CountSubTypes(char *regtype) - { - mDNSs32 NumSubTypes = 0; - char *stp = FindFirstSubType(regtype); - while (stp && *stp) // If we found a comma... - { - if (*stp == ',') return(-1); - NumSubTypes++; - stp = FindNextSubType(stp); - } - if (!stp) return(-1); - return(NumSubTypes); - } - -extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p); -mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p) - { - AuthRecord *st = mDNSNULL; - if (NumSubTypes) - { - mDNSs32 i; - st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord)); - if (!st) return(mDNSNULL); - for (i = 0; i < NumSubTypes; i++) - { - while (*p) p++; - p++; - if (!MakeDomainNameFromDNSNameString(&st[i].resrec.name, p)) - { freeL("ServiceSubTypes", st); return(mDNSNULL); } - } - } - return(st); - } - -static void handle_browse_request(request_state *request) - { - DNSServiceFlags flags; - uint32_t interfaceIndex; - char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; - DNSQuestion *q; - domainname typedn, domdn; - mDNSs32 NumSubTypes; - 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, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 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; - - typedn.c[0] = 0; - NumSubTypes = CountSubTypes(regtype); - if (NumSubTypes < 0 || NumSubTypes > 1) goto bad_param; - if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) goto bad_param; - - if (!AppendDNSNameString(&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[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME]; - 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; - mDNSs32 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, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 || - get_string(&ptr, host, MAX_ESCAPED_DOMAIN_NAME) < 0) - goto bad_param; - - port.NotAnInteger = get_short(&ptr); - txtlen = get_short(&ptr); - txtdata = get_rdata(&ptr, txtlen); - - // Check for sub-types after the service type - num_subtypes = CountSubTypes(regtype); - if (num_subtypes < 0) goto bad_param; - - 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; - - r_srv->subtypes = AllocateSubTypes(num_subtypes, regtype); - if (num_subtypes && !r_srv->subtypes) - { freeL("handle_regservice_request", r_srv); r_srv = NULL; goto malloc_error; } - 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 mStatus 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(-1); - } - - 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); - if (result) - { - freeL("handle_add_request", rstate->msgbuf); - rstate->msgbuf = NULL; - freeL("handle_add_request", extra); - return(result); - } - 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; - return(result); - } - -static mStatus 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(-1); - } - 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); - return(result); - } - -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 mStatus 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(-1); - } - - rr = read_rr_from_ipc_msg(rstate->msgdata, 1); - if (!rr) return(mStatus_BadParamErr); - - rcc = mallocL("handle_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("handle_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); - return(result); - -malloc_error: - my_perror("ERROR: malloc"); - return(-1); - } - -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 mStatus 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; - } - return(err); - } - - -// 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("handle_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[MAX_ESCAPED_DOMAIN_NAME]; - 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[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL. - char typestr[MAX_ESCAPED_DOMAIN_NAME]; - char domstr [MAX_ESCAPED_DOMAIN_NAME]; - - *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->sd, &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->sd; - rstate->u_err = undeliv; - return 0; - } - return 0; - -error: - 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; - - // 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 index bf223d1..193272d 100755 --- a/mDNSPosix/Client.c +++ b/mDNSPosix/Client.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSPosix/ExampleClientApp.c b/mDNSPosix/ExampleClientApp.c index a0531bc..8561388 100644 --- a/mDNSPosix/ExampleClientApp.c +++ b/mDNSPosix/ExampleClientApp.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSPosix/ExampleClientApp.h b/mDNSPosix/ExampleClientApp.h index 4274a46..d9b20c5 100644 --- a/mDNSPosix/ExampleClientApp.h +++ b/mDNSPosix/ExampleClientApp.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c index 412f829..ed9fd7d 100644 --- a/mDNSPosix/Identify.c +++ b/mDNSPosix/Identify.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -36,6 +38,42 @@ Change History (most recent first): $Log: Identify.c,v $ +Revision 1.23 2004/05/18 23:51:26 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.22 2004/04/20 22:43:28 cheshire +Use _services._dns-sd._udp query, as documented in + + +Revision 1.21 2004/01/28 21:38:57 cheshire +Also ask target host for _services._mdns._udp.local. list + +Revision 1.20 2004/01/28 19:04:38 cheshire +Fix Ctrl-C handling when multiple targets are specified + +Revision 1.19 2004/01/28 03:49:30 cheshire +Enhanced mDNSIdentify to make use of new targeted-query capability + +Revision 1.18 2004/01/27 19:06:51 cheshire +Remove workaround for WWDC 2003 bug; no one has run that buggy build for a long time + +Revision 1.17 2004/01/22 03:57:00 cheshire +Use the new meta-interface mDNSInterface_ForceMCast. This restores mDNSIdentify's +ability to use multicast queries with non-link-local target addresses, like 17.x.x.x. + +Revision 1.16 2004/01/22 00:03:32 cheshire +Add while() loop so that a list of targets may be specified on the command line + +Revision 1.15 2004/01/21 21:55:06 cheshire +Don't need to wait for timeout once we've got the information we wanted + +Revision 1.14 2003/12/17 00:51:22 cheshire +Changed mDNSNetMonitor and mDNSIdentify to link the object files +instead of #including the "DNSCommon.c" "uDNS.c" and source files + +Revision 1.13 2003/12/13 03:05:28 ksekar +: DynDNS: Unicast query of service records + Revision 1.12 2003/11/14 21:27:09 cheshire : Security: Crashing bug in mDNSResponder Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. @@ -114,6 +152,7 @@ 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[MAX_ESCAPED_DOMAIN_NAME], hardware[256], software[256]; +static mDNSAddr lastsrc, hostaddr, target; static mDNSOpaque16 lastid, id; //************************************************************************************************************* @@ -142,6 +181,11 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS { // Snag copy of header ID, then call through lastid = msg->h.id; + lastsrc = *srcaddr; + + // We *want* to allow off-net unicast responses here. + // For now, the simplest way to allow that is to smash the TTL to 255 so that mDNSCore doesn't reject the packet + ttl = 255; __MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, ttl); } @@ -151,9 +195,12 @@ static void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRec (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); + if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME) + { + 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) @@ -167,6 +214,8 @@ static void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRec NumAnswers++; NumAddr++; mprintf("%##s %s %.4a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ip); + hostaddr.type = mDNSAddrType_IPv4; // Prefer v4 target to v6 target, for now + hostaddr.ip.v4 = answer->rdata->u.ip; } else if (answer->rrtype == kDNSType_AAAA) { @@ -174,6 +223,11 @@ static void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRec NumAnswers++; NumAAAA++; mprintf("%##s %s %.16a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6); + if (!hostaddr.type) // Prefer v4 target to v6 target, for now + { + hostaddr.type = mDNSAddrType_IPv6; + hostaddr.ip.v6 = answer->rdata->u.ipv6; + } } else if (answer->rrtype == kDNSType_HINFO) { @@ -186,6 +240,26 @@ static void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRec NumAnswers++; NumHINFO++; } + + // If we've got everything we're looking for, don't need to wait any more + if (NumHINFO && (NumAddr || NumAAAA)) StopNow = 1; + } + +static void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + (void)m; // Unused + (void)question; // Unused + (void)AddRecord;// Unused + // Right now the mDNSCore targeted-query code is incomplete -- + // it issues targeted queries, but accepts answers from anywhere + // For now, we'll just filter responses here so we don't get confused by responses from someone else + if (answer->rrtype == kDNSType_PTR && mDNSSameAddress(&lastsrc, &target)) + { + NumAnswers++; + NumAddr++; + mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.name.c); + StopNow = 1; + } } mDNSexport void WaitForAnswer(mDNS *const m, int seconds) @@ -215,11 +289,13 @@ mDNSexport void WaitForAnswer(mDNS *const m, int seconds) } } -mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, mDNSQuestionCallback callback) +mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback) { if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname); - - q->InterfaceID = mDNSInterface_Any; + q->Target = target ? *target : zeroAddr; + q->TargetPort = MulticastDNSPort; + q->TargetQID = zeroID; + q->InterfaceID = mDNSInterface_ForceMCast; q->qtype = qtype; q->qclass = kDNSClass_IN; q->QuestionCallback = callback; @@ -229,18 +305,28 @@ mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, mDNSQue return(mDNS_StartQuery(&mDNSStorage, q)); } -mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, mDNSQuestionCallback callback) +mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback) { - mStatus status = StartQuery(q, qname, qtype, callback); + mStatus status = StartQuery(q, qname, qtype, target, 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)); } + } + +mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback) + { + DoOneQuery(q, qname, qtype, target, callback); + if (StopNow == 0 && target && target->type) + { + mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype)); + DoOneQuery(q, qname, qtype, NULL, callback); + } + if (StopNow == 0 && NumAnswers == 0) + mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype)); return(StopNow); } @@ -254,6 +340,7 @@ mDNSlocal void HandleSIG(int signal) mDNSexport int main(int argc, char **argv) { + int this_arg = 1; mStatus status; struct in_addr s4; struct in6_addr s6; @@ -262,6 +349,9 @@ mDNSexport int main(int argc, char **argv) if (argc < 2) goto usage; + // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog + mDNS_DebugMode = mDNStrue; + // Initialise the mDNS core. status = mDNS_Init(&mDNSStorage, &PlatformStorage, gRRCache, RR_CACHE_SIZE, @@ -272,63 +362,80 @@ mDNSexport int main(int argc, char **argv) signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C signal(SIGTERM, HandleSIG); - if (inet_pton(AF_INET, argv[1], &s4) == 1) + while (this_arg < argc) { - 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]); + char *arg = argv[this_arg++]; + if (this_arg > 2) printf("\n"); - // 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 + lastid = id = zeroID; + hostaddr = target = zeroAddr; + hostname[0] = hardware[0] = software[0] = 0; + NumAddr = NumAAAA = NumHINFO = 0; - 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"); + if (inet_pton(AF_INET, arg, &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); + target.type = mDNSAddrType_IPv4; + target.ip.v4.NotAnInteger = s4.s_addr; + DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); + if (StopNow == 2) break; + } + else if (inet_pton(AF_INET6, arg, &s6) == 1) + { + 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."); + target.type = mDNSAddrType_IPv6; + bcopy(&s6, &target.ip.v6, sizeof(target.ip.v6)); + DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); + if (StopNow == 2) break; + } + else + strcpy(hostname, arg); + + // Now we have the host name; get its A, AAAA, and HINFO + if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback); + if (StopNow == 2) break; + + if (hardware[0] || software[0]) + { + DNSQuestion q1, q2; + printf("HINFO Hardware: %s\n", hardware); + printf("HINFO Software: %s\n", software); + // We need to make sure the services query is targeted + if (target.type == 0) target = hostaddr; + StartQuery(&q1, "_services._mdns._udp.local.", kDNSQType_ANY, &target, ServicesCallback); + StartQuery(&q2, "_services._dns-sd._udp.local.", kDNSQType_ANY, &target, ServicesCallback); + WaitForAnswer(&mDNSStorage, 4); + mDNS_StopQuery(&mDNSStorage, &q1); + mDNS_StopQuery(&mDNSStorage, &q2); + if (StopNow == 2) break; + } + 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"); } - else - printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n"); -exit: mDNS_Close(&mDNSStorage); return(0); usage: - fprintf(stderr, "%s or or \n", argv[0]); + fprintf(stderr, "%s or or ...\n", argv[0]); return(-1); } diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile index 1801a93..9d28ad5 100755 --- a/mDNSPosix/Makefile +++ b/mDNSPosix/Makefile @@ -1,4 +1,92 @@ +# 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@ +# # $Log: Makefile,v $ +# Revision 1.34 2004/05/25 18:29:33 cheshire +# Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c, +# so that it's also accessible to dnssd_clientshim.c (single address space) clients. +# +# Revision 1.33 2004/04/30 16:46:32 rpantos +# Add support for building Java libraries. +# +# Revision 1.32 2004/04/14 23:09:29 ksekar +# Support for TSIG signed dynamic updates. +# +# Revision 1.31 2004/03/15 19:07:06 cheshire +# Fix error message +# +# Revision 1.30 2004/03/11 18:58:29 rpantos +# Fix Kill /etc/rc scripts so they run at halt & reboot. +# +# Revision 1.29 2004/03/04 23:35:41 cheshire +# Instead of using a dummy target to generate an error message, use "$(error text...)" +# +# Revision 1.28 2004/03/04 23:33:42 cheshire +# Fixes from Alfred Perlstein for FreeBSD's benefit +# +# Revision 1.27 2004/02/11 21:00:21 cheshire +# Update URL for GNU Make manual page +# +# Revision 1.26 2004/02/05 21:28:30 cheshire +# Fixes so that "sudo make install" works on *BSD +# +# Revision 1.25 2004/02/05 20:00:22 cheshire +# Define mdnsd's PID file to be /var/run/mdnsd.pid on Posix builds +# +# Revision 1.24 2004/02/05 01:00:01 rpantos +# Fix some issues that turned up when building for FreeBSD. +# +# Revision 1.23 2004/02/04 01:50:54 cheshire +# Make InstalledStartup conditional, so it automatically installs into +# either /etc/init.d/ or /etc/rc.d/init.d/ as appropriate +# +# Revision 1.22 2004/01/20 01:41:21 rpantos +# Define USES_NETLINK for Linux builds. +# +# Revision 1.21 2003/12/17 00:51:22 cheshire +# Changed mDNSNetMonitor and mDNSIdentify to link the object files +# instead of #including the "DNSCommon.c" "uDNS.c" and source files +# +# Revision 1.20 2003/12/13 03:05:28 ksekar +# Bug #: : DynDNS: Unicast query of service records +# +# Revision 1.19 2003/12/11 19:42:13 cheshire +# Change name "mDNSResponderd" to "mdnsd" for consistency with standard Linux (Unix) naming conventions +# +# Revision 1.18 2003/12/11 19:38:34 cheshire +# Add APSL +# +# Revision 1.17 2003/12/11 03:16:49 rpantos +# One more change for OS X build: make install work a little better. +# +# Revision 1.16 2003/12/11 03:03:51 rpantos +# Clean up mDNSPosix so that it builds on OS X again. +# +# Revision 1.15 2003/12/08 20:47:02 rpantos +# Add support for mDNSResponder on Linux. +# +# Revision 1.14 2003/11/14 20:59:09 cheshire +# Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +# Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. +# # Revision 1.13 2003/08/06 18:20:51 cheshire # Makefile cleanup # @@ -26,33 +114,95 @@ # Added NetMonitor.c # -# I assume that cc will be in your path. If not, you have to change the following to point to it. +# This Makefile builds an mDNSResponder daemon and a libmdns.so shared library +# for Linux. It also builds several example programs for embedded systems. +# +# Make with no arguments to build all production targets. +# 'make DEBUG=1' to build debugging targets. +# 'make clean' or 'make clean DEBUG=1' to delete prod/debug objects & targets +# 'sudo make install [DEBUG=1]' to install mdnsd daemon and libmdns. +# +# Notes: +# $@ 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 variables, see +# + +############################################################################# + +LIBVERS = 1 + +COREDIR = ../mDNSCore +SHAREDDIR = ../mDNSShared +JDK = /usr/jdk + CC = cc -CFLAGS_COMMON = -g -I../mDNSCore -I. -DMDNS_DEBUGMSGS=2 +LD = ld +CP = cp +RM = rm +LN = ln -s -f +CFLAGS_COMMON = -I. -I$(COREDIR) -I$(SHAREDDIR) -W -Wall -DPID_FILE=\"/var/run/mdnsd.pid\" +LDFLAGS = -shared +LDSUFFIX = so +JAVACFLAGS_OS = -fPIC -shared -lmdns +# Set up diverging paths for debug vs. prod builds +DEBUG=0 +ifeq ($(DEBUG),1) +CFLAGS_DEBUG = -g -DMDNS_DEBUGMSGS=2 +OBJDIR = objects/debug +BUILDDIR = build/debug +STRIP = echo +else +CFLAGS_DEBUG = -Os -DMDNS_DEBUGMSGS=0 +OBJDIR = objects/prod +BUILDDIR = build/prod +STRIP = strip -S +endif + +# Configure per-OS peculiarities ifeq ($(os),solaris) -CFLAGS_OS = -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME -lsocket -lnsl +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 +CFLAGS_OS = -DNOT_HAVE_SA_LEN -DUSES_NETLINK +JAVACFLAGS_OS += -I$(JDK)/include/linux else ifeq ($(os),netbsd) CFLAGS_OS = +LDCONFIG = ldconfig else ifeq ($(os),freebsd) +# If not already defined, set LOCALBASE to /usr/local +# FreeBSD requires the startup script to end in ".sh" +LOCALBASE?=/usr/local +INSTBASE=$(LOCALBASE) +STARTUPSCRIPTNAME=mdns.sh CFLAGS_OS = +LDCONFIG = ldconfig else ifeq ($(os),openbsd) CFLAGS_OS = -DHAVE_BROKEN_RECVDSTADDR +LDCONFIG = ldconfig else ifeq ($(os),jaguar) -CFLAGS_OS = -DHAVE_IPV6 -W -Wall -no-cpp-precomp -DNOT_HAVE_SOCKLEN_T +CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -DNOT_HAVE_SOCKLEN_T +LD = libtool +LDFLAGS = -dynamic -lSystem +LDSUFFIX = dylib +JDK = /System/Library/Frameworks/JavaVM.framework/Home +JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM else ifeq ($(os),panther) -CFLAGS_OS = -DHAVE_IPV6 -W -Wall -no-cpp-precomp +CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp +LD = libtool +LDFLAGS = -dynamic -lSystem +LDSUFFIX = dylib +JDK = /System/Library/Frameworks/JavaVM.framework/Home +JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM else -cantbuild: - @echo "Error: Must specify target OS on command-line, e.g. \"make os=panther\" or \"make os=jaguar\" or \"make os=linux\"" +$(error ERROR: Must specify target OS on command-line: "make os={jaguar,panther,linux,netbsd,freebsd,openbsd,solaris} [target]") endif endif endif @@ -60,61 +210,219 @@ endif endif endif endif -CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) -COMMONOBJ = objects/mDNSPosix.c.o objects/mDNSUNP.c.o objects/ExampleClientApp.c.o +# If not otherwise defined, we install into /usr/lib and /usr/include +# and our startup script is called mdns (e.g. /etc/init.d/mdns) +INSTBASE?=/usr +STARTUPSCRIPTNAME?=mdns + +ifeq ($(HAVE_IPV6),1) +CFLAGS_OS += -DHAVE_IPV6=1 +else +ifeq ($(HAVE_IPV6),0) +CFLAGS_OS += -DHAVE_IPV6=0 +endif +endif -HEADERS = Makefile mDNSUNP.h mDNSPosix.h \ -../mDNSCore/mDNSDebug.h \ -../mDNSCore/mDNSClientAPI.h \ -../mDNSCore/mDNSPlatformFunctions.h +# If directory /etc/rc.d/init.d/ exists, then we install into that (old Linux) +ifeq ($(wildcard /etc/rc.d/init.d/), /etc/rc.d/init.d/) +STARTUPSCRIPTDIR = /etc/rc.d/init.d +RUNLEVELSCRIPTSDIR = /etc/rc.d +else +# else if directory /etc/init.d/ exists, then we install into that (new Linux) +ifeq ($(wildcard /etc/init.d/), /etc/init.d/) +STARTUPSCRIPTDIR = /etc/init.d +RUNLEVELSCRIPTSDIR = /etc +else +# else install into /etc/rc.d/ (*BSD) +STARTUPSCRIPTDIR = $(INSTBASE)/etc/rc.d +endif +endif -all: setup Client Responder ProxyResponder Identify NetMonitor +CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) $(CFLAGS_DEBUG) +############################################################################# + +all: setup Daemon libmdns Client Responder ProxyResponder Identify NetMonitor + +install: setup InstalledDaemon InstalledLib InstalledStartup + +# 'setup' sets up the build directory structure the way we want setup: - if test ! -d objects ; then mkdir objects ; fi - if test ! -d build ; then mkdir build ; fi + @if test ! -d objects ; then mkdir objects ; fi + @if test ! -d build ; then mkdir build ; fi + @if test ! -d $(OBJDIR) ; then mkdir $(OBJDIR) ; fi + @if test ! -d $(BUILDDIR) ; then mkdir $(BUILDDIR) ; fi + +# clean removes targets and objects +clean: + if test -d $(OBJDIR) ; then rm -r $(OBJDIR) ; fi + if test -d $(BUILDDIR) ; then rm -r $(BUILDDIR) ; fi + +############################################################################# + +# daemon target builds the daemon +DAEMONOBJS = $(OBJDIR)/PosixDaemon.c.o $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNS.c.o \ + $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/uds_daemon.c.o \ + $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o + +Daemon: setup $(BUILDDIR)/mdnsd + @echo "Responder daemon done" + +$(BUILDDIR)/mdnsd: $(DAEMONOBJS) + $(CC) -o $@ $+ + $(STRIP) $@ + +# libmdns target builds the client library +libmdns: setup $(BUILDDIR)/libmdns.$(LDSUFFIX) + @echo "Client library done" + +$(BUILDDIR)/libmdns.$(LDSUFFIX): $(OBJDIR)/dnssd_clientlib.c.so.o $(OBJDIR)/dnssd_clientstub.c.so.o $(OBJDIR)/dnssd_ipc.c.so.o + $(LD) $(LDFLAGS) -o $@ $+ + $(STRIP) $@ + +############################################################################# + +# The Install targets place built stuff in their proper places +InstalledDaemon: $(INSTBASE)/sbin/mdnsd + @echo $< " installed" + +InstalledLib: $(INSTBASE)/lib/libmdns.$(LDSUFFIX).$(LIBVERS) $(INSTBASE)/include/dns_sd.h + @echo $< " installed" + +InstalledStartup: $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) + @echo $< " installed" + +$(INSTBASE)/sbin/mdnsd: $(BUILDDIR)/mdnsd + $(CP) $< $@ + +$(INSTBASE)/lib/libmdns.$(LDSUFFIX).$(LIBVERS): $(BUILDDIR)/libmdns.$(LDSUFFIX) + $(CP) $< $@ + $(LN) $@ $(INSTBASE)/lib/libmdns.$(LDSUFFIX) +ifdef LDCONFIG + # -m means 'merge into existing database', -R means 'rescan directories' + $(LDCONFIG) -mR +endif + +$(INSTBASE)/include/dns_sd.h: $(SHAREDDIR)/dns_sd.h + $(CP) $< $@ + +$(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME): mdnsd.sh $(STARTUPSCRIPTDIR) + $(CP) $< $@ + chmod ugo+x $@ +ifdef RUNLEVELSCRIPTSDIR + $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc2.d/S52mdns + $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc3.d/S52mdns + $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc4.d/S52mdns + $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc5.d/S52mdns + $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc0.d/K16mdns + $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc6.d/K16mdns +endif -Client: setup build/mDNSClientPosix - @echo "Client done" +############################################################################# -Responder: setup build/mDNSResponderPosix - @echo "Responder done" +# The following targets build Java wrappers for the dns-sd.h API. -ProxyResponder: setup build/mDNSProxyResponderPosix - @echo "ProxyResponder done" +JAVAC = $(JDK)/bin/javac +JAVAH = $(JDK)/bin/javah +JAVADOC = $(JDK)/bin/javadoc +JAR = $(JDK)/bin/jar +JAVACFLAGS = $(CFLAGS) $(JAVACFLAGS_OS) -I$(JDK)/include -Identify: setup build/mDNSIdentify +Java: setup $(BUILDDIR)/dns_sd.jar $(BUILDDIR)/libjdns_sd.$(LDSUFFIX) + @echo "Java wrappers done" + +JAVASRC = $(SHAREDDIR)/Java +JARCONTENTS = $(OBJDIR)/com/apple/dnssd/DNSSDService.class \ + $(OBJDIR)/com/apple/dnssd/DNSRecord.class \ + $(OBJDIR)/com/apple/dnssd/DNSSDException.class \ + $(OBJDIR)/com/apple/dnssd/TXTRecord.class \ + $(OBJDIR)/com/apple/dnssd/DNSSDRegistration.class \ + $(OBJDIR)/com/apple/dnssd/BaseListener.class \ + $(OBJDIR)/com/apple/dnssd/BrowseListener.class \ + $(OBJDIR)/com/apple/dnssd/ResolveListener.class \ + $(OBJDIR)/com/apple/dnssd/RegisterListener.class \ + $(OBJDIR)/com/apple/dnssd/QueryListener.class \ + $(OBJDIR)/com/apple/dnssd/DomainListener.class \ + $(OBJDIR)/com/apple/dnssd/DNSSD.class + +$(BUILDDIR)/dns_sd.jar: $(JARCONTENTS) + $(JAR) -cf $@ -C $(OBJDIR) com + +$(BUILDDIR)/libjdns_sd.$(LDSUFFIX): $(JAVASRC)/JNISupport.c $(OBJDIR)/DNSSD.java.h + $(CC) -o $@ $< $(JAVACFLAGS) -I$(OBJDIR) + +$(OBJDIR)/com/apple/dnssd/%.class: $(JAVASRC)/%.java + $(JAVAC) -d $(OBJDIR) -classpath $(OBJDIR) $< + +$(OBJDIR)/DNSSD.java.h: $(OBJDIR)/com/apple/dnssd/DNSSD.class + $(JAVAH) -force -classpath $(OBJDIR) -o $@ \ + com.apple.dnssd.AppleDNSSD \ + com.apple.dnssd.AppleBrowser \ + com.apple.dnssd.AppleResolver \ + com.apple.dnssd.AppleRegistration \ + com.apple.dnssd.AppleQuery \ + com.apple.dnssd.AppleDomainEnum \ + com.apple.dnssd.AppleService + +############################################################################# + +# The following target builds documentation for the Java wrappers. + +JavaDoc: setup Java + $(JAVADOC) $(JAVASRC)/*.java -classpath $(OBJDIR) -d $(BUILDDIR) -public + +############################################################################# + +# The following targets build embedded example programs +SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o +COMMONOBJ = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o +APPOBJ = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o + +Client: setup $(BUILDDIR)/mDNSClientPosix + @echo "Embedded Standalone Client done" + +Responder: setup $(BUILDDIR)/mDNSResponderPosix + @echo "Embedded Standalone Responder done" + +ProxyResponder: setup $(BUILDDIR)/mDNSProxyResponderPosix + @echo "Embedded Standalone ProxyResponder done" + +Identify: setup $(BUILDDIR)/mDNSIdentify @echo "Identify done" -NetMonitor: setup build/mDNSNetMonitor +NetMonitor: setup $(BUILDDIR)/mDNSNetMonitor @echo "NetMonitor done" -# $@ means "The file name of the target of the rule" -# $< means "The name of the first prerequisite" -# $+ means "The names of all the prerequisites, with spaces between them, exactly as given" -# For more magic automatic sariables, see -# -build/mDNSClientPosix: $(COMMONOBJ) objects/mDNS.c.o objects/Client.c.o +$(BUILDDIR)/mDNSClientPosix: $(APPOBJ) $(OBJDIR)/Client.c.o $(CC) $+ -o $@ -build/mDNSResponderPosix: $(COMMONOBJ) objects/mDNS.c.o objects/Responder.c.o +$(BUILDDIR)/mDNSResponderPosix: $(COMMONOBJ) $(OBJDIR)/Responder.c.o $(CC) $+ -o $@ -build/mDNSProxyResponderPosix: $(COMMONOBJ) objects/mDNS.c.o objects/ProxyResponder.c.o +$(BUILDDIR)/mDNSProxyResponderPosix: $(COMMONOBJ) $(OBJDIR)/ProxyResponder.c.o $(CC) $+ -o $@ -build/mDNSIdentify: $(COMMONOBJ) objects/Identify.c.o +$(BUILDDIR)/mDNSIdentify: $(SPECIALOBJ) $(OBJDIR)/Identify.c.o $(CC) $+ -o $@ -build/mDNSNetMonitor: $(COMMONOBJ) objects/NetMonitor.c.o +$(BUILDDIR)/mDNSNetMonitor: $(SPECIALOBJ) $(OBJDIR)/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 $@ +# Implicit rules +$(OBJDIR)/%.c.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< -clean: - -rm -rf objects build .gdb_history +$(OBJDIR)/%.c.o: $(COREDIR)/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR)/%.c.o: $(SHAREDDIR)/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR)/%.c.so.o: %.c + $(CC) $(CFLAGS) -c -fPIC -o $@ $< + +$(OBJDIR)/%.c.so.o: $(SHAREDDIR)/%.c + $(CC) $(CFLAGS) -c -fPIC -o $@ $< diff --git a/mDNSPosix/NetMonitor.c b/mDNSPosix/NetMonitor.c index 8a90d24..8bce09e 100644 --- a/mDNSPosix/NetMonitor.c +++ b/mDNSPosix/NetMonitor.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -36,6 +38,50 @@ Change History (most recent first): $Log: NetMonitor.c,v $ +Revision 1.63 2004/05/18 23:51:26 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.62 2004/03/16 18:24:25 cheshire +If packet destination was not multicast, then display it + +Revision 1.61 2004/02/20 09:36:46 cheshire +Also show TTL in packet header, if it's not 255 + +Revision 1.60 2004/02/07 02:11:35 cheshire +Make mDNSNetMonitor smarter about sending targeted unicast HINFO queries + +Revision 1.59 2004/02/03 21:42:55 cheshire +Add constants kReportTopServices and kReportTopHosts + +Revision 1.58 2004/01/27 20:15:23 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.57 2004/01/24 23:59:42 cheshire +Change to use mDNSVal16() instead of shifting and ORing + +Revision 1.56 2004/01/24 05:25:34 cheshire +mDNSNetMonitor now uses the new ability to send unicast queries so that +it causes less perturbation of the network traffic it's monitoring. + +Revision 1.55 2003/12/23 00:21:31 cheshire +Send HINFO queries to determine the mDNSResponder version of each host + +Revision 1.54 2003/12/17 01:06:39 cheshire +Also show host name along with HINFO data + +Revision 1.53 2003/12/17 00:51:22 cheshire +Changed mDNSNetMonitor and mDNSIdentify to link the object files +instead of #including the "DNSCommon.c" "uDNS.c" and source files + +Revision 1.52 2003/12/17 00:21:51 cheshire +If we received one, display host's HINFO record in final summary + +Revision 1.51 2003/12/13 03:05:28 ksekar +: DynDNS: Unicast query of service records + +Revision 1.50 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + Revision 1.49 2003/10/30 19:38:56 cheshire Fix warning on certain compilers @@ -213,6 +259,7 @@ Added NetMonitor.c #include // For malloc() #include // For bcopy() #include // For "struct tm" etc. +#include // For SIGINT, SIGTERM #include // For gethostbyname() #include // For AF_INET, AF_INET6, etc. #include // For inet_addr() @@ -267,6 +314,12 @@ struct FilterList_struct mDNSAddr FilterAddr; }; +//************************************************************************************************************* +// Constants + +#define kReportTopServices 15 +#define kReportTopHosts 15 + //************************************************************************************************************* // Globals @@ -322,6 +375,12 @@ typedef struct unsigned long pkts[HostPkt_NumTypes]; unsigned long totalops; unsigned long stat[OP_NumTypes]; + domainname hostname; + domainname revname; + UTF8str255 HIHardware; + UTF8str255 HISoftware; + mDNSu32 NumQueries; + mDNSs32 LastQuery; } HostEntry; #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B]) @@ -350,8 +409,9 @@ mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList* list) return NULL; } -mDNSlocal HostEntry *AddHost(HostList* list) +mDNSlocal HostEntry *AddHost(const mDNSAddr *addr, HostList* list) { + int i; HostEntry *entry; if (list->num >= list->max) { @@ -362,29 +422,130 @@ mDNSlocal HostEntry *AddHost(HostList* list) list->max = newMax; list->hosts = newHosts; } + entry = list->hosts + list->num++; + + entry->addr = *addr; + for (i=0; ipkts[i] = 0; + entry->totalops = 0; + for (i=0; istat[i] = 0; + entry->hostname.c[0] = 0; + entry->revname.c[0] = 0; + entry->HIHardware.c[0] = 0; + entry->HISoftware.c[0] = 0; + entry->NumQueries = 0; + + if (entry->addr.type == mDNSAddrType_IPv4) + { + mDNSv4Addr ip = entry->addr.ip.v4; + char buffer[32]; + 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(&entry->revname, buffer); + } + return(entry); } -mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t) +mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t, mDNSOpaque16 id) { if (ExactlyOneFilter) return(NULL); else { HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList; HostEntry *entry = FindHost(addr, list); - if (!entry) + if (!entry) entry = AddHost(addr, list); + if (!entry) return(NULL); + // Don't count our own interrogation packets + if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++; + return(entry); + } + } + +mDNSlocal void RecordHostInfo(HostEntry *entry, const ResourceRecord *const pktrr) + { + if (!entry->hostname.c[0]) + { + if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA) { - int i; - entry = AddHost(list); - if (!entry) return(NULL); - entry->addr = *addr; - for (i=0; ipkts[i] = 0; - entry->totalops = 0; - for (i=0; istat[i] = 0; + // Should really check that the rdata in the address record matches the source address of this packet + entry->NumQueries = 0; + AssignDomainName(entry->hostname, pktrr->name); } - entry->pkts[t]++; - return(entry); + + if (pktrr->rrtype == kDNSType_PTR) + if (SameDomainName(&entry->revname, &pktrr->name)) + { + entry->NumQueries = 0; + AssignDomainName(entry->hostname, pktrr->rdata->u.name); + } + } + else if (pktrr->rrtype == kDNSType_HINFO) + { + RDataBody *rd = &pktrr->rdata->u; + mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; + mDNSu8 *hw = rd->txt.c; + mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0]; + if (sw + 1 + sw[0] <= rdend) + { + AssignDomainName(entry->hostname, pktrr->name); + mDNSPlatformMemCopy(hw, entry->HIHardware.c, 1 + (mDNSu32)hw[0]); + mDNSPlatformMemCopy(sw, entry->HISoftware.c, 1 + (mDNSu32)sw[0]); + } + } + } + +mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID) + { + const mDNSOpaque16 id = { { 0xFF, 0xFF } }; + DNSMessage query; + mDNSu8 *qptr = query.data; + const mDNSu8 *const limit = query.data + sizeof(query.data); + const mDNSAddr *target = &entry->addr; + InitializeDNSMessage(&query.h, id, QueryFlags); + qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN); + entry->LastQuery = m->timenow; + entry->NumQueries++; + + // Note: When there are multiple mDNSResponder agents running on a single machine + // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder) + // it is possible that unicast queries may not go to the primary system responder. + // We try the first query using unicast, but if that doesn't work we try again via multicast. + if (entry->NumQueries > 2) + { + target = &AllDNSLinkGroup_v4; + } + else + { + //mprintf("%#a Q\n", target); + InterfaceID = mDNSInterface_Any; // Send query from our unicast reply socket + m->ExpectUnicastResponse = m->timenow; + } + + mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, target, MulticastDNSPort); + } + +mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID) + { + // If we've done four queries without answer, give up + if (entry->NumQueries >= 4) return; + + // If we've done a query in the last second, give the host a chance to reply before trying again + if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return; + + // If we don't know the host name, try to find that first + if (!entry->hostname.c[0]) + { + if (entry->revname.c[0]) + { + SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID); + //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries); + } + } + // If we have the host name but no HINFO, now ask for that + else if (!entry->HIHardware.c[0]) + { + SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID); + //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries); } } @@ -410,6 +571,10 @@ mDNSlocal void ShowSortedHostList(HostList *list, int max) 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"); + if (!e->HISoftware.c[0] && e->NumQueries > 2) + mDNSPlatformMemCopy("\x0E*** Jaguar ***", &e->HISoftware, 15); + if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0]) + mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c); } } @@ -477,6 +642,7 @@ mDNSlocal void recordstat(HostEntry *entry, domainname *fqdn, int op, mDNSu16 rr mDNSlocal void printstats(int max) { int i; + if (!stats) return; for (i=0; ih.numAuthorities; i++) @@ -514,7 +681,7 @@ mDNSlocal void DisplayTimestamp(void) 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) +mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSu8 ttl) { const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " : (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-"; @@ -523,7 +690,11 @@ mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *co 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.id.NotAnInteger) mprintf(" ID:%u", mDNSVal16(msg->h.id)); + + if (ttl != 255) mprintf(" TTL:%u", ttl); + + if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf(" To: %#a", dstaddr); if (msg->h.flags.b[0] & kDNSFlag0_TC) { @@ -574,7 +745,7 @@ mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char * 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; + case kDNSType_SRV: n += mprintf("%##s:%d", &rd->srv.target, mDNSVal16(rd->srv.port)); break; default: { mDNSu8 *s = rd->data; while (s < rdend && p < buffer+MaxWidth) @@ -620,17 +791,21 @@ mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mD 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) +mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID, mDNSu8 ttl) { 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); + HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id); LargeCacheRecord pkt; - DisplayPacketHeader(msg, end, srcaddr, srcport); - if (MQ) NumPktQ++; else NumPktL++; + DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr, ttl); + if (msg->h.id.NotAnInteger != 0xFFFF) + { + if (MQ) NumPktQ++; else NumPktL++; + } for (i=0; ih.numQuestions; i++) { @@ -656,7 +831,7 @@ mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mD 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); + if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype); } } @@ -680,17 +855,20 @@ mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mD ptr = skipResourceRecord(msg, ptr, end); if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } } + + if (entry) AnalyseHost(m, entry, InterfaceID); } -mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID) +mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID, mDNSu8 ttl) { int i; const mDNSu8 *ptr = msg->data; - HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R); + HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); LargeCacheRecord pkt; - DisplayPacketHeader(msg, end, srcaddr, srcport); - NumPktR++; + DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr, ttl); + if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++; for (i=0; ih.numQuestions; i++) { @@ -698,7 +876,10 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const 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); + if (mDNSAddrIsDNSMulticast(dstaddr)) + mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c); + else + mprintf("%#-16a (Q) %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c); } for (i=0; ih.numAnswers; i++) @@ -710,7 +891,8 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const { 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); + if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype); + if (entry) RecordHostInfo(entry, &pkt.r.resrec); } else { @@ -736,6 +918,24 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } NumAdditionals++; DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec); + if (entry) RecordHostInfo(entry, &pkt.r.resrec); + } + + if (entry) AnalyseHost(m, entry, InterfaceID); + } + +mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID) + { + int i; + const mDNSu8 *ptr = LocateAnswers(msg, end); + HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); + //mprintf("%#a R\n", srcaddr); + + for (i=0; ih.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++) + { + LargeCacheRecord pkt; + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + if (pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec); } } @@ -763,7 +963,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS 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", @@ -779,14 +979,24 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS // 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); + mDNS_Lock(m); + if (!mDNSAddrIsDNSMulticast(dstaddr)) + { + if (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr); + else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr, 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++; + if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID, ttl); + else if (QR_OP == StdR) DisplayResponse (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID, ttl); + else + { + debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]); + GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id); + NumPktB++; + } } + mDNS_Unlock(m); } } @@ -794,6 +1004,7 @@ mDNSlocal mStatus mDNSNetMonitor(void) { struct tm tm; int h, m, s, mul, div, TotPkt; + sigset_t signals; mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, @@ -802,7 +1013,16 @@ mDNSlocal mStatus mDNSNetMonitor(void) if (status) return(status); gettimeofday(&tv_start, NULL); - ExampleClientEventLoop(&mDNSStorage); // Wait for user to hit Ctrl-C + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + + do + { + struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM + mDNSBool gotSomething; + mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething); + } + while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM))); // Now display final summary TotPkt = NumPktQ + NumPktL + NumPktR; @@ -847,12 +1067,12 @@ mDNSlocal mStatus mDNSNetMonitor(void) 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); + printstats(kReportTopServices); if (!ExactlyOneFilter) { - ShowSortedHostList(&IPv4HostList, 15); - ShowSortedHostList(&IPv6HostList, 15); + ShowSortedHostList(&IPv4HostList, kReportTopHosts); + ShowSortedHostList(&IPv6HostList, kReportTopHosts); } mDNS_Close(&mDNSStorage); @@ -868,9 +1088,9 @@ mDNSexport int main(int argc, char **argv) for (i=1; i Runtime check to disable prod mdnsd on OS X. + +Revision 1.8 2004/04/07 01:19:04 cheshire +Hash slot value should be unsigned + +Revision 1.7 2004/02/14 06:34:57 cheshire +Use LogMsg instead of fprintf( stderr + +Revision 1.6 2004/02/14 01:10:42 rpantos +Allow daemon to run if 'nobody' is not defined, with a warning. (For Roku HD1000.) + +Revision 1.5 2004/02/05 07:45:43 cheshire +Add Log header + +Revision 1.4 2004/01/28 21:14:23 cheshire +Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode) + +Revision 1.3 2004/01/19 19:51:46 cheshire +Fix compiler error (mixed declarations and code) on some versions of Linux + +Revision 1.2 2003/12/11 03:03:51 rpantos +Clean up mDNSPosix so that it builds on OS X again. + +Revision 1.1 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mDNSClientAPI.h" +#include "mDNSDebug.h" +#include "mDNSPosix.h" +#include "uds_daemon.h" + + +static void ParseCmdLinArgs( int argc, char **argv); +static void DumpStateLog( mDNS *m); +static mStatus MainLoop( mDNS *m); + + +#define RR_CACHE_SIZE 500 +static CacheRecord gRRCache[RR_CACHE_SIZE]; + +extern const char mDNSResponderVersionString[]; + +int main( int argc, char **argv) +{ + mDNS mDNSRecord; + mDNS_PlatformSupport platformStorage; + mStatus err; + + bzero( &mDNSRecord, sizeof mDNSRecord); + bzero( &platformStorage, sizeof platformStorage); + + ParseCmdLinArgs( argc, argv); + + err = mDNS_Init( &mDNSRecord, &platformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, + mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + + if ( mStatus_NoError == err) + err = udsserver_init( &mDNSRecord); + + // Now that we're finished with anything privileged, switch over to running as "nobody" + if ( mStatus_NoError == err) + { + const struct passwd *pw = getpwnam("nobody"); + if ( pw != NULL) + setuid( pw->pw_uid); + else + LogMsg("WARNING: mdnsd continuing as root because user \"nobody\" does not exist"); + } + + if ( mStatus_NoError == err) + err = MainLoop( &mDNSRecord); + + mDNS_Close( &mDNSRecord); + + if (udsserver_exit() < 0) + LogMsg("ExitCallback: udsserver_exit failed"); + + #if MDNS_DEBUGMSGS > 0 + printf( "mDNSResponder exiting normally with %ld\n", err); + #endif + + return err; +} + + +static void ParseCmdLinArgs( int argc, char **argv) +// Do appropriate things at startup with command line arguments. Calls exit() if unhappy. +{ + if ( argc > 1) + { + if ( 0 == strcmp( argv[1], "-debug")) + { + mDNS_DebugMode = mDNStrue; + } + else + printf( "Usage: mDNSResponder [-debug]\n"); + } + + if ( !mDNS_DebugMode) + { + int result = daemon( 0, 0); + + if ( result != 0) + { + LogMsg("Could not run as daemon - exiting"); + exit( result); + } + +#if __APPLE__ + { + LogMsg("The POSIX mDNSResponder should only be used on OS X for testing - exiting"); + exit( -1); + } +#endif + } +} + + +static void DumpStateLog( mDNS *m) +// Dump a little log of what we've been up to. +{ + mDNSu32 slot; + CacheRecord *rr; + mDNSu32 CacheUsed = 0, CacheActive = 0; + mDNSs32 now = mDNSPlatformTimeNow(); + + LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----"); + + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + mDNSu32 SlotUsed = 0; + for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) + { + mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond; + CacheUsed++; + SlotUsed++; + if (rr->CRActiveQuestion) CacheActive++; + LogMsgNoIdent("%s%6ld %-6s%-6s%s", rr->CRActiveQuestion ? "*" : " ", remain, DNSTypeName(rr->resrec.rrtype), + ((PosixNetworkInterface *)rr->resrec.InterfaceID)->intfName, GetRRDisplayString(m, rr)); + usleep(1000); // Limit rate a little so we don't flood syslog too fast + } + if (m->rrcache_used[slot] != SlotUsed) + LogMsgNoIdent("Cache use mismatch: rrcache_used[slot] is %lu, true count %lu", m->rrcache_used[slot], SlotUsed); + } + if (m->rrcache_totalused != CacheUsed) + LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed); + if (m->rrcache_active != CacheActive) + LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive); + LogMsgNoIdent("Cache currently contains %lu records; %lu referenced by active questions", CacheUsed, CacheActive); + + udsserver_info(); + + LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----"); +} + +static mStatus MainLoop( mDNS *m) +// Loop until we quit. +{ + sigset_t signals; + mDNSBool gotData = mDNSfalse; + + mDNSPosixListenForSignalInEventLoop( SIGINT); + mDNSPosixListenForSignalInEventLoop( SIGTERM); + mDNSPosixListenForSignalInEventLoop( SIGUSR1); + mDNSPosixListenForSignalInEventLoop( SIGPIPE); + + for ( ; ;) + { + // Work out how long we expect to sleep before the next scheduled task + struct timeval timeout; + mDNSs32 ticks; + + // Only idle if we didn't find any data the last time around + if ( !gotData) + { + mDNSs32 nextTimerEvent = mDNS_Execute(m); + + nextTimerEvent = udsserver_idle( nextTimerEvent); + + ticks = nextTimerEvent - mDNSPlatformTimeNow(); + if (ticks < 1) ticks = 1; + } + else // otherwise call EventLoop again with 0 timemout + ticks = 0; + + timeout.tv_sec = ticks / mDNSPlatformOneSecond; + timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * 1000000 / mDNSPlatformOneSecond; + + (void) mDNSPosixRunEventLoopOnce( m, &timeout, &signals, &gotData); + + if ( sigismember( &signals, SIGUSR1)) + DumpStateLog( m); + if ( sigismember( &signals, SIGPIPE)) // happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up. + LogMsg("Received SIGPIPE - ignoring"); + if ( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)) + break; + } + + return EINTR; +} + + +// uds_daemon support //////////////////////////////////////////////////////////// + +#if MDNS_MALLOC_DEBUGGING >= 2 +#define LogMalloc LogMsg +#else +#define LogMalloc(ARGS...) ((void)0) +#endif + + +mStatus udsSupportAddFDToEventLoop( int fd, udsEventCallback callback, void *context) +/* Support routine for uds_daemon.c */ +{ + // Depends on the fact that udsEventCallback == mDNSPosixEventCallback + return mDNSPosixAddFDToEventLoop( fd, callback, context); +} + +mStatus udsSupportRemoveFDFromEventLoop( int fd) +{ + return mDNSPosixRemoveFDFromEventLoop( fd); +} + +#if MACOSX_MDNS_MALLOC_DEBUGGING >= 1 + +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, 0xDD, mem[1]+8); +// validatelists(&mDNSStorage); + free(mem); + } +} + +#endif // MACOSX_MDNS_MALLOC_DEBUGGING >= 1 + + + +// 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/mDNSPosix/ProxyResponder.c b/mDNSPosix/ProxyResponder.c index 13f0d18..5030d93 100644 --- a/mDNSPosix/ProxyResponder.c +++ b/mDNSPosix/ProxyResponder.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,15 @@ Change History (most recent first): $Log: ProxyResponder.c,v $ +Revision 1.27 2004/03/12 08:03:14 cheshire +Update comments + +Revision 1.26 2004/01/25 00:00:39 cheshire +Change to use mDNSOpaque16fromIntVal() instead of shifting and masking + +Revision 1.25 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + Revision 1.24 2003/11/14 21:27:09 cheshire : Security: Crashing bug in mDNSResponder Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. @@ -64,7 +75,7 @@ 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-existence function so that it works again +Fixed mDNS_RegisterNoSuchService non-existance function so that it works again Revision 1.11 2003/03/31 22:49:35 cheshire Add "$Log" header @@ -75,6 +86,7 @@ Add "$Log" header #include // For exit() etc. #include // For strlen() etc. #include // For select() +#include // For SIGINT, SIGTERM #include // For errno, EINTR #include // For inet_addr() #include // For INADDR_NONE @@ -181,15 +193,12 @@ mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, { domainlabel n; domainname t, d; - mDNSIPPort port; unsigned char txtbuffer[1024], *bptr = txtbuffer; char buffer[MAX_ESCAPED_DOMAIN_NAME]; 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]); @@ -203,10 +212,10 @@ mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, mDNS_RegisterService(m, recordset, &n, &t, &d, // Name, type, domain - host, port, // Host and port + host, mDNSOpaque16fromIntVal(PortAsNumber), txtbuffer, bptr-txtbuffer, // TXT data, length mDNSNULL, 0, // Subtypes - mDNSInterface_Any, // Interace ID + mDNSInterface_Any, // Interface ID ServiceCallback, mDNSNULL); // Callback and context ConvertDomainNameToCString(&recordset->RR_SRV.resrec.name, buffer); @@ -272,7 +281,8 @@ mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname * mDNSexport int main(int argc, char **argv) { - mStatus status; + mStatus status; + sigset_t signals; if (argc < 3) goto usage; @@ -282,6 +292,9 @@ mDNSexport int main(int argc, char **argv) mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %ld\n", status); return(status); } + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + if (!strcmp(argv[1], "-")) { domainname proxyhostname; @@ -291,8 +304,6 @@ mDNSexport int main(int argc, char **argv) AppendLiteralLabelString(&proxyhostname, argv[2]); AppendLiteralLabelString(&proxyhostname, "local"); RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local."); - ExampleClientEventLoop(&mDNSStorage); - mDNS_Close(&mDNSStorage); } else { @@ -318,10 +329,17 @@ mDNSexport int main(int argc, char **argv) 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); + do + { + struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM + mDNSBool gotSomething; + mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething); } + while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM))); + + mDNS_Close(&mDNSStorage); return(0); diff --git a/mDNSPosix/ReadMe.txt b/mDNSPosix/ReadMe.txt index fc00dbd..06df6e7 100755 --- a/mDNSPosix/ReadMe.txt +++ b/mDNSPosix/ReadMe.txt @@ -1,9 +1,7 @@ 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. +mDNSPosix is a port of Apple's core mDNS code to Posix platforms. 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 @@ -11,9 +9,9 @@ more information about mDNS, see the mDNS web site. -mDNS is part of a family of technologies developed by the IETF zeroconf -working group. For information about other zeroconf technologies, see -the zeroconf web site. +mDNS is part of a family of technologies resulting from the efforts of +the IETF zeroconf working group. For information about other zeroconf +technologies, see the zeroconf web site. @@ -25,198 +23,121 @@ 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. - +The sample uses the following directories: -o Client.c -- The main program for the sample mDNS client. +o mDNSCore -- A directory containing the core mDNS code. This code + is written in pure ANSI C and has proved to be very portable. + Every platform needs this core protocol engine code. -o Responder.c -- The main program for the sample mDNS responder. +o mDNSShared -- A directory containing useful code that's not core to + the main protocol engine itself, but nonetheless useful, and used by + more than one (but not necessarily all) platforms. -o Services.txt -- A sample configuration file for the mDNS responder. - You can test with this file using the option "-f Services.txt". +o mDNSPosix -- The files that are specific to Posix platforms: Linux, + Solaris, FreeBSD, NetBSD, OpenBSD, etc. This code will also work on + OS X, though that's not its primary purpose. -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 Clients -- Example client code showing how to use the API to the + services provided by the daemon. -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 Code +----------------- - -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. +cd to the mDNSPosix directory and type "make os=myos", e.g. - make os=osx + make os=panther 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. +porting to any other platform please add appropriate definitions for it +and send us the diffs so they can be incorporated into the main +distribution. 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.' +When you compile, you will get: -Back in the responder window you can quit the responder cleanly using -SIGINT (typically ^C). +o Main products for general-purpose use (e.g. on a desktop computer): + - mdnsd + - libmdns - quinn% build/mDNSResponderPosix -n Foo - ^C - quinn% +o Standalone products for dedicated devices (printer, network camera, etc.) + - mDNSClientPosix + - mDNSResponderPosix + - mDNSProxyResponderPosix -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. +o Debugging tools + - mDNSNetMonitor + - mDNSIdentify - 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% +Type "sudo make install" to install four things: +o mdnsd (usually in /usr/sbin) +o libmdns (usually in /usr/lib) +o dns_sd.h (usually in /usr/include) +o startup scripts (e.g. in /etc/rc.d) -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. +Once you've installed the header and the library, and started the +daemon running, you can cd to the "Clients" folder and type "make". +This builds a test client showing how to exercise all the major +functionality of the daemon. 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 + +--------------------+ + | Client Application | + +----------------+ +--------------------+ + | uds_daemon.c | <--- Unix Domain Socket ---> | libmdns | + +----------------+ +--------------------+ + | mDNSCore | + +----------------+ + | mDNSPosix.c | + +----------------+ -The guts of the code is in "mDNSPosix.c". +mdnsd is divided into three sections. -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". +o mDNSCore is the main protocol engine +o mDNSPosix.c provides the glue it needs to run on a Posix OS +o uds_daemon.c exports a Unix Domain Socket interface to + the services provided by mDNSCore -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. +Client applications link with the libmdns, which implements the functions +defined in the dns_sd.h header file, and implements the IPC protocol +used to communicate over the Unix Domain Socket interface to the daemon. -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. +Clients for Embedded Systems +---------------------------- -o the core calls the platformm at startup (mDNSPlatformInit) - and shutdown (mDNSPlatformClose) +For small devices with very constrained resources, with a single address +space and (typically) no virtual memory, the uds_daemon.c/UDS/libmdns +layer may be eliminated, and the Client Application may live directly +on top of mDNSCore: -o the core calls the platform to send a UDP packet (mDNSPlatformSendUDP) + +--------------------+ + | Client Application | + +--------------------+ + | mDNSCore | + +--------------------+ + | mDNSPosix.c | + +--------------------+ -o the core calls the platform to set a timer (mDNSPlatformScheduleTask) +Programming to this model is more work, so using the daemon and its +library is recommended if your platform is capable of that. -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. +The runtime behaviour when using the embedded model is as follows: 1. The application calls mDNS_Init, which in turns calls the platform (mDNSPlatformInit). @@ -252,7 +173,58 @@ 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. +mDNSPlatformUnlock() calls which are currently no-ops in mDNSPosix.c. + + +Once you've built the embedded samples you can test them 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. Caveats @@ -305,6 +277,5 @@ Networking, Communications, Hardware 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 +• port to a System V that's not Solaris +• use sig_atomic_t for signal to main thread flags diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c index 936df49..a8c297a 100755 --- a/mDNSPosix/Responder.c +++ b/mDNSPosix/Responder.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,8 +25,17 @@ Change History (most recent first): $Log: Responder.c,v $ -Revision 1.16.2.1 2004/04/07 23:51:09 cheshire -Remove obsolete comments referring to doing mDNS on port 53 +Revision 1.20 2004/05/18 23:51:26 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.19 2004/03/12 08:03:14 cheshire +Update comments + +Revision 1.18 2004/01/25 00:00:55 cheshire +Change to use mDNSOpaque16fromIntVal() instead of shifting and masking + +Revision 1.17 2003/12/11 19:11:55 cheshire +Fix compiler warning Revision 1.16 2003/08/14 02:19:55 cheshire Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord @@ -60,7 +71,7 @@ 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 + Xserve RAID needs to do interface-specific registrations Reviewed by: Josh Graessley, Bob Bradley Revision 1.4 2003/01/28 03:07:46 cheshire @@ -357,13 +368,14 @@ enum { static void PrintUsage() { fprintf(stderr, - "Usage: %s [-v level ] [-n name] [-t type] [-d domain] [-x TXT] [-p port] [-f file] [-b] [-P pidfile]\n", + "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); @@ -376,6 +388,7 @@ static void PrintUsage() 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; @@ -416,6 +429,9 @@ static void ParseArguments(int argc, char **argv) exit(1); } break; + case 'r': + gAvoidPort53 = mDNSfalse; + break; case 'n': gRichTextHostName = optarg; if ( ! CheckThatRichTextHostNameIsUsable(gRichTextHostName, mDNStrue) ) { @@ -559,7 +575,6 @@ static mStatus RegisterOneService(const char * richTextHostName, { mStatus status; PosixService * thisServ; - mDNSOpaque16 port; domainlabel name; domainname type; domainname domain; @@ -573,14 +588,12 @@ static mStatus RegisterOneService(const char * richTextHostName, 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 + NULL, mDNSOpaque16fromIntVal(portNumber), text, textLen, // TXT data, length NULL, 0, // Subtypes - mDNSInterface_Any, // Interace ID + mDNSInterface_Any, // Interface ID RegistrationCallback, thisServ); // Callback and context } if (status == mStatus_NoError) { @@ -608,20 +621,16 @@ static mStatus RegisterOneService(const char * richTextHostName, } 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); + { + mDNSBool good = (fgets(buf, bufSize, fp) != NULL); + if (good) + { + size_t len = strlen(buf); good = (len > 0 && buf[len - 1] == '\n'); - } - if (good) { - buf[len - 1] = 0; - } + if (good) buf[len - 1] = 0; + } return good; -} + } static mStatus RegisterServicesInFile(const char *filePath) { diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c index b28aa73..d9426ab 100755 --- a/mDNSPosix/mDNSPosix.c +++ b/mDNSPosix/mDNSPosix.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -36,8 +38,72 @@ Change History (most recent first): $Log: mDNSPosix.c,v $ -Revision 1.25.2.1 2004/04/09 17:57:31 cheshire -Make sure to set the TxAndRx field so that duplicate suppression works correctly +Revision 1.46 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.45 2004/05/12 22:03:09 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.44 2004/04/21 02:49:11 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.43 2004/04/14 23:09:29 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.42 2004/04/09 17:43:04 cheshire +Make sure to set the McastTxRx field so that duplicate suppression works correctly + +Revision 1.41 2004/02/06 01:19:51 cheshire +Conditionally exclude IPv6 code unless HAVE_IPV6 is set + +Revision 1.40 2004/02/05 01:00:01 rpantos +Fix some issues that turned up when building for FreeBSD. + +Revision 1.39 2004/01/28 21:12:15 cheshire +Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6) + +Revision 1.38 2004/01/27 20:15:23 cheshire +: Time to prune obsolete code for listening on port 53 + +Revision 1.37 2004/01/24 05:12:03 cheshire +: Need separate socket for issuing unicast queries + +Revision 1.36 2004/01/24 04:59:16 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.35 2004/01/23 21:37:08 cheshire +For consistency, rename multicastSocket to multicastSocket4, and multicastSocketv6 to multicastSocket6 + +Revision 1.34 2004/01/22 03:43:09 cheshire +Export constants like mDNSInterface_LocalOnly so that the client layers can use them + +Revision 1.33 2004/01/21 21:54:20 cheshire +: Don't try to receive unicast responses if we're not the first to bind to the UDP port + +Revision 1.32 2004/01/20 01:49:28 rpantos +Tweak error handling of last checkin a bit. + +Revision 1.31 2004/01/20 01:39:27 rpantos +Respond to If changes by rebuilding interface list. + +Revision 1.30 2003/12/11 19:40:36 cheshire +Fix 'destAddr.type == senderAddr.type;' that should have said 'destAddr.type = senderAddr.type;' + +Revision 1.29 2003/12/11 18:53:22 cheshire +Fix compiler warning reported by Paul Guyot + +Revision 1.28 2003/12/11 03:03:51 rpantos +Clean up mDNSPosix so that it builds on OS X again. + +Revision 1.27 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + +Revision 1.26 2003/11/14 20:59:09 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. Revision 1.25 2003/10/30 19:25:49 cheshire Fix signed/unsigned warning on certain compilers @@ -122,7 +188,6 @@ 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 @@ -131,34 +196,56 @@ First checkin #include #include #include +#include +#include #include #include +#include #include #include +#include #include +#include // platform support for UTC time + +#if USES_NETLINK +#include +#include +#include +#else // USES_NETLINK +#include +#include +#endif // USES_NETLINK #include "mDNSUNP.h" +#include "GenLinkedList.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; +// We keep a list of client-supplied event sources in PosixEventSource records +struct PosixEventSource + { + mDNSPosixEventCallback Callback; + void *Context; + int fd; + struct PosixEventSource *Next; + }; +typedef struct PosixEventSource PosixEventSource; -struct PosixNetworkInterface +// Context record for interface change callback +struct IfChangeRec { - NetworkInterfaceInfo coreIntf; - const char * intfName; - PosixNetworkInterface * aliasIntf; - int index; - int multicastSocket; - int multicastSocketv6; + int NotifySD; + mDNS* mDNS; }; +typedef struct IfChangeRec IfChangeRec; + +// Note that static data is initialized to zero in (modern) C. +static fd_set gEventFDs; +static int gMaxFD; // largest fd in gEventFDs +static GenLinkedList gEventSources; // linked list of PosixEventSource's +static sigset_t gEventSignalSet; // Signals which event loop listens for +static sigset_t gEventSignals; // Signals which were received while inside loop // *************************************************************************** // Globals (for debugging) @@ -172,43 +259,6 @@ static int num_pkts_rejected = 0; 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) @@ -224,7 +274,7 @@ static void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr break; } -#ifdef mDNSIPv6Support +#if HAVE_IPV6 case AF_INET6: { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa; @@ -250,19 +300,18 @@ static void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr // 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) + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) { - int err; + int err = 0; struct sockaddr_storage to; - PosixNetworkInterface * thisIntf; + PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID); + int sendingsocket = -1; 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 + assert(dstPort.NotAnInteger != 0); if (dst->type == mDNSAddrType_IPv4) { @@ -273,9 +322,10 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *co sin->sin_family = AF_INET; sin->sin_port = dstPort.NotAnInteger; sin->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + sendingsocket = thisIntf ? thisIntf->multicastSocket4 : m->p->unicastSocket4; } -#ifdef mDNSIPv6Support +#if HAVE_IPV6 else if (dst->type == mDNSAddrType_IPv6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to; @@ -284,23 +334,22 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *co sin6->sin6_family = AF_INET6; sin6->sin6_port = dstPort.NotAnInteger; sin6->sin6_addr = *(struct in6_addr*)&dst->ip.v6; + sendingsocket = thisIntf ? thisIntf->multicastSocket6 : m->p->unicastSocket6; } #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)); + if (sendingsocket >= 0) + err = sendto(sendingsocket, 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; + 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); + { + if (thisIntf) + 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); + else + verbosedebugf("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst); + } return PosixErrorToStatus(err); } @@ -316,15 +365,16 @@ static void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) struct sockaddr_storage from; socklen_t fromLen; int flags; + mDNSu8 ttl; mDNSBool reject; + const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL; 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); + packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl); if (packetLen >= 0) { @@ -349,7 +399,7 @@ static void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) #if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR)) if ( (destAddr.NotAnInteger == 0) && (flags & MSG_MCAST) ) { - destAddr.type == senderAddr.type; + destAddr.type = senderAddr.type; if (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup; else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroupv6; } @@ -364,31 +414,34 @@ static void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) // 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) + if (intf) { - 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) + 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 { - 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; + 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++; } } - 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)) @@ -399,21 +452,64 @@ static void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt) if (packetLen >= 0) mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen, - &senderAddr, senderPort, &destAddr, MulticastDNSPort, intf->coreIntf.InterfaceID, 255); + &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID, ttl); + } + +mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context, int *descriptor) + { + (void)dst; // Unused + (void)dstport; // Unused + (void)InterfaceID; // Unused + (void)callback; // Unused + (void)context; // Unused + (void)descriptor; // Unused + return(mStatus_UnsupportedErr); + } + +mDNSexport void mDNSPlatformTCPCloseConnection(int sd) + { + (void)sd; // Unused + } + +mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) + { + (void)sd; // Unused + (void)buf; // Unused + (void)buflen; // Unused + return(0); + } + +mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len) + { + (void)sd; // Unused + (void)msg; // Unused + (void)len; // Unused + return(0); } #if COMPILER_LIKES_PRAGMA_MARK -#pragma mark ***** Init and Term +#pragma mark ***** Get/Free Search Domain List #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) +mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void) { - MakeDomainLabelFromLiteralString(namelabel, "Fill in Default Service Name Here"); + static DNameListElem tmp; + static mDNSBool init = mDNSfalse; + + if (!init) + { + MakeDomainNameFromDNSNameString(&tmp.name, "local."); + tmp.next = NULL; + init = mDNStrue; + } + return mDNS_CopyDNameList(&tmp); } +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark ***** Init and Term +#endif + // This gets the current hostname, truncating it at the first dot if necessary mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) { @@ -423,6 +519,15 @@ mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) namelabel->c[0] = len; } +// 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) + { + // On Unix we have no better name than the host name, so we just use that. + GetUserSpecifiedRFC1034ComputerName( namelabel); + } + // 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) @@ -439,14 +544,46 @@ static PosixNetworkInterface *SearchForInterfaceByName(mDNS *const m, const char return intf; } +extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS *const m, mDNSu32 index) + { + PosixNetworkInterface *intf; + + assert(m != NULL); + + if (index == (uint32_t)~0) return(mDNSInterface_LocalOnly); + + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ( (intf != NULL) && (mDNSu32) intf->index != index) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); + + return (mDNSInterfaceID) intf; + } + +mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mDNSInterfaceID id) + { + PosixNetworkInterface *intf; + + assert(m != NULL); + + if (id == mDNSInterface_LocalOnly) return((mDNSu32)~0); + + intf = (PosixNetworkInterface*)(m->HostInterfaces); + while ( (intf != NULL) && (mDNSInterfaceID) intf != id) + intf = (PosixNetworkInterface *)(intf->coreIntf.next); + + return intf ? intf->index : 0; + } + // 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); + if (intf->multicastSocket4 != -1) assert(close(intf->multicastSocket4) == 0); +#if HAVE_IPV6 + if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 0); +#endif free(intf); } @@ -467,8 +604,9 @@ static void ClearInterfaceList(mDNS *const m) num_pkts_rejected = 0; } -// Sets up a multicast send/receive socket for the specified -// port on the interface specified by the IP addrelss intfAddr. +// Sets up a send/receive socket. +// If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface +// If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries static int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr) { int err = 0; @@ -476,22 +614,22 @@ static int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interface static const int kIntTwoFiveFive = 255; static const unsigned char kByteTwoFiveFive = 255; - (void) interfaceIndex; // Unused + (void) interfaceIndex; // This parameter unused on plaforms that don't have IPv6 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 +#if HAVE_IPV6 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) + // ... with a shared UDP port, if it's for multicast receiving + if (err == 0 && port.NotAnInteger) { #if defined(SO_REUSEPORT) err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); @@ -529,9 +667,16 @@ static int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interface #warning This platform has no way to get the destination interface information -- will only work for single-homed hosts #endif } + #if defined(IP_RECVTTL) // Linux + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IP_RECVTTL"); } + } + #endif // Add multicast group membership on this interface - if (err == 0) + if (err == 0 && port.NotAnInteger) { imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; imr.imr_interface = ((struct sockaddr_in*)intfAddr)->sin_addr; @@ -540,7 +685,7 @@ static int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interface } // Specify outgoing interface too - if (err == 0) + if (err == 0 && port.NotAnInteger) { 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"); } @@ -574,23 +719,30 @@ static int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interface } } // endif (intfAddr->sa_family == AF_INET) -#ifdef mDNSIPv6Support +#if HAVE_IPV6 else if (intfAddr->sa_family == AF_INET6) { struct ipv6_mreq imr6; struct sockaddr_in6 bindAddr6; + #if defined(IPV6_PKTINFO) 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 } + #else + #warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts + #endif + #if defined(IPV6_HOPLIMIT) + if (err == 0) + { + err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_HOPLIMIT, &kOn, sizeof(kOn)); + if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); } + } + #endif // Add multicast group membership on this interface - if (err == 0) + if (err == 0 && port.NotAnInteger) { imr6.ipv6mr_multiaddr = *(const struct in6_addr*)&AllDNSLinkGroupv6; imr6.ipv6mr_interface = interfaceIndex; @@ -604,7 +756,7 @@ static int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interface } // Specify outgoing interface too - if (err == 0) + if (err == 0 && port.NotAnInteger) { u_int multicast_if = interfaceIndex; err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if)); @@ -683,7 +835,7 @@ static int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, const cha assert(intfName != NULL); // Allocate the interface structure itself. - intf = malloc(sizeof(*intf)); + intf = (PosixNetworkInterface*)malloc(sizeof(*intf)); if (intf == NULL) { assert(0); err = ENOMEM; } // And make a copy of the intfName. @@ -698,13 +850,15 @@ static int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, const cha // Set up the fields required by the mDNS core. SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL); intf->coreIntf.Advertise = m->AdvertiseLocalAddresses; - intf->coreIntf.TxAndRx = mDNStrue; + intf->coreIntf.McastTxRx = mDNStrue; // 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; + intf->multicastSocket4 = -1; +#if HAVE_IPV6 + intf->multicastSocket6 = -1; +#endif alias = SearchForInterfaceByName(m, intf->intfName); if (alias == NULL) alias = intf; intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias; @@ -716,11 +870,11 @@ static int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, const cha // 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); + if (alias->multicastSocket4 == -1 && intfAddr->sa_family == AF_INET) + err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket4); +#if HAVE_IPV6 + else if (alias->multicastSocket6 == -1 && intfAddr->sa_family == AF_INET6) + err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket6); #endif } @@ -748,6 +902,7 @@ static int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, const cha return err; } +// Call get_ifi_info() to obtain a list of active interfaces and call SetupOneInterface() on each one. static int SetupInterfaceList(mDNS *const m) { mDNSBool foundav4 = mDNSfalse; @@ -760,7 +915,7 @@ static int SetupInterfaceList(mDNS *const m) if (intfList == NULL) err = ENOENT; -#ifdef mDNSIPv6Support +#if HAVE_IPV6 if (err == 0) /* Link the IPv6 list to the end of the IPv4 list */ { struct ifi_info **p = &intfList; @@ -775,7 +930,7 @@ static int SetupInterfaceList(mDNS *const m) while (i) { if ( ((i->ifi_addr->sa_family == AF_INET) -#ifdef mDNSIPv6Support +#if HAVE_IPV6 || (i->ifi_addr->sa_family == AF_INET6) #endif ) && (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT) ) @@ -809,12 +964,255 @@ static int SetupInterfaceList(mDNS *const m) return err; } +#if USES_NETLINK + +// See for a description of NetLink + +// Open a socket that will receive interface change notifications +mStatus OpenIfNotifySocket( int *pFD) + { + mStatus err = mStatus_NoError; + struct sockaddr_nl snl; + int sock; + int ret; + + sock = socket( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) + return errno; + + // Configure read to be non-blocking because inbound msg size is not known in advance + (void) fcntl( sock, F_SETFL, O_NONBLOCK); + + /* Subscribe the socket to Link & IP addr notifications. */ + bzero( &snl, sizeof snl); + snl.nl_family = AF_NETLINK; + snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; + ret = bind( sock, (struct sockaddr *) &snl, sizeof snl); + if ( 0 == ret) + *pFD = sock; + else + err = errno; + + return err; + } + +#if MDNS_DEBUGMSGS +static void PrintNetLinkMsg( const struct nlmsghdr *pNLMsg) + { + const char *kNLMsgTypes[] = { "", "NLMSG_NOOP", "NLMSG_ERROR", "NLMSG_DONE", "NLMSG_OVERRUN" }; + const char *kNLRtMsgTypes[] = { "RTM_NEWLINK", "RTM_DELLINK", "RTM_GETLINK", "RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR" }; + + printf( "nlmsghdr len=%d, type=%s, flags=0x%x\n", pNLMsg->nlmsg_len, + pNLMsg->nlmsg_type < RTM_BASE ? kNLMsgTypes[ pNLMsg->nlmsg_type] : kNLRtMsgTypes[ pNLMsg->nlmsg_type - RTM_BASE], + pNLMsg->nlmsg_flags); + + if ( RTM_NEWLINK <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETLINK) + { + struct ifinfomsg *pIfInfo = (struct ifinfomsg*) NLMSG_DATA( pNLMsg); + printf( "ifinfomsg family=%d, type=%d, index=%d, flags=0x%x, change=0x%x\n", pIfInfo->ifi_family, + pIfInfo->ifi_type, pIfInfo->ifi_index, pIfInfo->ifi_flags, pIfInfo->ifi_change); + + } + else if ( RTM_NEWADDR <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETADDR) + { + struct ifaddrmsg *pIfAddr = (struct ifaddrmsg*) NLMSG_DATA( pNLMsg); + printf( "ifaddrmsg family=%d, index=%d, flags=0x%x\n", pIfAddr->ifa_family, + pIfAddr->ifa_index, pIfAddr->ifa_flags); + } + printf( "\n"); + } +#endif + +static uint32_t ProcessRoutingNotification( int sd) +// Read through the messages on sd and if any indicate that any interface records should +// be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. + { + ssize_t readCount; + char buff[ 4096]; + struct nlmsghdr *pNLMsg = (struct nlmsghdr*) buff; + uint32_t result = 0; + + // The structure here is more complex than it really ought to be because, + // unfortunately, there's no good way to size a buffer in advance large + // enough to hold all pending data and so avoid message fragmentation. + // (Note that FIONREAD is not supported on AF_NETLINK.) + + readCount = read( sd, buff, sizeof buff); + while ( 1) + { + // Make sure we've got an entire nlmsghdr in the buffer, and payload, too. + // If not, discard already-processed messages in buffer and read more data. + if ( ( (char*) &pNLMsg[1] > ( buff + readCount)) || // i.e. *pNLMsg extends off end of buffer + ( (char*) pNLMsg + pNLMsg->nlmsg_len > ( buff + readCount))) + { + if ( buff < (char*) pNLMsg) // we have space to shuffle + { + // discard processed data + readCount -= ( (char*) pNLMsg - buff); + memmove( buff, pNLMsg, readCount); + pNLMsg = (struct nlmsghdr*) buff; + + // read more data + readCount += read( sd, buff + readCount, sizeof buff - readCount); + continue; // spin around and revalidate with new readCount + } + else + break; // Otherwise message does not fit in buffer + } + +#if MDNS_DEBUGMSGS + PrintNetLinkMsg( pNLMsg); +#endif + + // Process the NetLink message + if ( pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK) + result |= 1 << ((struct ifinfomsg*) NLMSG_DATA( pNLMsg))->ifi_index; + else if ( pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR) + result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA( pNLMsg))->ifa_index; + + // Advance pNLMsg to the next message in the buffer + if ( ( pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE) + { + ssize_t len = readCount - ( (char*)pNLMsg - buff); + pNLMsg = NLMSG_NEXT( pNLMsg, len); + } + else + break; // all done! + } + + return result; + } + +#else // USES_NETLINK + +// Open a socket that will receive interface change notifications +mStatus OpenIfNotifySocket( int *pFD) + { + *pFD = socket( AF_ROUTE, SOCK_RAW, 0); + + if ( *pFD < 0) + return mStatus_UnknownErr; + + // Configure read to be non-blocking because inbound msg size is not known in advance + (void) fcntl( *pFD, F_SETFL, O_NONBLOCK); + + return mStatus_NoError; + } + +#if MDNS_DEBUGMSGS +static void PrintRoutingSocketMsg( const struct ifa_msghdr *pRSMsg) + { + const char *kRSMsgTypes[] = { "", "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING", + "RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE", + "RTM_NEWADDR", "RTM_DELADDR", "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" }; + + int index = pRSMsg->ifam_type == RTM_IFINFO ? ((struct if_msghdr*) pRSMsg)->ifm_index : pRSMsg->ifam_index; + + printf( "ifa_msghdr len=%d, type=%s, index=%d\n", pRSMsg->ifam_msglen, kRSMsgTypes[ pRSMsg->ifam_type], index); + } +#endif + +static uint32_t ProcessRoutingNotification( int sd) +// Read through the messages on sd and if any indicate that any interface records should +// be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0. + { + ssize_t readCount; + char buff[ 4096]; + struct ifa_msghdr *pRSMsg = (struct ifa_msghdr*) buff; + uint32_t result = 0; + + readCount = read( sd, buff, sizeof buff); + if ( readCount < (ssize_t) sizeof( struct ifa_msghdr)) + return mStatus_UnsupportedErr; // cannot decipher message + +#if MDNS_DEBUGMSGS + PrintRoutingSocketMsg( pRSMsg); +#endif + + // Process the message + if ( pRSMsg->ifam_type == RTM_NEWADDR || pRSMsg->ifam_type == RTM_DELADDR || + pRSMsg->ifam_type == RTM_IFINFO) + { + if ( pRSMsg->ifam_type == RTM_IFINFO) + result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index; + else + result |= 1 << pRSMsg->ifam_index; + } + + return result; + } + +#endif // USES_NETLINK + +// Called when data appears on interface change notification socket +static void InterfaceChangeCallback( void *context) + { + IfChangeRec *pChgRec = (IfChangeRec*) context; + fd_set readFDs; + uint32_t changedInterfaces = 0; + struct timeval zeroTimeout = { 0, 0 }; + + FD_ZERO( &readFDs); + FD_SET( pChgRec->NotifySD, &readFDs); + + do + { + changedInterfaces |= ProcessRoutingNotification( pChgRec->NotifySD); + } + while ( 0 < select( pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)); + + // Currently we rebuild the entire interface list whenever any interface change is + // detected. If this ever proves to be a performance issue in a multi-homed + // configuration, more care should be paid to changedInterfaces. + if ( changedInterfaces) + mDNSPlatformPosixRefreshInterfaceList( pChgRec->mDNS); + } + +// Register with either a Routing Socket or RtNetLink to listen for interface changes. +static mStatus WatchForInterfaceChange(mDNS *const m) + { + mStatus err; + IfChangeRec *pChgRec; + + pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate( sizeof *pChgRec); + if ( pChgRec == NULL) + return mStatus_NoMemoryErr; + + pChgRec->mDNS = m; + err = OpenIfNotifySocket( &pChgRec->NotifySD); + if ( err == 0) + err = mDNSPosixAddFDToEventLoop( pChgRec->NotifySD, InterfaceChangeCallback, pChgRec); + + return err; + } + +// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. +// If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- +// we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. +mDNSlocal mDNSBool mDNSPlatformInit_ReceiveUnicast(void) + { + int err; + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + struct sockaddr_in s5353; + s5353.sin_family = AF_INET; + s5353.sin_port = MulticastDNSPort.NotAnInteger; + s5353.sin_addr.s_addr = 0; + err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); + close(s); + if (err) debugf("No unicast UDP responses"); + else debugf("Unicast UDP responses okay"); + return(err == 0); + } + // mDNS core calls this routine to initialise the platform-specific data. mDNSexport mStatus mDNSPlatformInit(mDNS *const m) { - int err; + int err = 0; + struct sockaddr sa; assert(m != NULL); + if (mDNSPlatformInit_ReceiveUnicast()) m->CanReceiveUnicast = mDNStrue; + // Tell mDNS core the names of this machine. // Set up the nice label @@ -829,13 +1227,33 @@ mDNSexport mStatus mDNSPlatformInit(mDNS *const m) mDNS_GenerateFQDN(m); + sa.sa_family = AF_INET; + m->p->unicastSocket4 = -1; + if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket4); +#if HAVE_IPV6 + sa.sa_family = AF_INET6; + m->p->unicastSocket6 = -1; + if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket6); +#endif + // Tell mDNS core about the network interfaces on this machine. - err = SetupInterfaceList(m); + if (err == mStatus_NoError) err = SetupInterfaceList(m); + + if (err == mStatus_NoError) + { + err = WatchForInterfaceChange(m); + // Failure to observe interface changes is non-fatal. + if ( err != mStatus_NoError) + { + fprintf(stderr, "mDNS(%d) WARNING: Unable to detect interface changes (%d).\n", getpid(), err); + err = mStatus_NoError; + } + } // 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) + if (err == mStatus_NoError) mDNSCoreInitComplete(m, mStatus_NoError); return PosixErrorToStatus(err); @@ -847,6 +1265,10 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) { assert(m != NULL); ClearInterfaceList(m); + if (m->p->unicastSocket4 != -1) assert(close(m->p->unicastSocket4) == 0); +#if HAVE_IPV6 + if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 0); +#endif } extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m) @@ -944,7 +1366,18 @@ mDNSexport mDNSs32 mDNSPlatformTimeNow() return( (tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625) ); } -mDNSexport void mDNSPosixGetFDSet(mDNS *const m, int *nfds, fd_set *readfds, struct timeval *timeout) +mDNSexport mDNSs32 mDNSPlatformUTC(void) + { + return time(NULL); + } + +mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s) + { + if (*nfds < s + 1) *nfds = s + 1; + FD_SET(s, readfds); + } + +mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout) { mDNSs32 ticks; struct timeval interval; @@ -954,20 +1387,16 @@ mDNSexport void mDNSPosixGetFDSet(mDNS *const m, int *nfds, fd_set *readfds, str // 2. Build our list of active file descriptors PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces); + if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket4); +#if HAVE_IPV6 + if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6); +#endif 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); - } + if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4); +#if HAVE_IPV6 + if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6); +#endif info = (PosixNetworkInterface *)(info->coreIntf.next); } @@ -989,18 +1418,173 @@ mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds) assert(m != NULL); assert(readfds != NULL); info = (PosixNetworkInterface *)(m->HostInterfaces); + + if (m->p->unicastSocket4 != -1 && FD_ISSET(m->p->unicastSocket4, readfds)) + { + FD_CLR(m->p->unicastSocket4, readfds); + SocketDataReady(m, NULL, m->p->unicastSocket4); + } +#if HAVE_IPV6 + if (m->p->unicastSocket6 != -1 && FD_ISSET(m->p->unicastSocket6, readfds)) + { + FD_CLR(m->p->unicastSocket6, readfds); + SocketDataReady(m, NULL, m->p->unicastSocket6); + } +#endif + while (info) { - if (info->multicastSocket != -1 && FD_ISSET(info->multicastSocket, readfds)) + if (info->multicastSocket4 != -1 && FD_ISSET(info->multicastSocket4, readfds)) { - FD_CLR(info->multicastSocket, readfds); - SocketDataReady(m, info, info->multicastSocket); + FD_CLR(info->multicastSocket4, readfds); + SocketDataReady(m, info, info->multicastSocket4); } - if (info->multicastSocketv6 != -1 && FD_ISSET(info->multicastSocketv6, readfds)) +#if HAVE_IPV6 + if (info->multicastSocket6 != -1 && FD_ISSET(info->multicastSocket6, readfds)) { - FD_CLR(info->multicastSocketv6, readfds); - SocketDataReady(m, info, info->multicastSocketv6); + FD_CLR(info->multicastSocket6, readfds); + SocketDataReady(m, info, info->multicastSocket6); } +#endif info = (PosixNetworkInterface *)(info->coreIntf.next); } } + +// update gMaxFD +static void DetermineMaxEventFD( void ) + { + PosixEventSource *iSource; + + gMaxFD = 0; + for ( iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + if ( gMaxFD < iSource->fd) + gMaxFD = iSource->fd; + } + +// Add a file descriptor to the set that mDNSPosixRunEventLoopOnce() listens to. +mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context) + { + PosixEventSource *newSource; + + if ( gEventSources.LinkOffset == 0) + InitLinkedList( &gEventSources, offsetof( PosixEventSource, Next)); + + if ( fd >= (int) FD_SETSIZE || fd < 0) + return mStatus_UnsupportedErr; + if ( callback == NULL) + return mStatus_BadParamErr; + + newSource = (PosixEventSource*) malloc( sizeof *newSource); + if ( NULL == newSource) + return mStatus_NoMemoryErr; + + newSource->Callback = callback; + newSource->Context = context; + newSource->fd = fd; + + AddToTail( &gEventSources, newSource); + FD_SET( fd, &gEventFDs); + + DetermineMaxEventFD(); + + return mStatus_NoError; + } + +// Remove a file descriptor from the set that mDNSPosixRunEventLoopOnce() listens to. +mStatus mDNSPosixRemoveFDFromEventLoop( int fd) + { + PosixEventSource *iSource; + + for ( iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + { + if ( fd == iSource->fd) + { + FD_CLR( fd, &gEventFDs); + RemoveFromList( &gEventSources, iSource); + free( iSource); + DetermineMaxEventFD(); + return mStatus_NoError; + } + } + return mStatus_NoSuchNameErr; + } + +// Simply note the received signal in gEventSignals. +static void NoteSignal( int signum) + { + sigaddset( &gEventSignals, signum); + } + +// Tell the event package to listen for signal and report it in mDNSPosixRunEventLoopOnce(). +mStatus mDNSPosixListenForSignalInEventLoop( int signum) + { + struct sigaction action; + mStatus err; + + bzero( &action, sizeof action); // more portable than member-wise assignment + action.sa_handler = NoteSignal; + err = sigaction( signum, &action, (struct sigaction*) NULL); + + sigaddset( &gEventSignalSet, signum); + + return err; + } + +// Tell the event package to stop listening for signal in mDNSPosixRunEventLoopOnce(). +mStatus mDNSPosixIgnoreSignalInEventLoop( int signum) + { + struct sigaction action; + mStatus err; + + bzero( &action, sizeof action); // more portable than member-wise assignment + action.sa_handler = SIG_DFL; + err = sigaction( signum, &action, (struct sigaction*) NULL); + + sigdelset( &gEventSignalSet, signum); + + return err; + } + +// Do a single pass through the attendent event sources and dispatch any found to their callbacks. +// Return as soon as internal timeout expires, or a signal we're listening for is received. +mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, + sigset_t *pSignalsReceived, mDNSBool *pDataDispatched) + { + fd_set listenFDs = gEventFDs; + int fdMax = 0, numReady; + struct timeval timeout = *pTimeout; + + // Include the sockets that are listening to the wire in our select() set + mDNSPosixGetFDSet( m, &fdMax, &listenFDs, &timeout); // timeout may get modified + if ( fdMax < gMaxFD) + fdMax = gMaxFD; + + numReady = select( fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout); + + // If any data appeared, invoke its callback + if ( numReady > 0) + { + PosixEventSource *iSource; + + (void) mDNSPosixProcessFDSet( m, &listenFDs); // call this first to process wire data for clients + + for ( iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next) + { + if ( FD_ISSET( iSource->fd, &listenFDs)) + { + iSource->Callback( iSource->Context); + break; // in case callback removed elements from gEventSources + } + } + *pDataDispatched = mDNStrue; + } + else + *pDataDispatched = mDNSfalse; + + (void) sigprocmask( SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL); + *pSignalsReceived = gEventSignals; + sigemptyset( &gEventSignals); + (void) sigprocmask( SIG_UNBLOCK, &gEventSignalSet, (sigset_t*) NULL); + + return mStatus_NoError; + } diff --git a/mDNSPosix/mDNSPosix.h b/mDNSPosix/mDNSPosix.h index a254568..d6ffbd6 100755 --- a/mDNSPosix/mDNSPosix.h +++ b/mDNSPosix/mDNSPosix.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,24 @@ Change History (most recent first): $Log: mDNSPosix.h,v $ +Revision 1.15 2004/02/06 01:19:51 cheshire +Conditionally exclude IPv6 code unless HAVE_IPV6 is set + +Revision 1.14 2004/01/28 21:12:15 cheshire +Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6) + +Revision 1.13 2004/01/24 05:12:03 cheshire +: Need separate socket for issuing unicast queries + +Revision 1.12 2004/01/23 21:37:08 cheshire +For consistency, rename multicastSocket to multicastSocket4, and multicastSocketv6 to multicastSocket6 + +Revision 1.11 2003/12/11 03:03:51 rpantos +Clean up mDNSPosix so that it builds on OS X again. + +Revision 1.10 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + Revision 1.9 2003/10/30 19:25:19 cheshire Fix warning on certain compilers @@ -57,23 +77,42 @@ First checkin #ifndef __mDNSPlatformPosix_h #define __mDNSPlatformPosix_h +#include #include -#if HAVE_IPV6 -#define mDNSIPv6Support 1 -#endif - #ifdef __cplusplus extern "C" { #endif +// 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 multicastSocket4; +#if HAVE_IPV6 + int multicastSocket6; +#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 - long dummy[1]; // Some compilers don't like empty structures + int unicastSocket4; +#if HAVE_IPV6 + int unicastSocket6; +#endif }; extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m); @@ -85,9 +124,17 @@ extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m); // 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 mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout); extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds); +typedef void (*mDNSPosixEventCallback)( void *context); + +extern mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context); +extern mStatus mDNSPosixRemoveFDFromEventLoop( int fd); +extern mStatus mDNSPosixListenForSignalInEventLoop( int signum); +extern mStatus mDNSPosixIgnoreSignalInEventLoop( int signum); +extern mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched); + #ifdef __cplusplus } #endif diff --git a/mDNSPosix/mDNSUNP.c b/mDNSPosix/mDNSUNP.c index fa9bda0..8e60808 100755 --- a/mDNSPosix/mDNSUNP.c +++ b/mDNSPosix/mDNSUNP.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,19 @@ Change History (most recent first): $Log: mDNSUNP.c,v $ +Revision 1.16 2004/03/20 05:37:09 cheshire +Fix contributed by Terry Lambert & Alfred Perlstein: +Don't use uint8_t -- it requires stdint.h, which doesn't exist on FreeBSD 4.x + +Revision 1.15 2004/02/14 01:09:45 rpantos +Just use HAVE_IPV6 rather than defined(HAVE_IPV6). + +Revision 1.14 2003/12/11 18:53:40 cheshire +Fix compiler warning reported by Paul Guyot + +Revision 1.13 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + Revision 1.12 2003/09/02 20:47:13 cheshire Fix signed/unsigned warning @@ -105,7 +120,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) struct ifreq *ifr, ifrcopy; struct sockaddr_in *sinptr; -#if defined(AF_INET6) && defined(HAVE_IPV6) +#if defined(AF_INET6) && HAVE_IPV6 struct sockaddr_in6 *sinptr6; #endif @@ -121,7 +136,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) lastlen = 0; len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ for ( ; ; ) { - buf = malloc(len); + buf = (char*)malloc(len); if (buf == NULL) { goto gotError; } @@ -175,7 +190,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) if ((flags & IFF_UP) == 0) continue; /* ignore if interface not up */ - ifi = calloc(1, sizeof(struct ifi_info)); + ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info)); if (ifi == NULL) { goto gotError; } @@ -193,7 +208,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) case AF_INET: sinptr = (struct sockaddr_in *) &ifr->ifr_addr; if (ifi->ifi_addr == NULL) { - ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in)); + ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_addr == NULL) { goto gotError; } @@ -205,7 +220,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) goto gotError; } sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; - ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in)); + ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_brdaddr == NULL) { goto gotError; } @@ -219,7 +234,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) goto gotError; } sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; - ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in)); + ifi->ifi_dstaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); if (ifi->ifi_dstaddr == NULL) { goto gotError; } @@ -229,7 +244,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) } break; -#if defined(AF_INET6) && defined(HAVE_IPV6) +#if defined(AF_INET6) && HAVE_IPV6 case AF_INET6: sinptr6 = (struct sockaddr_in6 *) &ifr->ifr_addr; if (ifi->ifi_addr == NULL) { @@ -292,7 +307,7 @@ free_ifi_info(struct ifi_info *ifihead) 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 sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl) { struct msghdr msg; struct iovec iov[1]; @@ -305,6 +320,8 @@ recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, char control[1024]; } control_un; + *ttl = 255; // If kernel fails to provide TTL data then assume the TTL was 255 as it should be + msg.msg_control = control_un.control; msg.msg_controllen = sizeof(control_un.control); msg.msg_flags = 0; @@ -312,9 +329,9 @@ recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp, memset(&msg, 0, sizeof(msg)); /* make certain msg_accrightslen = 0 */ #endif /* CMSG_FIRSTHDR */ - msg.msg_name = (void *) sa; + msg.msg_name = (char *) sa; msg.msg_namelen = *salenptr; - iov[0].iov_base = ptr; + iov[0].iov_base = (char *)ptr; iov[0].iov_len = nbytes; msg.msg_iov = iov; msg.msg_iovlen = 1; @@ -401,7 +418,20 @@ struct in_pktinfo } #endif -#if defined(IPV6_PKTINFO) && defined(HAVE_IPV6) +#ifdef IP_RECVTTL + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVTTL) { + *ttl = *(u_char*)CMSG_DATA(cmptr); + continue; + } + else if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_TTL) { // some implementations seem to send IP_TTL instead of IP_RECVTTL + *ttl = *(int*)CMSG_DATA(cmptr); + continue; + } +#endif + +#if defined(IPV6_PKTINFO) && HAVE_IPV6 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == IPV6_PKTINFO) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr; @@ -417,6 +447,14 @@ struct in_pktinfo continue; } #endif + +#if defined(IPV6_HOPLIMIT) && HAVE_IPV6 + if (cmptr->cmsg_level == IPPROTO_IPV6 && + cmptr->cmsg_type == IPV6_HOPLIMIT) { + *ttl = *(int*)CMSG_DATA(cmptr); + continue; + } +#endif assert(0); // unknown ancillary data } return(n); diff --git a/mDNSPosix/mDNSUNP.h b/mDNSPosix/mDNSUNP.h index 21d13f1..0d78baa 100755 --- a/mDNSPosix/mDNSUNP.h +++ b/mDNSPosix/mDNSUNP.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,23 @@ Change History (most recent first): $Log: mDNSUNP.h,v $ +Revision 1.13 2004/03/20 05:37:09 cheshire +Fix contributed by Terry Lambert & Alfred Perlstein: +Don't use uint8_t -- it requires stdint.h, which doesn't exist on FreeBSD 4.x + +Revision 1.12 2004/01/28 21:12:15 cheshire +Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6) + +Revision 1.11 2003/12/13 05:43:09 bradley +Fixed non-sa_len and non-IPv6 version of GET_SA_LEN macro to cast as sockaddr to access +sa_family so it works with any sockaddr-compatible address structure (e.g. sockaddr_storage). + +Revision 1.10 2003/12/11 03:03:51 rpantos +Clean up mDNSPosix so that it builds on OS X again. + +Revision 1.9 2003/12/08 20:47:02 rpantos +Add support for mDNSResponder on Linux. + Revision 1.8 2003/08/12 19:56:26 cheshire Update to APSL 2.0 @@ -74,11 +93,11 @@ First checkin #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 +#elif HAVE_IPV6 #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)) +#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr)) #endif #define IFI_NAME 16 /* same as IFNAMSIZ in */ @@ -92,8 +111,14 @@ struct my_in_pktinfo { char ipi_ifname[IFI_NAME]; /* received interface name */ }; +/* From the text (Stevens, section 20.2): */ +/* 'As an example of recvmsg we will write a function named recvfrom_flags that */ +/* is similar to recvfrom but also returns: */ +/* 1. the returned msg_flags value, */ +/* 2. the destination addres of the received datagram (from the IP_RECVDSTADDR socket option, and */ +/* 3. the index of the interface on which the datagram was received (the IP_RECVIF socket option).' */ 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 sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl); struct ifi_info { char ifi_name[IFI_NAME]; /* interface name, null terminated */ @@ -110,7 +135,14 @@ struct ifi_info { #define IFI_ALIAS 1 /* ifi_addr is an alias */ +/* From the text (Stevens, section 16.6): */ +/* 'Since many programs need to know all the interfaces on a system, we will develop a */ +/* function of our own named get_ifi_info that returns a linked list of structures, one */ +/* for each interface that is currently "up."' */ extern struct ifi_info *get_ifi_info(int family, int doaliases); + +/* 'The free_ifi_info function, which takes a pointer that was */ +/* returned by get_ifi_info and frees all the dynamic memory.' */ extern void free_ifi_info(struct ifi_info *); #ifdef __cplusplus diff --git a/mDNSPosix/mdnsd.sh b/mDNSPosix/mdnsd.sh new file mode 100644 index 0000000..92be941 --- /dev/null +++ b/mDNSPosix/mdnsd.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# +# Linux /etc/init.d script to start/stop the mdnsd daemon. +# 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@ +# +# $Log: mdnsd.sh,v $ +# Revision 1.4 2004/02/05 20:23:10 cheshire +# Fix mdnsd.sh to work on *BSD distributions +# +# +# Revision 1.3 2004/01/19 22:47:17 cheshire +# Define killprocterm() to do "killproc $1 -TERM" for Linux +# +# Revision 1.2 2003/12/11 19:42:13 cheshire +# Change name "mDNSResponderd" to "mdnsd" for consistency with standard Linux (Unix) naming conventions +# +# Revision 1.1 2003/12/08 20:47:02 rpantos +# Add support for mDNSResponder on Linux. +# +# The following lines are used by the *BSD rcorder system to decide +# the order it's going to run the rc.d scripts at startup time. +# PROVIDE: mdnsd +# REQUIRE: NETWORKING + +if [ -r /usr/sbin/mdnsd ]; then + DAEMON=/usr/sbin/mdnsd +else + DAEMON=/usr/local/sbin/mdnsd +fi + +test -r $DAEMON || exit 0 + +# Some systems have start-stop-daemon, some don't. +if [ -r /sbin/start-stop-daemon ]; then + START=start-stop-daemon --start --quiet --exec + STOP=start-stop-daemon --stop -s TERM --quiet --oknodo --exec +else + killmdnsd() { + kill -TERM `cat /var/run/mdnsd.pid` + } + START= + STOP=killmdnsd +fi + +case "$1" in + start) + echo -n "Starting Apple Darwin Multicast DNS / DNS Service Discovery daemon:" + echo -n " mdnsd" + $START $DAEMON + echo "." + ;; + stop) + echo -n "Stopping Apple Darwin Multicast DNS / DNS Service Discovery daemon:" + echo -n " mdnsd" ; $STOP $DAEMON + echo "." + ;; + reload|restart|force-reload) + echo -n "Restarting Apple Darwin Multicast DNS / DNS Service Discovery daemon:" + $STOP $DAEMON + sleep 1 + $START $DAEMON + echo -n " mdnsd" + ;; + *) + echo "Usage: /etc/init.d/mDNS {start|stop|reload|restart}" + exit 1 + ;; +esac + +exit 0 diff --git a/mDNSShared/GenLinkedList.c b/mDNSShared/GenLinkedList.c new file mode 100755 index 0000000..04a1581 --- /dev/null +++ b/mDNSShared/GenLinkedList.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + File: GenLinkedList.c + + Contains: implementation of generic linked lists. + + Version: 1.0 + Tabs: 4 spaces + + Change History (most recent first): + +$Log: GenLinkedList.c,v $ +Revision 1.3 2004/04/22 21:14:42 cheshire +Fix comment spelling mistake + +Revision 1.2 2004/02/05 07:41:08 cheshire +Add Log header + +*/ + +#include "GenLinkedList.h" + + +// Return the link pointer contained within element e at offset o. +#define GETLINK( e, o) ( *(void**)((char*) (e) + (o)) ) + +// Assign the link pointer l to element e at offset o. +#define ASSIGNLINK( e, l, o) ( *((void**)((char*) (e) + (o))) = (l)) + + +// GenLinkedList ///////////////////////////////////////////////////////////// + +void InitLinkedList( GenLinkedList *pList, size_t linkOffset) +/* Initialize the block of memory pointed to by pList as a linked list. */ +{ + pList->Head = NULL; + pList->Tail = NULL; + pList->LinkOffset = linkOffset; +} + + +void AddToTail( GenLinkedList *pList, void *elem) +/* Add a linked list element to the tail of the list. */ +{ + if ( pList->Tail) { + ASSIGNLINK( pList->Tail, elem, pList->LinkOffset); + } else + pList->Head = elem; + ASSIGNLINK( elem, NULL, pList->LinkOffset); + + pList->Tail = elem; +} + + +void AddToHead( GenLinkedList *pList, void *elem) +/* Add a linked list element to the head of the list. */ +{ + ASSIGNLINK( elem, pList->Head, pList->LinkOffset); + if ( pList->Tail == NULL) + pList->Tail = elem; + + pList->Head = elem; +} + + +int RemoveFromList( GenLinkedList *pList, void *elem) +/* Remove a linked list element from the list. Return 0 if it was not found. */ +/* If the element is removed, its link will be set to NULL. */ +{ +void *iElem, *lastElem; + + for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) { + if ( iElem == elem) { + if ( lastElem) { // somewhere past the head + ASSIGNLINK( lastElem, GETLINK( elem, pList->LinkOffset), pList->LinkOffset); + } else { // at the head + pList->Head = GETLINK( elem, pList->LinkOffset); + } + if ( pList->Tail == elem) + pList->Tail = lastElem ? lastElem : NULL; + ASSIGNLINK( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug. + return 1; + } + lastElem = iElem; + } + + return 0; +} + + +int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem) +/* Replace an element in the list with a new element, in the same position. */ +{ +void *iElem, *lastElem; + + if ( elemInList == NULL || newElem == NULL) + return 0; + + for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) + { + if ( iElem == elemInList) + { + ASSIGNLINK( newElem, GETLINK( elemInList, pList->LinkOffset), pList->LinkOffset); + if ( lastElem) // somewhere past the head + { + ASSIGNLINK( lastElem, newElem, pList->LinkOffset); + } + else // at the head + { + pList->Head = newElem; + } + if ( pList->Tail == elemInList) + pList->Tail = newElem; + return 1; + } + lastElem = iElem; + } + + return 0; +} + + +// GenDoubleLinkedList ///////////////////////////////////////////////////////// + +void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, + size_t backLinkOffset) +/* Initialize the block of memory pointed to by pList as a double linked list. */ +{ + pList->Head = NULL; + pList->Tail = NULL; + pList->FwdLinkOffset = fwdLinkOffset; + pList->BackLinkOffset = backLinkOffset; +} + + +void DLLAddToHead( GenDoubleLinkedList *pList, void *elem) +/* Add a linked list element to the head of the list. */ +{ +void *pNext; + + pNext = pList->Head; + + // fix up the forward links + ASSIGNLINK( elem, pList->Head, pList->FwdLinkOffset); + pList->Head = elem; + + // fix up the backward links + if ( pNext) { + ASSIGNLINK( pNext, elem, pList->BackLinkOffset); + } else + pList->Tail = elem; + ASSIGNLINK( elem, NULL, pList->BackLinkOffset); +} + + +void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem) +/* Remove a linked list element from the list. */ +/* When the element is removed, its link will be set to NULL. */ +{ +void *pNext, *pPrev; + + pNext = GETLINK( elem, pList->FwdLinkOffset); + pPrev = GETLINK( elem, pList->BackLinkOffset); + + // fix up the forward links + if ( pPrev) + ASSIGNLINK( pPrev, pNext, pList->FwdLinkOffset); + else + pList->Head = pNext; + + // fix up the backward links + if ( pNext) + ASSIGNLINK( pNext, pPrev, pList->BackLinkOffset); + else + pList->Tail = pPrev; + + ASSIGNLINK( elem, NULL, pList->FwdLinkOffset); + ASSIGNLINK( elem, NULL, pList->BackLinkOffset); +} + + +// GenLinkedOffsetList ///////////////////////////////////////////////////// + +// Extract the Next offset from element +#define GETOFFSET( e, o) ( *(size_t*)((char*) (e) + (o)) ) + +static void AssignOffsetLink( void *elem, void *link, size_t linkOffset); + + +static void AssignOffsetLink( void *elem, void *link, size_t linkOffset) +// Assign link to elem as an offset from elem. Assign 0 to elem if link is NULL. +{ + GETOFFSET( elem, linkOffset) = link ? (size_t) link - (size_t) elem : 0; +} + + +void *GetHeadPtr( GenLinkedOffsetList *pList) +/* Return a pointer to the head element of a list, or NULL if none. */ +{ + return pList->Head ? ( (char*) (pList) + pList->Head) : NULL; +} + + +void *GetTailPtr( GenLinkedOffsetList *pList) +/* Return a pointer to the tail element of a list, or NULL if none. */ +{ + return pList->Tail ? ( (char*) (pList) + pList->Tail) : NULL; +} + + +void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem) +/* Return the link pointer contained within element e for pList, or NULL if it is 0. */ +{ +size_t nextOffset; + + nextOffset = GETOFFSET( elem, pList->LinkOffset); + + return nextOffset ? (char*) elem + nextOffset : NULL; +} + + +void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset) +/* Initialize the block of memory pointed to by pList as a linked list. */ +{ + pList->Head = 0; + pList->Tail = 0; + pList->LinkOffset = linkOffset; +} + + +void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem) +/* Add a linked list element to the tail of the list. */ +{ + if ( pList->Tail) { + AssignOffsetLink( GetTailPtr( pList), elem, pList->LinkOffset); + } else + pList->Head = (size_t) elem - (size_t) pList; + AssignOffsetLink( elem, NULL, pList->LinkOffset); + + pList->Tail = (size_t) elem - (size_t) pList; +} + + +void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem) +/* Add a linked list element to the head of the list. */ +{ + AssignOffsetLink( elem, GetHeadPtr( pList), pList->LinkOffset); + if ( pList->Tail == 0) + pList->Tail = (size_t) elem - (size_t) pList; + + pList->Head = (size_t) elem - (size_t) pList; +} + + +int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem) +/* Remove a linked list element from the list. Return 0 if it was not found. */ +/* If the element is removed, its link will be set to NULL. */ +{ +void *iElem, *lastElem; + + for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; + iElem = GetOffsetLink( pList, iElem)) + { + if ( iElem == elem) { + if ( lastElem) { // somewhere past the head + AssignOffsetLink( lastElem, GetOffsetLink( pList, elem), pList->LinkOffset); + } else { // at the head + iElem = GetOffsetLink( pList, elem); + pList->Head = iElem ? (size_t) iElem - (size_t) pList : 0; + } + if ( GetTailPtr( pList) == elem) + pList->Tail = lastElem ? (size_t) lastElem - (size_t) pList : 0; + AssignOffsetLink( elem, NULL, pList->LinkOffset); // maybe catch a stale reference bug. + return 1; + } + lastElem = iElem; + } + + return 0; +} + + +int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem) +/* Replace an element in the list with a new element, in the same position. */ +{ +void *iElem, *lastElem; + + if ( elemInList == NULL || newElem == NULL) + return 0; + + for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; + iElem = GetOffsetLink( pList, iElem)) + { + if ( iElem == elemInList) + { + AssignOffsetLink( newElem, GetOffsetLink( pList, elemInList), pList->LinkOffset); + if ( lastElem) // somewhere past the head + { + AssignOffsetLink( lastElem, newElem, pList->LinkOffset); + } + else // at the head + { + pList->Head = (size_t) newElem - (size_t) pList; + } + if ( GetTailPtr( pList) == elemInList) + pList->Tail = (size_t) newElem - (size_t) pList; + return 1; + } + lastElem = iElem; + } + + return 0; +} + + diff --git a/mDNSShared/GenLinkedList.h b/mDNSShared/GenLinkedList.h new file mode 100755 index 0000000..46cbb23 --- /dev/null +++ b/mDNSShared/GenLinkedList.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + File: GenLinkedList.c + + Contains: interface to generic linked lists. + + Version: 1.0 + Tabs: 4 spaces + + Change History (most recent first): + +$Log: GenLinkedList.h,v $ +Revision 1.2 2004/02/05 07:41:08 cheshire +Add Log header + +*/ + +#ifndef __GenLinkedList__ +#define __GenLinkedList__ + + +#include + + +struct GenLinkedList +{ + void *Head, + *Tail; + size_t LinkOffset; +}; +typedef struct GenLinkedList GenLinkedList; + + +void InitLinkedList( GenLinkedList *pList, size_t linkOffset); + +void AddToHead( GenLinkedList *pList, void *elem); +void AddToTail( GenLinkedList *pList, void *elem); + +int RemoveFromList( GenLinkedList *pList, void *elem); + +int ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem); + + + +struct GenDoubleLinkedList +{ + void *Head, + *Tail; + size_t FwdLinkOffset, + BackLinkOffset; +}; +typedef struct GenDoubleLinkedList GenDoubleLinkedList; + + +void InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset, + size_t backLinkOffset); + +void DLLAddToHead( GenDoubleLinkedList *pList, void *elem); + +void DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem); + + + +/* A GenLinkedOffsetList is like a GenLinkedList that stores the *Next field as a signed */ +/* offset from the address of the beginning of the element, rather than as a pointer. */ + +struct GenLinkedOffsetList +{ + size_t Head, + Tail; + size_t LinkOffset; +}; +typedef struct GenLinkedOffsetList GenLinkedOffsetList; + + +void InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset); + +void *GetHeadPtr( GenLinkedOffsetList *pList); +void *GetTailPtr( GenLinkedOffsetList *pList); +void *GetOffsetLink( GenLinkedOffsetList *pList, void *elem); + +void OffsetAddToHead( GenLinkedOffsetList *pList, void *elem); +void OffsetAddToTail( GenLinkedOffsetList *pList, void *elem); + +int OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem); + +int OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem); + + +#endif // __GenLinkedList__ diff --git a/mDNSShared/Java/BaseListener.java b/mDNSShared/Java/BaseListener.java index 665263d..f4f298b 100644 --- a/mDNSShared/Java/BaseListener.java +++ b/mDNSShared/Java/BaseListener.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSShared/Java/BrowseListener.java b/mDNSShared/Java/BrowseListener.java index 5bda3ae..2664e84 100644 --- a/mDNSShared/Java/BrowseListener.java +++ b/mDNSShared/Java/BrowseListener.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSShared/Java/DNSRecord.java b/mDNSShared/Java/DNSRecord.java index 73e1626..d82ebd5 100644 --- a/mDNSShared/Java/DNSRecord.java +++ b/mDNSShared/Java/DNSRecord.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,9 +25,6 @@ Change History (most recent first): $Log: DNSRecord.java,v $ -Revision 1.2 2004/12/11 03:00:59 rpantos - Java DNSRecord API should be cleaned up - Revision 1.1 2004/04/30 16:32:34 rpantos First checked in. @@ -43,27 +42,7 @@ package com.apple.dnssd; they are shared between concurrent threads. */ -public interface DNSRecord +abstract public class DNSRecord { - /** Update a registered resource record.

- The record must either be the primary txt record of a service registered via DNSSD.register(), - or a record added to a registered service via addRecord().

- - @param flags - Currently unused, reserved for future use. -

- @param rData - The new rdata to be contained in the updated resource record. -

- @param ttl - The time to live of the updated resource record, in seconds. - */ - void update( int flags, byte[] rData, int ttl) - throws DNSSDException; - - /** Remove a registered resource record.

- */ - void remove() - throws DNSSDException; } diff --git a/mDNSShared/Java/DNSSD.java b/mDNSShared/Java/DNSSD.java index 831b4b4..79cb5c7 100644 --- a/mDNSShared/Java/DNSSD.java +++ b/mDNSShared/Java/DNSSD.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,12 +25,6 @@ Change History (most recent first): $Log: DNSSD.java,v $ -Revision 1.4 2004/12/11 03:00:59 rpantos - Java DNSRecord API should be cleaned up - -Revision 1.3 2004/11/12 03:23:08 rpantos -rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex. - Revision 1.2 2004/05/20 17:43:18 cheshire Fix invalid UTF-8 characters in file @@ -603,12 +599,12 @@ class AppleDNSSD extends DNSSD protected String _getNameForIfIndex( int ifIndex) { - return GetNameForIfIndex( ifIndex); + return null; // ••Fix me - RNP } protected int _getIfIndexForName( String ifName) { - return GetIfIndexForName( ifName); + return 0; // ••Fix me - RNP } @@ -617,10 +613,6 @@ class AppleDNSSD extends DNSSD protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, int rrclass, byte[] rdata); - protected native String GetNameForIfIndex( int ifIndex); - - protected native int GetIfIndexForName( String ifName); - protected static native int InitLibrary( int callerVersion); } @@ -723,38 +715,9 @@ class AppleResolver extends AppleService } // An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord. -class AppleDNSRecord implements DNSRecord +class AppleDNSRecord extends DNSRecord { - public AppleDNSRecord( AppleService owner) - { - fOwner = owner; - fRecord = 0; // record always starts out empty - } - - public void update( int flags, byte[] rData, int ttl) - throws DNSSDException - { - this.ThrowOnErr( this.Update( flags, rData, ttl)); - } - - public void remove() - throws DNSSDException - { - this.ThrowOnErr( this.Remove()); - } - - protected int fRecord; // Really a DNSRecord; sizeof(int) == sizeof(void*) ? - protected AppleService fOwner; - - protected void ThrowOnErr( int rc) throws DNSSDException - { - if ( rc != 0) - throw new AppleDNSSDException( rc); - } - - protected native int Update( int flags, byte[] rData, int ttl); - - protected native int Remove(); + public int fRecord; // Really a DNSRecord; sizeof(int) == sizeof(void*) ? } class AppleRegistration extends AppleService implements DNSSDRegistration @@ -772,17 +735,23 @@ class AppleRegistration extends AppleService implements DNSSDRegistration public DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl) throws DNSSDException { - AppleDNSRecord newRecord = new AppleDNSRecord( this); + AppleDNSRecord newRecord = new AppleDNSRecord(); this.ThrowOnErr( this.AddRecord( flags, rrType, rData, ttl, newRecord)); return newRecord; } - public DNSRecord getTXTRecord() + public void updateRecord( DNSRecord record, int flags, byte[] rData, int ttl) throws DNSSDException { - return new AppleDNSRecord( this); // A record with ref 0 is understood to be primary TXT record + this.ThrowOnErr( this.UpdateRecord( (AppleDNSRecord) record, flags, rData, ttl)); + } + + public void removeRecord( DNSRecord record, int flags) + throws DNSSDException + { + this.ThrowOnErr( this.RemoveRecord( (AppleDNSRecord) record, flags)); } // Sets fNativeContext. Returns non-zero on error. @@ -792,6 +761,10 @@ class AppleRegistration extends AppleService implements DNSSDRegistration // Sets fNativeContext. Returns non-zero on error. protected native int AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj); + protected native int UpdateRecord( AppleDNSRecord destObj, int flags, byte[] rData, int ttl); + + protected native int RemoveRecord( AppleDNSRecord destObj, int flags); + protected RegisterListener fClient; } diff --git a/mDNSShared/Java/DNSSDException.java b/mDNSShared/Java/DNSSDException.java index 595e405..f3ccbcb 100644 --- a/mDNSShared/Java/DNSSDException.java +++ b/mDNSShared/Java/DNSSDException.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSShared/Java/DNSSDRegistration.java b/mDNSShared/Java/DNSSDRegistration.java index 4a4942f..7be186d 100644 --- a/mDNSShared/Java/DNSSDRegistration.java +++ b/mDNSShared/Java/DNSSDRegistration.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,9 +25,6 @@ Change History (most recent first): $Log: DNSSDRegistration.java,v $ -Revision 1.2 2004/12/11 03:01:00 rpantos - Java DNSRecord API should be cleaned up - Revision 1.1 2004/04/30 16:32:34 rpantos First checked in. @@ -42,20 +41,10 @@ package com.apple.dnssd; public interface DNSSDRegistration extends DNSSDService { - /** Get a reference to the primary TXT record of a registered service.

- The record can be updated by sending it an update() message.

- -

- @return A {@link DNSRecord}. - If {@link DNSSDRegistration#stop} is called, the DNSRecord is also - invalidated and may not be used further. - */ - DNSRecord getTXTRecord() - throws DNSSDException; - /** 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 be updated or deregistered by sending it an update() or remove() message.

+ The record can later be updated or deregistered by passing the DNSRecord returned + by this function to updateRecord() or removeRecord().

@param flags Currently unused, reserved for future use. @@ -75,5 +64,37 @@ public interface DNSSDRegistration extends DNSSDService */ DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl) throws DNSSDException; + + /** Update a registered resource record.

+ The record must either be the primary txt record of a service registered via DNSSD.register(), + or a record added to a registered service via addRecord().

+ + @param record + A DNSRecord initialized by addRecord(), or null to update the + service's primary txt record. +

+ @param flags + Currently unused, reserved for future use. +

+ @param rData + The new rdata to be contained in the updated resource record. +

+ @param ttl + The time to live of the updated resource record, in seconds. + */ + void updateRecord( DNSRecord record, int flags, byte[] rData, int ttl) + throws DNSSDException; + + /** Remove a registered resource record.

+ The record must have been previously added to a service record set via via addRecord().

+ + @param record + A DNSRecord initialized by addRecord(). +

+ @param flags + Currently unused, reserved for future use. + */ + void removeRecord( DNSRecord record, int flags) + throws DNSSDException; } diff --git a/mDNSShared/Java/DNSSDService.java b/mDNSShared/Java/DNSSDService.java index 9868372..bd7611a 100644 --- a/mDNSShared/Java/DNSSDService.java +++ b/mDNSShared/Java/DNSSDService.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSShared/Java/DomainListener.java b/mDNSShared/Java/DomainListener.java index bfb319a..13fad3b 100644 --- a/mDNSShared/Java/DomainListener.java +++ b/mDNSShared/Java/DomainListener.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSShared/Java/JNISupport.c b/mDNSShared/Java/JNISupport.c index ef9940a..b57bd46 100644 --- a/mDNSShared/Java/JNISupport.c +++ b/mDNSShared/Java/JNISupport.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,30 +25,8 @@ Change History (most recent first): $Log: JNISupport.c,v $ -Revision 1.9 2004/12/11 03:01:00 rpantos - Java DNSRecord API should be cleaned up - -Revision 1.8 2004/11/30 23:51:05 cheshire -Remove double semicolons - -Revision 1.7 2004/11/23 08:12:04 shersche -Implement if_nametoindex and if_indextoname for Win32 platforms - -Revision 1.6 2004/11/23 03:41:14 cheshire -Change JNISupport.c to call if_indextoname & if_nametoindex directly. -(May require some additional glue code to work on Windows.) - -Revision 1.5 2004/11/17 17:07:44 cheshire -Updated comment about AUTO_CALLBACKS - -Revision 1.4 2004/11/12 03:23:09 rpantos -rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex. - -Revision 1.3 2004/06/18 04:44:17 rpantos -Adapt to API unification on Windows - Revision 1.2 2004/05/28 23:34:42 ksekar -: Java project build errors +: Java project doesn't build on Tiger8A132 Revision 1.1 2004/04/30 16:29:35 rpantos First checked in. @@ -54,45 +34,46 @@ First checked in. This file contains the platform support for DNSSD and related Java classes. It is used to shim through to the underlying API. + + To do: + - normalize code to eliminate USE_WIN_API */ // AUTO_CALLBACKS should be set to 1 if the underlying mDNS implementation fires response -// callbacks automatically (as in the early Windows prototypes). +// callbacks automatically (as it does on Windows). // AUTO_CALLBACKS should be set to 0 if the client must call DNSServiceProcessResult() to -// invoke response callbacks (as is true on Mac OS X, Posix, Windows, etc.). -// (Invoking callbacks automatically on a different thread sounds attractive, but while -// the client gains by not needing to add an event source to its main event loop, it loses -// by being forced to deal with concurrency and locking, which can be a bigger burden.) +// invoke response callbacks (as is true on Mac OS X). #ifndef AUTO_CALLBACKS #define AUTO_CALLBACKS 0 #endif -#if !AUTO_CALLBACKS -#ifdef _WIN32 -#include -#else //_WIN32 -#include -#include -#endif // _WIN32 -#endif // AUTO_CALLBACKS - -#include +// (Temporary, I hope - RNP) +// USE_WIN_API should be set to 1 to use the DNSSD.h API (on Windows). +// USE_WIN_API should be set to 0 to use the dns_sd.h API (on everything else). +#ifndef USE_WIN_API +#define USE_WIN_API 0 +#endif #include #include #include -#ifdef _WIN32 -#include -#include -static char * if_indextoname( DWORD ifIndex, char * nameBuff); -static DWORD if_nametoindex( const char * nameStr ); -#define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH -#else // _WIN32 -#include -#include -#endif // _WIN32 #include +#if USE_WIN_API +#include +#else +#include +#endif + +#if !USE_WIN_API +#define CALLBACK_COMPAT +#endif + +#if !AUTO_CALLBACKS +#include +#include +#endif + #include "DNSSD.java.h" // convenience definition @@ -131,6 +112,16 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv *pEnv if ( callerVersion != kInterfaceVersion) return kDNSServiceErr_Incompatible; +#if USE_WIN_API + { + DNSServiceErrorType err; + + err = DNSServiceInitialize( kDNSServiceInitializeFlagsNone, 0); + if ( err != kDNSServiceErr_NoError) + return err; + } +#endif + #if AUTO_CALLBACKS { jsize numVMs; @@ -198,7 +189,7 @@ static OpContext *NewContext( JNIEnv *pEnv, jobject owner, const char *ownerClas const char *callbackName, const char *callbackSig) // Create and initialize a new OpContext. { - OpContext *pContext = (OpContext*) malloc( sizeof *pContext); + OpContext *pContext = (OpContext*) malloc( sizeof *pContext);; if ( pContext != NULL) { @@ -312,7 +303,7 @@ JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv } -static void DNSSD_API ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, +static void CALLBACK_COMPAT ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) { @@ -377,7 +368,7 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv * } -static void DNSSD_API ServiceResolveReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, +static void CALLBACK_COMPAT ServiceResolveReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context) { @@ -462,7 +453,7 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv } -static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, +static void CALLBACK_COMPAT ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *fullname, const char *regType, const char *domain, void *context) { @@ -573,28 +564,23 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv return err; } -JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, jobject pThis, - jint flags, jbyteArray rData, jint ttl) +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_UpdateRecord( JNIEnv *pEnv, jobject pThis, + jobject destObj, jint flags, jbyteArray rData, jint ttl) { jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); - jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "I"); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "I"); + jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "I"); OpContext *pContext = NULL; DNSServiceErrorType err = kDNSServiceErr_NoError; jbyte *pBytes; jsize numBytes; DNSRecordRef recRef = NULL; - if ( ownerField != 0) - { - jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); - jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "I"); - if ( contextField != 0) - pContext = (OpContext*) (*pEnv)->GetIntField( pEnv, ownerObj, contextField); - } + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetIntField( pEnv, pThis, contextField); if ( recField != 0) - recRef = (DNSRecordRef) (*pEnv)->GetIntField( pEnv, pThis, recField); + recRef = (DNSRecordRef) (*pEnv)->GetIntField( pEnv, destObj, recField); if ( pContext == NULL || pContext->ServiceRef == NULL) return kDNSServiceErr_BadParam; @@ -609,35 +595,31 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, return err; } -JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv *pEnv, jobject pThis) +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_RemoveRecord( JNIEnv *pEnv, jobject pThis, + jobject destObj, jint flags) { jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); - jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "I"); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "I"); + jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "I"); OpContext *pContext = NULL; DNSServiceErrorType err = kDNSServiceErr_NoError; DNSRecordRef recRef = NULL; - if ( ownerField != 0) - { - jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); - jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "I"); - if ( contextField != 0) - pContext = (OpContext*) (*pEnv)->GetIntField( pEnv, ownerObj, contextField); - } + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetIntField( pEnv, pThis, contextField); if ( recField != 0) - recRef = (DNSRecordRef) (*pEnv)->GetIntField( pEnv, pThis, recField); + recRef = (DNSRecordRef) (*pEnv)->GetIntField( pEnv, destObj, recField); if ( pContext == NULL || pContext->ServiceRef == NULL) return kDNSServiceErr_BadParam; - err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0); + err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, flags); return err; } -static void DNSSD_API ServiceQueryReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, +static void CALLBACK_COMPAT ServiceQueryReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) @@ -703,7 +685,7 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv *pEnv } -static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, +static void CALLBACK_COMPAT DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context) { OpContext *pContext = (OpContext*) context; @@ -802,141 +784,5 @@ JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord( JNIEnv * SafeReleaseUTFChars( pEnv, fullName, nameStr); } -#define LOCAL_ONLY_NAME "loo" - -JNIEXPORT jstring JNICALL Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv *pEnv, jobject pThis _UNUSED, - jint ifIndex) -{ - char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE]; - - if (ifIndex != kDNSServiceInterfaceIndexLocalOnly) - p = if_indextoname( ifIndex, nameBuff ); - - return (*pEnv)->NewStringUTF( pEnv, p); -} - - -JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv *pEnv, jobject pThis _UNUSED, - jstring ifName) -{ - uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly; - const char *nameStr = SafeGetUTFChars( pEnv, ifName); - - if (strcmp(nameStr, LOCAL_ONLY_NAME)) - ifIndex = if_nametoindex( nameStr); - - SafeReleaseUTFChars( pEnv, ifName, nameStr); - - return ifIndex; -} - - -#if defined(_WIN32) -static char* -if_indextoname( DWORD ifIndex, char * nameBuff) -{ - PIP_ADAPTER_INFO pAdapterInfo = NULL; - PIP_ADAPTER_INFO pAdapter = NULL; - DWORD dwRetVal = 0; - char * ifName = NULL; - ULONG ulOutBufLen = 0; - - if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW) - { - goto exit; - } - - pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); - if (pAdapterInfo == NULL) - { - goto exit; - } - dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen ); - - if (dwRetVal != NO_ERROR) - { - goto exit; - } - - pAdapter = pAdapterInfo; - while (pAdapter) - { - if (pAdapter->Index == ifIndex) - { - // It would be better if we passed in the length of nameBuff to this - // function, so we would have absolute certainty that no buffer - // overflows would occur. Buffer overflows *shouldn't* occur because - // nameBuff is of size MAX_ADAPTER_NAME_LENGTH. - strcpy( nameBuff, pAdapter->AdapterName ); - ifName = nameBuff; - break; - } - - pAdapter = pAdapter->Next; - } - -exit: - - if (pAdapterInfo != NULL) - { - free( pAdapterInfo ); - pAdapterInfo = NULL; - } - - return ifName; -} - - -static DWORD -if_nametoindex( const char * nameStr ) -{ - PIP_ADAPTER_INFO pAdapterInfo = NULL; - PIP_ADAPTER_INFO pAdapter = NULL; - DWORD dwRetVal = 0; - DWORD ifIndex = 0; - ULONG ulOutBufLen = 0; - - if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW) - { - goto exit; - } - - pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); - - if (pAdapterInfo == NULL) - { - goto exit; - } - - dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen ); - - if (dwRetVal != NO_ERROR) - { - goto exit; - } - - pAdapter = pAdapterInfo; - while (pAdapter) - { - if (strcmp(pAdapter->AdapterName, nameStr) == 0) - { - ifIndex = pAdapter->Index; - break; - } - - pAdapter = pAdapter->Next; - } - -exit: - - if (pAdapterInfo != NULL) - { - free( pAdapterInfo ); - pAdapterInfo = NULL; - } - - return ifIndex; -} -#endif diff --git a/mDNSShared/Java/QueryListener.java b/mDNSShared/Java/QueryListener.java index 7ece74c..1a8637b 100644 --- a/mDNSShared/Java/QueryListener.java +++ b/mDNSShared/Java/QueryListener.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSShared/Java/RegisterListener.java b/mDNSShared/Java/RegisterListener.java index a6cf331..28c9fa5 100644 --- a/mDNSShared/Java/RegisterListener.java +++ b/mDNSShared/Java/RegisterListener.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSShared/Java/ResolveListener.java b/mDNSShared/Java/ResolveListener.java index 0e21353..ac606cf 100644 --- a/mDNSShared/Java/ResolveListener.java +++ b/mDNSShared/Java/ResolveListener.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSShared/Java/TXTRecord.java b/mDNSShared/Java/TXTRecord.java index 5eed93e..97bcea9 100644 --- a/mDNSShared/Java/TXTRecord.java +++ b/mDNSShared/Java/TXTRecord.java @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,15 +25,6 @@ Change History (most recent first): $Log: TXTRecord.java,v $ -Revision 1.5 2004/08/25 21:54:36 rpantos - Fix getValue() for values containing '='. - -Revision 1.4 2004/08/04 01:04:50 rpantos - Fix set(); add remove() & toString(). - -Revision 1.3 2004/07/13 21:24:25 rpantos -Fix for . - Revision 1.2 2004/04/30 21:48:27 rpantos Change line endings for CVS. @@ -49,7 +42,7 @@ package com.apple.dnssd; /** Object used to construct and parse DNS-SD format TXT records. - For more info see DNS-Based Service Discovery, section 6. + For more info see DNS-SD TXT record format. */ public class TXTRecord @@ -97,8 +90,10 @@ public class TXTRecord */ public void set( String key, byte[] value) { + byte[] oldBytes = fBytes; byte[] keyBytes; int valLen = (value != null) ? value.length : 0; + byte newLen; try { keyBytes = key.getBytes( "US-ASCII"); @@ -113,66 +108,17 @@ public class TXTRecord if ( keyBytes.length + valLen >= 255) throw new ArrayIndexOutOfBoundsException(); + newLen = (byte) ( keyBytes.length + valLen + (valLen > 0 ? 1 : 0)); - int prevLoc = this.remove( key); - if ( prevLoc == -1) - prevLoc = this.size(); - - this.insert( keyBytes, value, prevLoc); - } - - protected void insert( byte[] keyBytes, byte[] value, int index) - // Insert a key-value pair at index - { - byte[] oldBytes = fBytes; - int valLen = (value != null) ? value.length : 0; - int insertion = 0; - byte newLen, avLen; - - // locate the insertion point - for ( int i=0; i < index && insertion < fBytes.length; i++) - insertion += fBytes[ insertion] + 1; - - avLen = (byte) ( keyBytes.length + valLen + (value != null ? 1 : 0)); - newLen = (byte) ( avLen + oldBytes.length + 1); - - fBytes = new byte[ newLen]; - System.arraycopy( oldBytes, 0, fBytes, 0, insertion); - int secondHalfLen = oldBytes.length - insertion; - System.arraycopy( oldBytes, insertion, fBytes, newLen - secondHalfLen, secondHalfLen); - fBytes[ insertion] = avLen; - System.arraycopy( keyBytes, 0, fBytes, insertion + 1, keyBytes.length); - if ( value != null) - { - fBytes[ insertion + 1 + keyBytes.length] = kAttrSep; - System.arraycopy( value, 0, fBytes, insertion + keyBytes.length + 2, valLen); - } - } - - /** Remove a key/value pair from the TXT record. Returns index it was at, or -1 if not found. */ - public int remove( String key) - { - int avStart = 0; - - for ( int i=0; avStart < fBytes.length; i++) + fBytes = new byte[ oldBytes.length + newLen + 1]; + System.arraycopy( oldBytes, 0, fBytes, 0, oldBytes.length); + fBytes[ oldBytes.length] = newLen; + System.arraycopy( keyBytes, 0, fBytes, oldBytes.length + 1, keyBytes.length); + if ( valLen > 0) { - int avLen = fBytes[ avStart]; - if ( key.length() <= avLen && - ( key.length() == avLen || fBytes[ avStart + key.length() + 1] == kAttrSep)) - { - String s = new String( fBytes, avStart + 1, key.length()); - if ( 0 == key.compareToIgnoreCase( s)) - { - byte[] oldBytes = fBytes; - fBytes = new byte[ oldBytes.length - avLen - 1]; - System.arraycopy( oldBytes, 0, fBytes, 0, avStart); - System.arraycopy( oldBytes, avStart + avLen + 1, fBytes, avStart, oldBytes.length - avStart - avLen - 1); - return i; - } - } - avStart += avLen + 1; + fBytes[ oldBytes.length + 1 + keyBytes.length] = kAttrSep; + System.arraycopy( value, 0, fBytes, oldBytes.length + keyBytes.length + 2, value.length); } - return -1; } /** Return the number of keys in the TXT record. */ @@ -241,7 +187,6 @@ public class TXTRecord { value = new byte[ avLen - aLen - 1]; System.arraycopy( fBytes, avStart + aLen + 2, value, 0, avLen - aLen - 1); - break; } } } @@ -289,26 +234,5 @@ public class TXTRecord /** Return the contents of the TXT record as raw bytes. */ public byte[] getRawBytes() { return (byte[]) fBytes.clone(); } - - /** Return a string representation of the object. */ - public String toString() - { - String a, result = null; - - for ( int i=0; null != ( a = this.getKey( i)); i++) - { - String av = String.valueOf( i) + "={" + a; - String val = this.getValueAsString( i); - if ( val != null) - av += "=" + val + "}"; - else - av += "}"; - if ( result == null) - result = av; - else - result = result + ", " + av; - } - return result != null ? result : ""; - } } diff --git a/mDNSShared/dns_sd.h b/mDNSShared/dns_sd.h index 4f22f67..45f51b7 100755 --- a/mDNSShared/dns_sd.h +++ b/mDNSShared/dns_sd.h @@ -1,28 +1,84 @@ /* - * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: dns_sd.h,v $ +Revision 1.17 2004/06/01 14:34:48 cheshire +For compatibility with older compilers, change '//' comments to ' / * ... * / ' + +Revision 1.16 2004/05/25 17:08:55 cheshire +Fix compiler warning (doesn't make sense for function return type to be const) + +Revision 1.15 2004/05/21 21:41:35 cheshire +Add TXT record building and parsing APIs + +Revision 1.14 2004/05/20 18:40:31 cheshire +Remove trailing comma that breaks build on strict compilers + +Revision 1.13 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.12 2004/05/07 21:11:07 ksekar +API Update: Exposed new core error codes. Added constants for +InterfaceIndexAny and InterfaceIndexLocalOnly. Added flag for +long-lived unicast queries via DNSServiceQueryRecord. + +Revision 1.11 2004/05/07 20:51:18 ksekar +: dns_sd.h needs to direct developers to +register their services at + +Revision 1.10 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +: Remove flags with zero value +: Passing in NULL causes a crash. + +Revision 1.9 2004/03/20 05:43:39 cheshire +Fix contributed by Terry Lambert & Alfred Perlstein: +On FreeBSD 4.x we need to include instead of + +Revision 1.8 2004/03/19 17:50:40 cheshire +Clarify comment about kDNSServiceMaxDomainName + +Revision 1.7 2004/03/12 08:00:06 cheshire +Minor comment changes, headers, and wrap file in extern "C" for the benefit of C++ clients + +Revision 1.6 2003/12/04 06:24:33 cheshire +Clarify meaning of MoreComing/Finished flag + +Revision 1.5 2003/11/13 23:35:35 ksekar +: Header doesn't say that add/remove are possible values for flags +Bringing mDNSResponder project copy of dns_sd.h header up to date with +Libinfo copy + +Revision 1.4 2003/10/13 23:50:53 ksekar +Updated dns_sd clientstub files to bring copies in synch with +top-of-tree Libinfo: A memory leak in dnssd_clientstub.c is fixed, +and comments in dns_sd.h are improved. + +Revision 1.3 2003/08/12 19:51:51 cheshire +Update to APSL 2.0 */ #ifndef _DNS_SD_H @@ -32,28 +88,9 @@ extern "C" { #endif -/* standard calling convention under Win32 is __stdcall */ -#if defined(_WIN32) -#define DNSSD_API __stdcall -#else -#define DNSSD_API -#endif - -#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000) +#if defined(__FreeBSD__) && (__FreeBSD_version < 500000) /* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */ #include -#elif defined(__sun__) -#include -#elif defined(_WIN32) -#include -#define _UNUSED -#define bzero(a, b) memset(a, 0, b) -typedef UINT8 uint8_t; -typedef INT8 int8_t; -typedef UINT16 uint16_t; -typedef INT16 int16_t; -typedef UINT32 uint32_t; -typedef INT32 int32_t; #else #include #endif @@ -100,7 +137,7 @@ enum * by renaming the service. NoAutoRename overrides this behavior - with this * flag set, name conflicts will result in a callback. The NoAutorename flag * is only valid if a name is explicitly specified when registering a service - * (i.e. the default name is not used.) + * (ie the default name is not used.) */ kDNSServiceFlagsShared = 0x10, @@ -118,89 +155,10 @@ enum * enumerates domains recommended for registration. */ - kDNSServiceFlagsLongLivedQuery = 0x100, + kDNSServiceFlagsLongLivedQuery = 0x100 /* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */ - - kDNSServiceFlagsAllowRemoteQuery = 0x200, - /* Flag for creating a record for which we will answer remote queries - * (queries from hosts more than one hop away; hosts not directly connected to the local link). - */ - - kDNSServiceFlagsForceMulticast = 0x400 - /* Flag for signifying that a query or registration should be performed exclusively via multicast DNS, - * even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS. - */ - }; - -/* - * The values for DNS Classes and Types are listed in RFC 1035, and are available - * on every OS in its DNS header file. Unfortunately every OS does not have the - * same header file containing DNS Class and Type constants, and the names of - * the constants are not consistent. For example, BIND 8 uses "T_A", - * BIND 9 uses "ns_t_a", Windows uses "DNS_TYPE_A", etc. - * For this reason, these constants are also listed here, so that code using - * the DNS-SD programming APIs can use these constants, so that the same code - * can compile on all our supported platforms. - */ - -enum - { - kDNSServiceClass_IN = 1 /* Internet */ - }; - -enum - { - kDNSServiceType_A = 1, /* Host address. */ - kDNSServiceType_NS = 2, /* Authoritative server. */ - kDNSServiceType_MD = 3, /* Mail destination. */ - kDNSServiceType_MF = 4, /* Mail forwarder. */ - kDNSServiceType_CNAME = 5, /* Canonical name. */ - kDNSServiceType_SOA = 6, /* Start of authority zone. */ - kDNSServiceType_MB = 7, /* Mailbox domain name. */ - kDNSServiceType_MG = 8, /* Mail group member. */ - kDNSServiceType_MR = 9, /* Mail rename name. */ - kDNSServiceType_NULL = 10, /* Null resource record. */ - kDNSServiceType_WKS = 11, /* Well known service. */ - kDNSServiceType_PTR = 12, /* Domain name pointer. */ - kDNSServiceType_HINFO = 13, /* Host information. */ - kDNSServiceType_MINFO = 14, /* Mailbox information. */ - kDNSServiceType_MX = 15, /* Mail routing information. */ - kDNSServiceType_TXT = 16, /* Text strings. */ - kDNSServiceType_RP = 17, /* Responsible person. */ - kDNSServiceType_AFSDB = 18, /* AFS cell database. */ - kDNSServiceType_X25 = 19, /* X_25 calling address. */ - kDNSServiceType_ISDN = 20, /* ISDN calling address. */ - kDNSServiceType_RT = 21, /* Router. */ - kDNSServiceType_NSAP = 22, /* NSAP address. */ - kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ - kDNSServiceType_SIG = 24, /* Security signature. */ - kDNSServiceType_KEY = 25, /* Security key. */ - kDNSServiceType_PX = 26, /* X.400 mail mapping. */ - kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */ - kDNSServiceType_AAAA = 28, /* Ip6 Address. */ - kDNSServiceType_LOC = 29, /* Location Information. */ - kDNSServiceType_NXT = 30, /* Next domain (security). */ - kDNSServiceType_EID = 31, /* Endpoint identifier. */ - kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */ - kDNSServiceType_SRV = 33, /* Server Selection. */ - kDNSServiceType_ATMA = 34, /* ATM Address */ - kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */ - kDNSServiceType_KX = 36, /* Key Exchange */ - kDNSServiceType_CERT = 37, /* Certification record */ - kDNSServiceType_A6 = 38, /* IPv6 address (deprecates AAAA) */ - kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ - kDNSServiceType_SINK = 40, /* Kitchen sink (experimentatl) */ - kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */ - kDNSServiceType_TKEY = 249, /* Transaction key */ - kDNSServiceType_TSIG = 250, /* Transaction signature. */ - kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */ - kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ - kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ - kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ - kDNSServiceType_ANY = 255 /* Wildcard match. */ }; - /* possible error code values */ enum { @@ -217,16 +175,12 @@ enum kDNSServiceErr_AlreadyRegistered = -65547, kDNSServiceErr_NameConflict = -65548, kDNSServiceErr_Invalid = -65549, - kDNSServiceErr_Firewall = -65550, kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */ kDNSServiceErr_BadInterfaceIndex = -65552, kDNSServiceErr_Refused = -65553, kDNSServiceErr_NoSuchRecord = -65554, kDNSServiceErr_NoAuth = -65555, - kDNSServiceErr_NoSuchKey = -65556, - kDNSServiceErr_NATTraversal = -65557, - kDNSServiceErr_DoubleNAT = -65558, - kDNSServiceErr_BadTime = -65559 + kDNSServiceErr_NoSuchKey = -65556 /* mDNS Error codes are in the range * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ }; @@ -237,84 +191,13 @@ enum #define kDNSServiceMaxDomainName 1005 -/* - * Notes on DNS Name Escaping - * -- or -- - * "Why is kDNSServiceMaxDomainName 1005, when the maximum legal domain name is 255 bytes?" - * - * All strings used in DNS-SD are UTF-8 strings. - * With few exceptions, most are also escaped using standard DNS escaping rules: - * - * '\\' represents a single literal '\' in the name - * '\.' represents a single literal '.' in the name - * '\ddd', where ddd is a three-digit decimal value from 000 to 255, - * represents a single literal byte with that value. - * A bare unescaped '.' is a label separator, marking a boundary between domain and subdomain. - * - * The exceptions, that do not use escaping, are the routines where the full - * DNS name of a resource is broken, for convenience, into servicename/regtype/domain. - * In these routines, the "servicename" is NOT escaped. It does not need to be, since - * it is, by definition, just a single literal string. Any characters in that string - * represent exactly what they are. The "regtype" portion is, technically speaking, - * escaped, but since legal regtypes are only allowed to contain letters, digits, - * and hyphens, the issue is moot. The "domain" portion is also escaped, though - * most domains in use on the public Internet today, like regtypes, don't contain - * any characters that need to be escaped. As DNS-SD becomes more popular, rich-text - * domains for service discovery will become common, so software should be written - * to cope with domains with escaping. - * - * For most software, these issues are transparent. When browsing, the discovered - * servicenames should simply be displayed as-is. When resolving, the discovered - * servicename/regtype/domain are simply passed unchanged to DNSServiceResolve(). - * When a DNSServiceResolve() succeeds, the returned fullname is already in - * the correct format to pass to standard system DNS APIs such as res_query(). - * For converting from servicename/regtype/domain to a single properly-escaped - * full DNS name, the helper function DNSServiceConstructFullName() is provided. - * - * The following (highly contrived) example illustrates the escaping process. - * Suppose you have an service called "Dr. Smith\Dr. Johnson", of type "_ftp._tcp" - * in subdomain "4th. Floor" of subdomain "Building 2" of domain "apple.com." - * The full (escaped) DNS name of this service's SRV record would be: - * Dr\.\032Smith\\Dr\.\032Johnson._ftp._tcp.4th\.\032Floor.Building\0322.apple.com. - */ - - -/* - * Constants for specifying an interface index - * - * Specific interface indexes are identified via a 32-bit unsigned integer returned - * by the if_nametoindex() family of calls. - * - * If the client passes 0 for interface index, that means "do the right thing", - * which (at present) means, "if the name is in an mDNS local multicast domain - * (e.g. 'local.', '254.169.in-addr.arpa.', '0.8.E.F.ip6.arpa.') then multicast - * on all applicable interfaces, otherwise send via unicast to the appropriate - * DNS server." Normally, most clients will use 0 for interface index to - * automatically get the default sensible behaviour. - * - * If the client passes a positive interface index, then for multicast names that - * indicates to do the operation only on that one interface. For unicast names the - * interface index is ignored unless kDNSServiceFlagsForceMulticast is also set. - * - * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering - * a service, then that service will be found *only* by other local clients - * on the same machine that are browsing using kDNSServiceInterfaceIndexLocalOnly - * or kDNSServiceInterfaceIndexAny. - * If a client has a 'private' service, accessible only to other processes - * running on the same machine, this allows the client to advertise that service - * in a way such that it does not inadvertently appear in service lists on - * all the other machines on the network. - * - * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing - * then it will find *all* records registered on that same local machine. - * Clients explicitly wishing to discover *only* LocalOnly services can - * accomplish this by inspecting the interfaceIndex of each service reported - * to their DNSServiceBrowseReply() callback function, and discarding those - * where the interface index is not kDNSServiceInterfaceIndexLocalOnly. +/* Constants for specifying an interface index. Specific interface indexes are + * identified via a 32-bit unsigned integer returned by the if_nametoindex() + * family of calls */ #define kDNSServiceInterfaceIndexAny 0 -#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) -1 ) +#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) ~0 ) typedef uint32_t DNSServiceFlags; @@ -349,7 +232,7 @@ typedef int32_t DNSServiceErrorType; * error. */ -int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); +int DNSServiceRefSockFD(DNSServiceRef sdRef); /* DNSServiceProcessResult() @@ -370,7 +253,7 @@ int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); * an error code indicating the specific failure that occurred. */ -DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); +DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef); /* DNSServiceRefDeallocate() @@ -399,7 +282,7 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); * */ -void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); +void DNSServiceRefDeallocate(DNSServiceRef sdRef); /********************************************************************************************* @@ -411,16 +294,11 @@ void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); /* 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. * - * Note that the names returned are (like all of DNS-SD) UTF-8 strings, - * and are escaped using standard DNS escaping rules. - * (See "Notes on DNS Name Escaping" earlier in this file for more details.) - * A graphical browser displaying a hierarchical tree-structured view should cut - * the names at the bare dots to yield individual labels, then de-escape each - * label according to the escaping rules, and then display the resulting UTF-8 text. * * DNSServiceDomainEnumReply Callback Parameters: * @@ -443,7 +321,7 @@ void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); * */ -typedef void (DNSSD_API *DNSServiceDomainEnumReply) +typedef void (*DNSServiceDomainEnumReply) ( DNSServiceRef sdRef, DNSServiceFlags flags, @@ -457,10 +335,8 @@ typedef void (DNSSD_API *DNSServiceDomainEnumReply) /* DNSServiceEnumerateDomains() Parameters: * * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the enumeration operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef. May be passed to + * DNSServiceRefDeallocate() to cancel the enumeration. * * flags: Possible values are: * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing. @@ -470,7 +346,7 @@ typedef void (DNSSD_API *DNSServiceDomainEnumReply) * 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. See "Constants for specifying an interface index" for more details. + * all interfaces. * * callBack: The function to be called when a domain is found or the call asynchronously * fails. @@ -484,7 +360,7 @@ typedef void (DNSSD_API *DNSServiceDomainEnumReply) * is not initialized.) */ -DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains +DNSServiceErrorType DNSServiceEnumerateDomains ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -527,7 +403,7 @@ DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains * */ -typedef void (DNSSD_API *DNSServiceRegisterReply) +typedef void (*DNSServiceRegisterReply) ( DNSServiceRef sdRef, DNSServiceFlags flags, @@ -541,15 +417,15 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) /* DNSServiceRegister() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the registration will remain active indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef. 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. See "Constants for specifying an interface index" for more details. + * 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. @@ -602,7 +478,7 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * */ -DNSServiceErrorType DNSSD_API DNSServiceRegister +DNSServiceErrorType DNSServiceRegister ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -638,19 +514,19 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister * * flags: Currently ignored, reserved for future use. * - * rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc) + * 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. Pass 0 to use a default value. + * 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 DNSSD_API DNSServiceAddRecord +DNSServiceErrorType DNSServiceAddRecord ( DNSServiceRef sdRef, DNSRecordRef *RecordRef, @@ -690,7 +566,7 @@ DNSServiceErrorType DNSSD_API DNSServiceAddRecord * error code indicating the error that occurred. */ -DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord +DNSServiceErrorType DNSServiceUpdateRecord ( DNSServiceRef sdRef, DNSRecordRef RecordRef, /* may be NULL */ @@ -722,7 +598,7 @@ DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord * error code indicating the error that occurred. */ -DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord +DNSServiceErrorType DNSServiceRemoveRecord ( DNSServiceRef sdRef, DNSRecordRef RecordRef, @@ -753,29 +629,19 @@ DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord * indicate the failure that occurred. Other parameters are undefined if * the errorCode is nonzero. * - * serviceName: The discovered service name. This name should be displayed to the user, - * and stored for subsequent use in the DNSServiceResolve() call. + * serviceName: The service name discovered. * - * regtype: The service type, which is usually (but not always) the same as was passed - * to DNSServiceBrowse(). One case where the discovered service type may - * not be the same as the requested service type is when using subtypes: - * The client may want to browse for only those ftp servers that allow - * anonymous connections. The client will pass the string "_ftp._tcp,_anon" - * to DNSServiceBrowse(), but the type of the service that's discovered - * is simply "_ftp._tcp". The regtype for each discovered service instance - * should be stored along with the name, so that it can be passed to - * DNSServiceResolve() when the service is later resolved. + * regtype: The service type, as passed in to DNSServiceBrowse(). * - * domain: The domain of the discovered service instance. This may or may not be the - * same as the domain that was passed to DNSServiceBrowse(). The domain for each - * discovered service instance should be stored along with the name, so that - * it can be passed to DNSServiceResolve() when the service is later resolved. + * 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 (DNSSD_API *DNSServiceBrowseReply) +typedef void (*DNSServiceBrowseReply) ( DNSServiceRef sdRef, DNSServiceFlags flags, @@ -790,17 +656,15 @@ typedef void (DNSSD_API *DNSServiceBrowseReply) /* DNSServiceBrowse() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the browse operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef. 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. See "Constants for specifying an interface index" for more details. + * 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". @@ -821,7 +685,7 @@ typedef void (DNSSD_API *DNSServiceBrowseReply) * is not initialized.) */ -DNSServiceErrorType DNSSD_API DNSServiceBrowse +DNSServiceErrorType DNSServiceBrowse ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -861,10 +725,12 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse * the errorCode is nonzero. * * fullname: The full service domain name, in the form ... - * (This name is escaped following standard DNS rules, making it suitable for - * passing to standard system DNS APIs such as res_query(), or to the - * special-purpose functions included in this API that take fullname parameters. - * See "Notes on DNS Name Escaping" earlier in this file for more details.) + * (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. @@ -880,7 +746,7 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse * */ -typedef void (DNSSD_API *DNSServiceResolveReply) +typedef void (*DNSServiceResolveReply) ( DNSServiceRef sdRef, DNSServiceFlags flags, @@ -897,29 +763,23 @@ typedef void (DNSSD_API *DNSServiceResolveReply) /* DNSServiceResolve() Parameters * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the resolve operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef. 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. If this resolve call is - * as a result of a currently active DNSServiceBrowse() operation, then the - * interfaceIndex should be the index reported in the DNSServiceBrowseReply - * callback. If this resolve call is using information previously saved - * (e.g. in a preference file) for later use, then use interfaceIndex 0, because - * the desired service may now be reachable via a different physical interface. - * See "Constants for specifying an interface index" for more details. + * 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 name of the service instance to be resolved, as reported to the - * DNSServiceBrowseReply() callback. + * name: The servicename to be resolved. * - * regtype: The type of the service instance to be resolved, as reported to the - * DNSServiceBrowseReply() callback. + * 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 of the service instance to be resolved, as reported to the - * DNSServiceBrowseReply() callback. + * 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. @@ -933,7 +793,7 @@ typedef void (DNSSD_API *DNSServiceResolveReply) * is not initialized.) */ -DNSServiceErrorType DNSSD_API DNSServiceResolve +DNSServiceErrorType DNSServiceResolve ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -952,6 +812,28 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve * *********************************************************************************************/ +/* Note on DNS Naming Conventions: + * + * The functions below refer to resource records by their full domain name, unlike the + * functions above which divide the name into servicename/regtype/domain fields. In the + * functions above, 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 calls, 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.) The function DNSServiceConstructFullName() is provided + * to aid in this conversion from servicename/regtype/domain to a single fully-qualified DNS + * name with proper escaping. + */ + /* DNSServiceCreateConnection() * * Create a connection to the daemon allowing efficient registration of @@ -969,7 +851,7 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve * case the DNSServiceRef is not initialized). */ -DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); +DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef); /* DNSServiceRegisterRecord @@ -999,7 +881,7 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); * */ - typedef void (DNSSD_API *DNSServiceRegisterRecordReply) + typedef void (*DNSServiceRegisterRecordReply) ( DNSServiceRef sdRef, DNSRecordRef RecordRef, @@ -1025,19 +907,21 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); * 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. - * See "Constants for specifying an interface index" for more details. + * 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. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * 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 (usually kDNSServiceClass_IN) + * 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. Pass 0 to use a default value. + * 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.) @@ -1051,7 +935,7 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); * not initialized.) */ -DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord +DNSServiceErrorType DNSServiceRegisterRecord ( DNSServiceRef sdRef, DNSRecordRef *RecordRef, @@ -1083,7 +967,6 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord * * interfaceIndex: The interface on which the query was resolved (the index for a given * interface is determined via the if_nametoindex() family of calls). - * See "Constants for specifying an interface index" for more details. * * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will * indicate the failure that occurred. Other parameters are undefined if @@ -1091,9 +974,9 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord * * fullname: The resource record's full domain name. * - * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. * - * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * rrclass: The class of the resource record, as defined in nameser.h (usually 1). * * rdlen: The length, in bytes, of the resource record rdata. * @@ -1105,7 +988,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord * */ -typedef void (DNSSD_API *DNSServiceQueryRecordReply) +typedef void (*DNSServiceQueryRecordReply) ( DNSServiceRef DNSServiceRef, DNSServiceFlags flags, @@ -1123,10 +1006,7 @@ typedef void (DNSSD_API *DNSServiceQueryRecordReply) /* DNSServiceQueryRecord() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the query operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * sdRef: A pointer to an uninitialized DNSServiceRef. * * flags: Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast * query in a non-local domain. Without setting this flag, unicast queries @@ -1138,14 +1018,16 @@ typedef void (DNSSD_API *DNSServiceQueryRecordReply) * 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. See "Constants for specifying an interface index" for more details. + * 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. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * 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 (usually kDNSServiceClass_IN). + * 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. @@ -1159,7 +1041,7 @@ typedef void (DNSSD_API *DNSServiceQueryRecordReply) * is not initialized.) */ -DNSServiceErrorType DNSSD_API DNSServiceQueryRecord +DNSServiceErrorType DNSServiceQueryRecord ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -1183,14 +1065,11 @@ DNSServiceErrorType DNSSD_API DNSServiceQueryRecord * * flags: Currently unused, reserved for future use. * - * interfaceIndex: If non-zero, specifies the interface of the record in question. - * Passing 0 causes all instances of this record to be reconfirmed. - * * fullname: The resource record's full domain name. * - * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. * - * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * rrclass: The class of the resource record, as defined in nameser.h (usually 1). * * rdlen: The length, in bytes, of the resource record rdata. * @@ -1198,7 +1077,7 @@ DNSServiceErrorType DNSSD_API DNSServiceQueryRecord * */ -void DNSSD_API DNSServiceReconfirmRecord +void DNSServiceReconfirmRecord ( DNSServiceFlags flags, uint32_t interfaceIndex, @@ -1228,21 +1107,21 @@ void DNSSD_API DNSServiceReconfirmRecord * The buffer must be kDNSServiceMaxDomainName (1005) bytes in length to * accommodate the longest legal domain name without buffer overrun. * - * service: The service name - any dots or backslashes must NOT be escaped. + * 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."). + * "_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.". Literal dots or backslashes, - * if any, must be escaped, e.g. "1st\. Floor.apple.com." + * 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 DNSSD_API DNSServiceConstructFullName +int DNSServiceConstructFullName ( char *fullName, const char *service, /* may be NULL */ @@ -1278,7 +1157,7 @@ int DNSSD_API DNSServiceConstructFullName * Note: Represents a DNS-SD TXT record. */ -typedef struct _TXTRecordRef_t { char privatedata[16]; } TXTRecordRef; +typedef struct _TXTRecordRef_t { char private[16]; } TXTRecordRef; /* TXTRecordCreate() @@ -1308,11 +1187,6 @@ typedef struct _TXTRecordRef_t { char privatedata[16]; } TXTRecordRef; * Recommended size limits for DNS-SD TXT Records are discussed in * * - * Note: When passing parameters to and from these TXT record APIs, - * the key name does not include the '=' character. The '=' character - * is the separator between the key and value in the on-the-wire - * packet format; it is not part of either the key or the value. - * * txtRecord: A pointer to an uninitialized TXTRecordRef. * * bufferLen: The size of the storage provided in the "buffer" parameter. @@ -1322,7 +1196,7 @@ typedef struct _TXTRecordRef_t { char privatedata[16]; } TXTRecordRef; * the TXTRecordRef. */ -void DNSSD_API TXTRecordCreate +void TXTRecordCreate ( TXTRecordRef *txtRecord, uint16_t bufferLen, @@ -1340,7 +1214,7 @@ void DNSSD_API TXTRecordCreate * */ -void DNSSD_API TXTRecordDeallocate +void TXTRecordDeallocate ( TXTRecordRef *txtRecord ); @@ -1363,7 +1237,7 @@ void DNSSD_API TXTRecordDeallocate * * key: A null-terminated string which only contains printable ASCII * values (0x20-0x7E), excluding '=' (0x3D). Keys should be - * 8 characters or less (not counting the terminating null). + * 14 characters or less (not counting the terminating null). * * valueSize: The size of the value. * @@ -1383,7 +1257,7 @@ void DNSSD_API TXTRecordDeallocate * exceed the available storage. */ -DNSServiceErrorType DNSSD_API TXTRecordSetValue +DNSServiceErrorType TXTRecordSetValue ( TXTRecordRef *txtRecord, const char *key, @@ -1407,7 +1281,7 @@ DNSServiceErrorType DNSSD_API TXTRecordSetValue * */ -DNSServiceErrorType DNSSD_API TXTRecordRemoveValue +DNSServiceErrorType TXTRecordRemoveValue ( TXTRecordRef *txtRecord, const char *key @@ -1427,7 +1301,7 @@ DNSServiceErrorType DNSSD_API TXTRecordRemoveValue * */ -uint16_t DNSSD_API TXTRecordGetLength +uint16_t TXTRecordGetLength ( const TXTRecordRef *txtRecord ); @@ -1445,7 +1319,7 @@ uint16_t DNSSD_API TXTRecordGetLength * */ -const void * DNSSD_API TXTRecordGetBytesPtr +const void * TXTRecordGetBytesPtr ( const TXTRecordRef *txtRecord ); @@ -1500,7 +1374,7 @@ const void * DNSSD_API TXTRecordGetBytesPtr * */ -int DNSSD_API TXTRecordContainsKey +int TXTRecordContainsKey ( uint16_t txtLen, const void *txtRecord, @@ -1529,7 +1403,7 @@ int DNSSD_API TXTRecordContainsKey * For non-empty value, valueLen will be length of value data. */ -const void * DNSSD_API TXTRecordGetValuePtr +const void * TXTRecordGetValuePtr ( uint16_t txtLen, const void *txtRecord, @@ -1551,7 +1425,7 @@ const void * DNSSD_API TXTRecordGetValuePtr * */ -uint16_t DNSSD_API TXTRecordGetCount +uint16_t TXTRecordGetCount ( uint16_t txtLen, const void *txtRecord @@ -1582,7 +1456,7 @@ uint16_t DNSSD_API TXTRecordGetCount * key: A string buffer used to store the key name. * On return, the buffer contains a null-terminated C string * giving the key name. DNS-SD TXT keys are usually - * 8 characters or less. To hold the maximum possible + * 14 characters or less. To hold the maximum possible * key name, the buffer should be 256 bytes long. * * valueLen: On output, will be set to the size of the "value" data. @@ -1596,7 +1470,7 @@ uint16_t DNSSD_API TXTRecordGetCount * TXTRecordGetCount()-1. */ -DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex +DNSServiceErrorType TXTRecordGetItemAtIndex ( uint16_t txtLen, const void *txtRecord, @@ -1607,39 +1481,6 @@ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex const void **value ); -#ifdef __APPLE_API_PRIVATE - -/* - * Mac OS X specific functionality - * 3rd party clients of this API should not depend on future support or availability of this routine - */ - -/* DNSServiceSetDefaultDomainForUser() - * - * Set the default domain for the caller's UID. Future browse and registration - * calls by this user that do not specify an explicit domain will browse and - * register in this wide-area domain in addition to .local. In addition, this - * domain will be returned as a Browse domain via domain enumeration calls. - * - * - * Parameters: - * - * flags: Pass kDNSServiceFlagsAdd to add a domain for a user. Call without - * this flag set to clear a previously added domain. - * - * domain: The domain to be used for the caller's UID. - * - * return value: Returns kDNSServiceErr_NoError on succeses, otherwise returns - * an error code indicating the error that occurred - */ - -DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser - ( - DNSServiceFlags flags, - const char *domain - ); - -#endif //__APPLE_API_PRIVATE #ifdef __cplusplus } diff --git a/mDNSShared/dnssd_clientlib.c b/mDNSShared/dnssd_clientlib.c new file mode 100755 index 0000000..d5a0f04 --- /dev/null +++ b/mDNSShared/dnssd_clientlib.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: dnssd_clientlib.c,v $ +Revision 1.5 2004/05/25 18:29:33 cheshire +Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c, +so that it's also accessible to dnssd_clientshim.c (single address space) clients. + +Revision 1.4 2004/05/25 17:08:55 cheshire +Fix compiler warning (doesn't make sense for function return type to be const) + +Revision 1.3 2004/05/21 21:41:35 cheshire +Add TXT record building and parsing APIs + +Revision 1.2 2004/05/20 22:22:21 cheshire +Enable code that was bracketed by "#if 0" + +Revision 1.1 2004/03/12 21:30:29 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + + */ + +#include +#include + +#include "dns_sd.h" + +#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY +#pragma export on +#endif + +/********************************************************************************************* + * + * Supporting Functions + * + *********************************************************************************************/ + +#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') + +static int DomainEndsInDot(const char *dom) + { + while (dom[0] && dom[1]) + { + if (dom[0] == '\\') // advance past escaped byte sequence + { + if (mdnsIsDigit(dom[1]) && mdnsIsDigit(dom[2]) && mdnsIsDigit(dom[3])) + dom += 4; // If "\ddd" then skip four + else dom += 2; // else if "\x" then skip two + } + else dom++; // else goto next character + } + return (dom[0] == '.'); + } + +static uint8_t *InternalTXTRecordSearch + ( + uint16_t txtLen, + const void *txtRecord, + const char *key, + unsigned long *keylen + ) + { + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + *keylen = strlen(key); + while (pbuffer = buffer; + txtRec->buflen = buffer ? bufferLen : (uint16_t)0; + txtRec->datalen = 0; + txtRec->malloced = 0; + } + +void TXTRecordDeallocate(TXTRecordRef *txtRecord) + { + if (txtRec->malloced) free(txtRec->buffer); + } + +DNSServiceErrorType TXTRecordSetValue + ( + TXTRecordRef *txtRecord, + const char *key, + uint8_t valueSize, + const void *value + ) + { + uint8_t *start, *p; + const char *k; + unsigned long keysize, keyvalsize; + + for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid); + keysize = (unsigned long)(k - key); + keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0); + if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid); + (void)TXTRecordRemoveValue(txtRecord, key); + if (txtRec->datalen + keyvalsize > txtRec->buflen) + { + unsigned char *newbuf; + unsigned long newlen = txtRec->datalen + keyvalsize; + if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid); + newbuf = malloc((size_t)newlen); + if (!newbuf) return(kDNSServiceErr_NoMemory); + memcpy(newbuf, txtRec->buffer, txtRec->datalen); + if (txtRec->malloced) free(txtRec->buffer); + txtRec->buffer = newbuf; + txtRec->buflen = (uint16_t)(newlen); + txtRec->malloced = 1; + } + start = txtRec->buffer + txtRec->datalen; + p = start + 1; + memcpy(p, key, keysize); + p += keysize; + if (value) + { + *p++ = '='; + memcpy(p, value, valueSize); + p += valueSize; + } + *start = (uint8_t)(p - start - 1); + txtRec->datalen += p - start; + return(kDNSServiceErr_NoError); + } + +DNSServiceErrorType TXTRecordRemoveValue + ( + TXTRecordRef *txtRecord, + const char *key + ) + { + unsigned long keylen, itemlen, remainder; + uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen); + if (!item) return(kDNSServiceErr_NoSuchKey); + itemlen = (unsigned long)(1 + item[0]); + remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen)); + // Use memmove because memcpy behaviour is undefined for overlapping regions + memmove(item, item + itemlen, remainder); + txtRec->datalen -= itemlen; + return(kDNSServiceErr_NoError); + } + +uint16_t TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); } +const void * TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); } + +/********************************************************************************************* + * + * TXT Record Parsing Functions + * + *********************************************************************************************/ + +int TXTRecordContainsKey + ( + uint16_t txtLen, + const void *txtRecord, + const char *key + ) + { + unsigned long keylen; + return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0); + } + +const void * TXTRecordGetValuePtr + ( + uint16_t txtLen, + const void *txtRecord, + const char *key, + uint8_t *valueLen + ) + { + unsigned long keylen; + uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen); + if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL + *valueLen = (uint8_t)(item[0] - (keylen + 1)); + return (item + 1 + keylen + 1); + } + +uint16_t TXTRecordGetCount + ( + uint16_t txtLen, + const void *txtRecord + ) + { + uint16_t count = 0; + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + while (pe) ? (uint16_t)0 : count); + } + +DNSServiceErrorType TXTRecordGetItemAtIndex + ( + uint16_t txtLen, + const void *txtRecord, + uint16_t index, + uint16_t keyBufLen, + char *key, + uint8_t *valueLen, + const void **value + ) + { + uint16_t count = 0; + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + while (p= keyBufLen) return(kDNSServiceErr_NoMemory); + memcpy(key, x, len); + key[len] = 0; + if (x+lendisposefn(op); + } + +//************************************************************************************************************* +// Domain Enumeration + +// Not yet implemented, so don't include in stub library +// We DO include it in the actual Extension, so that if a later client compiled to use this +// is run against this Extension, it will get a reasonable error code instead of just +// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link) +#if !MDNS_BUILDINGSTUBLIBRARY +DNSServiceErrorType DNSServiceEnumerateDomains + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callback, + void *context /* may be NULL */ + ) + { + (void)sdRef; // Unused + (void)flags; // Unused + (void)interfaceIndex; // Unused + (void)callback; // Unused + (void)context; // Unused + return(kDNSServiceErr_Unsupported); + } +#endif + +//************************************************************************************************************* +// Register Service + +mDNSlocal void FreeDNSServiceRegistration(mDNS_DirectOP_Register *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) + mDNSPlatformMemFree(extras->r.resrec.rdata); + mDNSPlatformMemFree(extras); + } + + if (x->s.RR_TXT.resrec.rdata != &x->s.RR_TXT.rdatastorage) + mDNSPlatformMemFree(x->s.RR_TXT.resrec.rdata); + + if (x->s.SubTypes) mDNSPlatformMemFree(x->s.SubTypes); + + mDNSPlatformMemFree(x); + } + +static void DNSServiceRegisterDispose(mDNS_DirectOP *op) + { + mDNS_DirectOP_Register *x = (mDNS_DirectOP_Register*)op; + x->autorename = mDNSfalse; + // 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); + } + +mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) + { + mDNS_DirectOP_Register *x = (mDNS_DirectOP_Register*)sr->ServiceContext; + + domainlabel name; + domainname type, dom; + char namestr[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL. + char typestr[MAX_ESCAPED_DOMAIN_NAME]; + char domstr [MAX_ESCAPED_DOMAIN_NAME]; + if (!DeconstructServiceName(&sr->RR_SRV.resrec.name, &name, &type, &dom)) return; + if (!ConvertDomainLabelToCString_unescaped(&name, namestr)) return; + if (!ConvertDomainNameToCString(&type, typestr)) return; + if (!ConvertDomainNameToCString(&dom, domstr)) return; + + if (result == mStatus_NoError) + { + if (x->callback) + x->callback((DNSServiceRef)x, 0, result, namestr, typestr, domstr, x->context); + } + else if (result == mStatus_NameConflict) + { + if (x->autoname) mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + else if (x->callback) + x->callback((DNSServiceRef)x, 0, result, namestr, typestr, domstr, x->context); + } + else if (result == mStatus_MemFree) + { + if (x->autorename) + { + x->autorename = mDNSfalse; + x->name = mDNSStorage.nicelabel; + mDNS_RenameAndReregisterService(m, &x->s, &x->name); + } + else + FreeDNSServiceRegistration(x); + } + } + +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 notAnIntPort, + uint16_t txtLen, + const void *txtRecord, /* may be NULL */ + DNSServiceRegisterReply callback, /* may be NULL */ + void *context /* may be NULL */ + ) + { + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + domainlabel n; + domainname t, d, h, srv; + mDNSIPPort port; + unsigned int size = sizeof(RDataBody); + AuthRecord *SubTypes = mDNSNULL; + mDNSu32 NumSubTypes = 0; + mDNS_DirectOP_Register *x; + (void)flags; // Unused + (void)interfaceIndex; // Unused + + // Check parameters + if (!name[0]) n = mDNSStorage.nicelabel; + else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } + if (!regtype || !*regtype || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + if (!MakeDomainNameFromDNSNameString(&d, (domain && *domain) ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; } + if (!MakeDomainNameFromDNSNameString(&h, (host && *host ) ? host : "")) { errormsg = "Bad Target Host"; goto badparam; } + if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } + port.NotAnInteger = notAnIntPort; + + // Allocate memory, and handle failure + if (size < txtLen) + size = txtLen; + x = (mDNS_DirectOP_Register *)mDNSPlatformMemAllocate(sizeof(*x) - sizeof(RDataBody) + size); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object + x->disposefn = DNSServiceRegisterDispose; + x->callback = callback; + x->context = context; + x->autoname = (!name[0]); + x->autorename = mDNSfalse; + x->name = n; + + // Do the operation + err = mDNS_RegisterService(&mDNSStorage, &x->s, + &x->name, &t, &d, // Name, type, domain + &h, port, // Host and port + txtRecord, txtLen, // TXT data, length + SubTypes, NumSubTypes, // Subtypes + mDNSInterface_Any, // Interface ID + RegCallback, x); // Callback and context + if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_RegisterService"; goto fail; } + + // Succeeded: Wrap up and return + *sdRef = (DNSServiceRef)x; + return(mStatus_NoError); + +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", regtype, domain, errormsg, err); + return(err); + } + +//************************************************************************************************************* +// Add / Update / Remove records from existing Registration + +// Not yet implemented, so don't include in stub library +// We DO include it in the actual Extension, so that if a later client compiled to use this +// is run against this Extension, it will get a reasonable error code instead of just +// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link) +#if !MDNS_BUILDINGSTUBLIBRARY +DNSServiceErrorType DNSServiceAddRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ) + { + (void)sdRef; // Unused + (void)RecordRef; // Unused + (void)flags; // Unused + (void)rrtype; // Unused + (void)rdlen; // Unused + (void)rdata; // Unused + (void)ttl; // Unused + return(kDNSServiceErr_Unsupported); + } + +DNSServiceErrorType DNSServiceUpdateRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, /* may be NULL */ + DNSServiceFlags flags, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ) + { + (void)sdRef; // Unused + (void)RecordRef; // Unused + (void)flags; // Unused + (void)rdlen; // Unused + (void)rdata; // Unused + (void)ttl; // Unused + return(kDNSServiceErr_Unsupported); + } + +DNSServiceErrorType DNSServiceRemoveRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags + ) + { + (void)sdRef; // Unused + (void)RecordRef; // Unused + (void)flags; // Unused + return(kDNSServiceErr_Unsupported); + } +#endif + +//************************************************************************************************************* +// Browse for services + +static void DNSServiceBrowseDispose(mDNS_DirectOP *op) + { + mDNS_DirectOP_Browse *x = (mDNS_DirectOP_Browse*)op; + //LogMsg("DNSServiceBrowseDispose"); + mDNS_StopBrowse(&mDNSStorage, &x->q); + mDNSPlatformMemFree(x); + } + +mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : (DNSServiceFlags)0; + domainlabel name; + domainname type, domain; + char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL. + char ctype[MAX_ESCAPED_DOMAIN_NAME]; + char cdom [MAX_ESCAPED_DOMAIN_NAME]; + mDNS_DirectOP_Browse *x = (mDNS_DirectOP_Browse*)question->QuestionContext; + (void)m; // Unused + + if (answer->rrtype != kDNSType_PTR) + { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; } + + 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; + } + + ConvertDomainLabelToCString_unescaped(&name, cname); + ConvertDomainNameToCString(&type, ctype); + ConvertDomainNameToCString(&domain, cdom); + if (x->callback) + x->callback((DNSServiceRef)x, flags, 0, 0, cname, ctype, cdom, x->context); + } + +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 */ + ) + { + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + domainname t, d; + mDNS_DirectOP_Browse *x; + (void)flags; // Unused + (void)interfaceIndex; // Unused + + // Check parameters + 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 + x = (mDNS_DirectOP_Browse *)mDNSPlatformMemAllocate(sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object + x->disposefn = DNSServiceBrowseDispose; + x->callback = callback; + x->context = context; + x->q.QuestionContext = x; + + // Do the operation + err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSInterface_Any, FoundInstance, x); + if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_StartBrowse"; goto fail; } + + // Succeeded: Wrap up and return + *sdRef = (DNSServiceRef)x; + return(mStatus_NoError); + +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", regtype, domain, errormsg, err); + return(err); + } + +//************************************************************************************************************* +// Resolve Service Info + +static void DNSServiceResolveDispose(mDNS_DirectOP *op) + { + mDNS_DirectOP_Resolve *x = (mDNS_DirectOP_Resolve*)op; + if (x->qSRV.ThisQInterval >= 0) mDNS_StopQuery(&mDNSStorage, &x->qSRV); + if (x->qTXT.ThisQInterval >= 0) mDNS_StopQuery(&mDNSStorage, &x->qTXT); + mDNSPlatformMemFree(x); + } + +mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + mDNS_DirectOP_Resolve *x = (mDNS_DirectOP_Resolve*)question->QuestionContext; + (void)m; // Unused + if (!AddRecord) + { + if (answer->rrtype == kDNSType_SRV && x->SRV == answer) x->SRV = mDNSNULL; + if (answer->rrtype == kDNSType_TXT && x->TXT == answer) x->TXT = mDNSNULL; + } + else + { + if (answer->rrtype == kDNSType_SRV) x->SRV = answer; + if (answer->rrtype == kDNSType_TXT) x->TXT = answer; + if (x->SRV && x->TXT && x->callback) + { + char fullname[MAX_ESCAPED_DOMAIN_NAME], targethost[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(&answer->name, fullname); + ConvertDomainNameToCString(&x->SRV->rdata->u.srv.target, targethost); + x->callback((DNSServiceRef)x, 0, 0, kDNSServiceErr_NoError, fullname, targethost, + x->SRV->rdata->u.srv.port.NotAnInteger, x->TXT->rdlength, (char*)x->TXT->rdata->u.txt.c, x->context); + } + } + } + +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 */ + ) + { + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + domainlabel n; + domainname t, d, srv; + mDNS_DirectOP_Resolve *x; + + (void)flags; // Unused + (void)interfaceIndex; // Unused + + // Check parameters + 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 (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain )) { errormsg = "Bad Domain"; goto badparam; } + if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } + + // Allocate memory, and handle failure + x = (mDNS_DirectOP_Resolve *)mDNSPlatformMemAllocate(sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object + x->disposefn = DNSServiceResolveDispose; + x->callback = callback; + x->context = context; + x->SRV = mDNSNULL; + x->TXT = mDNSNULL; + + x->qSRV.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question + x->qSRV.InterfaceID = mDNSInterface_Any; + x->qSRV.Target = zeroAddr; + AssignDomainName(x->qSRV.qname, srv); + x->qSRV.qtype = kDNSType_SRV; + x->qSRV.qclass = kDNSClass_IN; + x->qSRV.QuestionCallback = FoundServiceInfo; + x->qSRV.QuestionContext = x; + + x->qTXT.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question + x->qTXT.InterfaceID = mDNSInterface_Any; + x->qTXT.Target = zeroAddr; + AssignDomainName(x->qTXT.qname, srv); + x->qTXT.qtype = kDNSType_TXT; + x->qTXT.qclass = kDNSClass_IN; + x->qTXT.QuestionCallback = FoundServiceInfo; + x->qTXT.QuestionContext = x; + + err = mDNS_StartQuery(&mDNSStorage, &x->qSRV); + if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery qSRV"; goto fail; } + err = mDNS_StartQuery(&mDNSStorage, &x->qTXT); + if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery qTXT"; goto fail; } + + // Succeeded: Wrap up and return + *sdRef = (DNSServiceRef)x; + return(mStatus_NoError); + +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", name, regtype, domain, errormsg, err); + return(err); + } + +//************************************************************************************************************* +// Connection-oriented calls + +// Not yet implemented, so don't include in stub library +// We DO include it in the actual Extension, so that if a later client compiled to use this +// is run against this Extension, it will get a reasonable error code instead of just +// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link) +#if !MDNS_BUILDINGSTUBLIBRARY +DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef) + { + (void)sdRef; // Unused + return(kDNSServiceErr_Unsupported); + } + +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 */ + ) + { + (void)sdRef; // Unused + (void)RecordRef; // Unused + (void)flags; // Unused + (void)interfaceIndex; // Unused + (void)fullname; // Unused + (void)rrtype; // Unused + (void)rrclass; // Unused + (void)rdlen; // Unused + (void)rdata; // Unused + (void)ttl; // Unused + (void)callback; // Unused + (void)context; // Unused + return(kDNSServiceErr_Unsupported); + } +#endif + +//************************************************************************************************************* +// DNSServiceQueryRecord + +static void DNSServiceQueryRecordDispose(mDNS_DirectOP *op) + { + mDNS_DirectOP_QueryRecord *x = (mDNS_DirectOP_QueryRecord*)op; + if (x->q.ThisQInterval >= 0) mDNS_StopQuery(&mDNSStorage, &x->q); + mDNSPlatformMemFree(x); + } + +mDNSlocal void DNSServiceQueryRecordResponse(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + mDNS_DirectOP_QueryRecord *x = (mDNS_DirectOP_QueryRecord*)question->QuestionContext; + char fullname[MAX_ESCAPED_DOMAIN_NAME]; + (void)m; // Unused + ConvertDomainNameToCString(&answer->name, fullname); + x->callback((DNSServiceRef)x, AddRecord ? kDNSServiceFlagsAdd : (DNSServiceFlags)0, 0, kDNSServiceErr_NoError, + fullname, answer->rrtype, answer->rrclass, answer->rdlength, answer->rdata->u.data, answer->rroriginalttl, x->context); + } + +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 */ + ) + { + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + mDNS_DirectOP_QueryRecord *x; + + (void)flags; // Unused + (void)interfaceIndex; // Unused + + // Allocate memory, and handle failure + x = (mDNS_DirectOP_QueryRecord *)mDNSPlatformMemAllocate(sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object + x->disposefn = DNSServiceQueryRecordDispose; + x->callback = callback; + x->context = context; + + x->q.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question + x->q.InterfaceID = mDNSInterface_Any; + x->q.Target = zeroAddr; + MakeDomainNameFromDNSNameString(&x->q.qname, fullname); + x->q.qtype = rrtype; + x->q.qclass = rrclass; + x->q.QuestionCallback = DNSServiceQueryRecordResponse; + x->q.QuestionContext = x; + + err = mDNS_StartQuery(&mDNSStorage, &x->q); + if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery"; goto fail; } + + // Succeeded: Wrap up and return + *sdRef = (DNSServiceRef)x; + return(mStatus_NoError); + +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("DNSServiceQueryRecord(\"%s\", %d, %d) failed: %s (%ld)", fullname, rrtype, rrclass, errormsg, err); + return(err); + } + +//************************************************************************************************************* +// DNSServiceReconfirmRecord + +// Not yet implemented, so don't include in stub library +// We DO include it in the actual Extension, so that if a later client compiled to use this +// is run against this Extension, it will get a reasonable error code instead of just +// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link) +#if !MDNS_BUILDINGSTUBLIBRARY +void DNSServiceReconfirmRecord + ( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata + ) + { + (void)flags; // Unused + (void)interfaceIndex; // Unused + (void)fullname; // Unused + (void)rrtype; // Unused + (void)rrclass; // Unused + (void)rdlen; // Unused + (void)rdata; // Unused + } +#endif diff --git a/mDNSShared/dnssd_clientstub.c b/mDNSShared/dnssd_clientstub.c index 7277eda..6173ca7 100755 --- a/mDNSShared/dnssd_clientstub.c +++ b/mDNSShared/dnssd_clientstub.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,45 @@ Change History (most recent first): $Log: dnssd_clientstub.c,v $ +Revision 1.19 2004/05/25 18:29:33 cheshire +Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c, +so that it's also accessible to dnssd_clientshim.c (single address space) clients. + +Revision 1.18 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.17 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +: Remove flags with zero value +: Passing in NULL causes a crash. + +Revision 1.16 2004/03/12 22:00:37 cheshire +Added: #include + +Revision 1.15 2004/01/20 18:36:29 ksekar +Propagated Libinfo fix for : SU: +DNSServiceUpdateRecord() doesn't allow you to update the TXT record +into TOT mDNSResponder. + +Revision 1.14 2004/01/19 22:39:17 cheshire +Don't use "MSG_WAITALL"; it makes send() return "Invalid argument" on Linux; +use an explicit while() loop instead. (In any case, this should only make a difference +with non-blocking sockets, which we don't use on the client side right now.) + +Revision 1.13 2004/01/19 21:46:52 cheshire +Fix compiler warning + +Revision 1.12 2003/12/23 20:46:47 ksekar +: sync dnssd files between libinfo & mDNSResponder + +Revision 1.11 2003/12/08 21:11:42 rpantos +Changes necessary to support mDNSResponder on Linux. + +Revision 1.10 2003/10/13 23:50:53 ksekar +Updated dns_sd clientstub files to bring copies in synch with +top-of-tree Libinfo: A memory leak in dnssd_clientstub.c is fixed, +and comments in dns_sd.h are improved. + Revision 1.9 2003/08/15 21:30:39 cheshire Bring up to date with LibInfo version @@ -34,8 +75,13 @@ Update to APSL 2.0 */ +#include +#include +#include + #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 @@ -46,7 +92,6 @@ 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); @@ -63,7 +108,7 @@ typedef struct _DNSServiceRef_t void *app_callback; void *app_context; uint32_t max_index; //largest assigned record index - 0 if no additl. recs registered - } _DNSServiceRef_t; + } _DNSServiceRef_t; typedef struct _DNSRecordRef_t { @@ -102,7 +147,8 @@ DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef) if (my_read(sdRef->sockfd, data, hdr.datalen) < 0) return kDNSServiceErr_Unknown; sdRef->process_reply(sdRef, &hdr, data); - return kDNSServiceErr_Unknown; + free(data); + return kDNSServiceErr_NoError; } @@ -116,14 +162,14 @@ void DNSServiceRefDeallocate(DNSServiceRef sdRef) DNSServiceErrorType DNSServiceResolve ( - DNSServiceRef *sdRef, + DNSServiceRef *sdRef, const DNSServiceFlags flags, const uint32_t interfaceIndex, - const char *name, - const char *regtype, - const char *domain, + const char *name, + const char *regtype, + const char *domain, const DNSServiceResolveReply callBack, - void *context + void *context ) { char *msg = NULL, *ptr; @@ -135,6 +181,8 @@ DNSServiceErrorType DNSServiceResolve if (!sdRef) return kDNSServiceErr_BadParam; *sdRef = NULL; + if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; + // calculate total message length len = sizeof(flags); len += sizeof(interfaceIndex); @@ -184,18 +232,19 @@ static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *d uint32_t ifi; DNSServiceErrorType err; char *txtrecord; - - (void)hdr; //unused + int str_error = 0; + (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); + if (get_string(&data, fullname, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, target, kDNSServiceMaxDomainName) < 0) str_error = 1; port = get_short(&data); txtlen = get_short(&data); txtrecord = get_rdata(&data, txtlen); - + + if (!err && str_error) err = kDNSServiceErr_Unknown; ((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port, txtlen, txtrecord, sdr->app_context); } @@ -204,14 +253,14 @@ static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *d 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 + 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; @@ -269,23 +318,25 @@ static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *dat DNSServiceFlags flags; uint32_t interfaceIndex, ttl; DNSServiceErrorType errorCode; - char name[256]; + char name[kDNSServiceMaxDomainName]; uint16_t rrtype, rrclass, rdlen; char *rdata; + int str_error = 0; (void)hdr;//Unused flags = get_flags(&data); interfaceIndex = get_long(&data); errorCode = get_error_code(&data); - (get_string(&data, name, 256) < 0); + if (get_string(&data, name, kDNSServiceMaxDomainName) < 0) str_error = 1; 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); + ttl = get_long(&data); + + if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown; + ((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass, + rdlen, rdata, ttl, sdr->app_context); return; } @@ -353,16 +404,19 @@ static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *da DNSServiceFlags flags; uint32_t interfaceIndex; DNSServiceErrorType errorCode; - char replyName[256], replyType[256], replyDomain[256]; - (void)hdr;//Unused + char replyName[256], replyType[kDNSServiceMaxDomainName], + replyDomain[kDNSServiceMaxDomainName]; + int str_error = 0; + (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); + if (get_string(&data, replyName, 256) < 0) str_error = 1; + if (get_string(&data, replyType, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, replyDomain, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown; + ((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context); } @@ -443,7 +497,7 @@ DNSServiceErrorType DNSServiceRegister error: if (msg) free(msg); - if (*sdRef) { free(*sdRef); *sdRef = NULL; } + if (*sdRef) { free(*sdRef); *sdRef = NULL; } return kDNSServiceErr_Unknown; } @@ -453,15 +507,17 @@ static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char DNSServiceFlags flags; uint32_t interfaceIndex; DNSServiceErrorType errorCode; - char name[256], regtype[256], domain[256]; - (void)hdr;//Unused + char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName]; + int str_error = 0; + (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); + if (get_string(&data, name, 256) < 0) str_error = 1; + if (get_string(&data, regtype, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown; ((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context); } @@ -484,7 +540,10 @@ DNSServiceErrorType DNSServiceEnumerateDomains if (!sdRef) return kDNSServiceErr_BadParam; *sdRef = NULL; - len = sizeof(DNSServiceFlags); + if (flags != kDNSServiceFlagsBrowseDomains && flags != kDNSServiceFlagsRegistrationDomains) + return kDNSServiceErr_BadParam; + + len = sizeof(DNSServiceFlags); len += sizeof(uint32_t); hdr = create_hdr(enumeration_request, &len, &ptr, 1); @@ -522,13 +581,15 @@ static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, cha DNSServiceFlags flags; uint32_t interfaceIndex; DNSServiceErrorType err; - char domain[256]; - (void)hdr;//Unused + char domain[kDNSServiceMaxDomainName]; + int str_error = 0; + (void)hdr;//Unused flags = get_flags(&data); interfaceIndex = get_long(&data); err = get_error_code(&data); - get_string(&data, domain, 256); + if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (!err && str_error) err = kDNSServiceErr_Unknown; ((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context); } @@ -567,18 +628,18 @@ static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char 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 + 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; @@ -591,7 +652,10 @@ DNSServiceErrorType DNSServiceRegisterRecord return kDNSServiceErr_BadReference; *RecordRef = NULL; - len = sizeof(DNSServiceFlags); + if (flags != kDNSServiceFlagsShared && flags != kDNSServiceFlagsUnique) + return kDNSServiceErr_BadReference; + + len = sizeof(DNSServiceFlags); len += 2 * sizeof(uint32_t); // interfaceIndex, ttl len += 3 * sizeof(uint16_t); // rrtype, rrclass, rdlen len += strlen(fullname) + 1; @@ -631,13 +695,13 @@ error: //sdRef returned by DNSServiceRegister() DNSServiceErrorType DNSServiceAddRecord ( - const DNSServiceRef sdRef, - DNSRecordRef *RecordRef, + const DNSServiceRef sdRef, + DNSRecordRef *RecordRef, const DNSServiceFlags flags, - const uint16_t rrtype, - const uint16_t rdlen, - const void *rdata, - const uint32_t ttl + const uint16_t rrtype, + const uint16_t rdlen, + const void *rdata, + const uint32_t ttl ) { ipc_msg_hdr *hdr; @@ -684,20 +748,19 @@ error: //DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord DNSServiceErrorType DNSServiceUpdateRecord ( - const DNSServiceRef sdRef, - DNSRecordRef RecordRef, + const DNSServiceRef sdRef, + DNSRecordRef RecordRef, const DNSServiceFlags flags, - const uint16_t rdlen, - const void *rdata, - const uint32_t ttl + 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; + if (!sdRef) return kDNSServiceErr_BadReference; len += sizeof(uint16_t); len += rdlen; @@ -719,7 +782,7 @@ DNSServiceErrorType DNSServiceUpdateRecord DNSServiceErrorType DNSServiceRemoveRecord ( const DNSServiceRef sdRef, - const DNSRecordRef RecordRef, + const DNSRecordRef RecordRef, const DNSServiceFlags flags ) { @@ -780,74 +843,6 @@ void DNSServiceReconfirmRecord } -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) { @@ -858,19 +853,19 @@ static DNSServiceRef connect_to_server(void) 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; + } + return sdr; } @@ -878,7 +873,13 @@ static DNSServiceRef connect_to_server(void) int my_write(int sd, char *buf, int len) { - if (send(sd, buf, len, MSG_WAITALL) != len) return -1; + while (len) + { + ssize_t num_written = send(sd, buf, len, 0); + if (num_written < 0 || num_written > len) return -1; + buf += num_written; + len -= num_written; + } return 0; } @@ -910,15 +911,18 @@ DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd) bzero(&caddr, sizeof(caddr)); caddr.sun_family = AF_LOCAL; - caddr.sun_len = sizeof(struct sockaddr_un); - path = (char *)msg + sizeof(ipc_msg_hdr); +#ifndef NOT_HAVE_SA_LEN // According to Stevens (section 3.2), there is no portable way to + // determine whether sa_len is defined on a particular platform. + caddr.sun_len = sizeof(struct sockaddr_un); +#endif + 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); + { + umask(mask); goto cleanup; - } + } umask(mask); listen(listenfd, 1); } @@ -943,7 +947,7 @@ DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd) } cleanup: if (!reuse_sd && listenfd > 0) close(listenfd); - if (!reuse_sd && errsd > 0) close(errsd); + if (!reuse_sd && errsd > 0) close(errsd); if (!reuse_sd && path) unlink(path); if (msg) free(msg); return err; @@ -971,10 +975,9 @@ static ipc_msg_hdr *create_hdr(int op, int *len, char **data_start, int reuse_so 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); - + if (gettimeofday(&time, NULL) < 0) return NULL; + sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), + (unsigned long)(time.tv_sec & 0xFFF), (unsigned long)(time.tv_usec)); *len += strlen(ctrl_path) + 1; } diff --git a/mDNSShared/dnssd_ipc.c b/mDNSShared/dnssd_ipc.c index 186f4d3..9f509a4 100644 --- a/mDNSShared/dnssd_ipc.c +++ b/mDNSShared/dnssd_ipc.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,13 @@ Change History (most recent first): $Log: dnssd_ipc.c,v $ +Revision 1.9 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.8 2003/11/05 22:44:57 ksekar +: No bounds checking when reading data from client +Reviewed by: Stuart Cheshire + Revision 1.7 2003/08/12 19:56:25 cheshire Update to APSL 2.0 @@ -100,7 +109,6 @@ int put_string(const char *str, char **ptr) 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; diff --git a/mDNSShared/dnssd_ipc.h b/mDNSShared/dnssd_ipc.h index 2b7b323..4585284 100644 --- a/mDNSShared/dnssd_ipc.h +++ b/mDNSShared/dnssd_ipc.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSShared/mDNS.1 b/mDNSShared/mDNS.1 new file mode 100644 index 0000000..be51457 --- /dev/null +++ b/mDNSShared/mDNS.1 @@ -0,0 +1,184 @@ +.\" Copyright (c) 2004 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@ +.\" +.\" $Log: mDNS.1,v $ +.\" Revision 1.3 2004/05/19 00:31:28 cheshire +.\" Add missing "name type domain" for -L option +.\" +.\" Revision 1.2 2004/05/18 18:58:29 cheshire +.\" Refinements from Soren Spies +.\" +.\" Revision 1.1 2004/04/22 02:52:53 cheshire +.\" : mDNSResponder missing man pages: mDNS +.\" +.\" +.\" +.Dd April 2004 \" Date +.Dt mDNS 1 \" Document Title +.Os Darwin \" Operating System +.\" +.Sh NAME +.Nm mDNS +.Nd Multicast DNS Service Discovery (mDNS-SD) Test Tool \" For whatis +.\" +.Sh SYNOPSIS +.Nm Fl R Ar name type domain port Op Ar key=value ... +.Pp +.Nm Fl B Ar type domain +.Pp +.Nm Fl L Ar name type domain +.\" +.Sh DESCRIPTION +The +.Nm +command is a network diagnostic tool, much like +.Xr ping 8 or +.Xr traceroute 8 . +Unlike those tools, most of its functionality is implemented in library +code that is available to any application. The API that it uses is +described in +.Pa /usr/include/dns_sd.h . +.Pp +The +.Nm +command is primarily intended for interactive use. +Because its command-line arguments and output format are subject to change, +invoking it from a shell script will generally be fragile. Additionally, +the asynchronous nature of Multicast DNS Service Discovery does +not lend itself easily to script-oriented programming. For example, +calls like "browse" never complete; the action of performing a "browse" +sets in motion machinery to notify the client whenever instances of +that service type appear or disappear from the network. These +notifications continue to be delivered indefinitely, for minutes, +hours, or even days, as services come and go, until the client +explicitly terminates the call. This style of asynchronous interaction +works best with applications that are either multi-threaded, or use a +main event-handling loop to receive keystrokes, network data, and other +asynchronous event notifications as they happen. +.Pp +.Bl -tag -width R +.It Fl R Ar name type domain port Op Ar key=value ... +register (advertise) a service in the specified +.Ar domain +with the given +.Ar name +and +.Ar type +as listening (on the current machine) on +.Ar port. +.Pp +.Ar name +can be arbitrary unicode text, containing any legal unicode characters +(including dots, spaces, slashes, colons, etc. without restriction), +up to 63 UTF-8 bytes long. +.Ar type +must be of the form "_app-proto._tcp" or "_app-proto._udp", where +"app-proto" is an application protocol name registered at +.Pa http://www.dns-sd.org/ServiceTypes.html . +.Pp +.Ar domain +is the domain in which to register the service. +In current implementations, only the local multicast domain "local" is +supported. In the future, registering will be supported in any arbitrary +domain that has a working DNS Update server [RFC 2136]. The +.Ar domain +"." is a synonym for "pick a sensible default" which today +means "local". +.Pp +.Ar port +is a number from 0 to 65535, and is the TCP or UDP port number upon +which the service is listening. +.Pp +Additional attributes of the service may optionally be described by +key/value pairs, which are stored in the advertised service's DNS TXT +record. Allowable keys and values are listed with the service +registration at +.Pa http://www.dns-sd.org/ServiceTypes.html . +.It Fl B Ar type domain +browse for instances of service +.Ar type +in +.Ar domain . +.Pp +For valid +.Ar type Ns s +see +.Pa http://www.dns-sd.org/ServiceTypes.html +as described above. Omitting the +.Ar domain +or using "." means "pick a sensible default." +.It Fl L Ar name type domain +look up and display the information necessary to contact and use the +named service: the hostname of the machine where that service is +available, the port number on which the service is listening, and (if +present) TXT record attributes describing properties of the service. +.Pp +Note that in a typical application, browsing happens rarely, while lookup +(or "resolving") happens every time the service is used. For example, a +user browses the network to pick a default printer fairly rarely, but once +a default printer has been picked, that named service is resolved to its +current IP address and port number every time the user presses Cmd-P to +print. +.El +.Sh EXAMPLES +.Pp +To advertise the existence of LPR printing service on port 515 on this +machine, such that it will be discovered by the Mac OS X printing software +and other mDNS-SD compatible printing clients, use: +.Pp +.Dl mDNS -R \&"My Test\&" _printer._tcp. \&. 515 pdl=application/postscript +.Pp +For this registration to be useful, you need to actually have LPR service +available on port 515. Advertising a service that does not exist is not +very useful, and will be confusing and annoying to other people on the +network. +.Pp +Similarly, to advertise a web page being served by an HTTP +server on port 80 on this machine, such that it will show up in the +Rendezvous list in Safari and other mDNS-SD compatible Web clients, use: +.Pp +.Dl mDNS -R \&"My Test\&" _http._tcp \&. 80 path=/path-to-page.html +.Pp +To find the advertised web pages on the local network (the same list that +Safari shows), use: +.Pp +.Dl mDNS -B _http._tcp +.Pp +While that command is running, in another window, try the "mDNS -R" +example given above to advertise a web page, and you should see the +"Add" event reported to the "mDNS -B" window. Now press Ctrl-C in the +"mDNS -R" window and you should see the "Remove" event reported to the +"mDNS -B" window. +.Pp +.Sh FILES +.Pa /usr/bin/mDNS \" Pathname +.\" +.Sh SEE ALSO +.Xr mDNSResponder 8 +.\" +.Sh BUGS +.Nm +bugs are tracked in Apple Radar component "mDNSResponder". +.\" +.Sh HISTORY +The +.Nm +command first appeared in Mac OS X 10.3 (Panther). diff --git a/mDNSShared/mDNSDebug.c b/mDNSShared/mDNSDebug.c new file mode 100644 index 0000000..9196f00 --- /dev/null +++ b/mDNSShared/mDNSDebug.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + File: mDNSDebug.c + + Contains: Implementation of debugging utilities. Requires a POSIX environment. + + Version: 1.0 + + Change History (most recent first): + +$Log: mDNSDebug.c,v $ +Revision 1.3 2004/01/28 21:14:23 cheshire +Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode) + +Revision 1.2 2003/12/09 01:30:40 rpantos +Fix usage of ARGS... macros to build properly on Windows. + +Revision 1.1 2003/12/08 21:11:42; rpantos +Changes necessary to support mDNSResponder on Linux. + +*/ + +#include "mDNSDebug.h" + +#include +#include + +#include "mDNSClientAPI.h" + +#if MDNS_DEBUGMSGS +mDNSexport int mDNS_DebugMode = mDNStrue; +#else +mDNSexport int mDNS_DebugMode = mDNSfalse; +#endif + +// 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 + +mDNSlocal void WriteLogMsg(const char *ident, const char *buffer, int logoptflags) + { + if (mDNS_DebugMode) // In debug mode we write to stderr + { + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } + else // else, in production mode, we write to syslog + { + openlog(ident, LOG_CONS | LOG_PERROR | logoptflags, LOG_DAEMON); + syslog(LOG_ERR, "%s", buffer); + closelog(); + } + } + +mDNSlocal void LogMsgWithIdent(const char *ident, const char *format, va_list ptr) + { + unsigned char buffer[512]; + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + WriteLogMsg(ident, buffer, ident && *ident ? LOG_PID : 0); + } + +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); + WriteLogMsg("mDNSResponder", buffer, 0); + } + +mDNSexport void LogMsgIdent(const char *ident, const char *format, ...) + { + va_list ptr; + va_start(ptr,format); + LogMsgWithIdent(ident, format, ptr); + va_end(ptr); + } + +mDNSexport void LogMsgNoIdent(const char *format, ...) + { + va_list ptr; + va_start(ptr,format); + LogMsgWithIdent("", format, ptr); + va_end(ptr); + } diff --git a/mDNSShared/mDNSResponder.8 b/mDNSShared/mDNSResponder.8 new file mode 100644 index 0000000..cef8944 --- /dev/null +++ b/mDNSShared/mDNSResponder.8 @@ -0,0 +1,108 @@ +.\" Copyright (c) 2003-2004 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@ +.\" +.\" $Log: mDNSResponder.8,v $ +.\" Revision 1.4 2004/05/18 18:14:36 cheshire +.\" Minor wording update +.\" +.\" Revision 1.3 2004/04/22 02:56:08 cheshire +.\" : mDNSResponder man page format error +.\" +.\" Revision 1.2 2004/04/12 18:03:24 ksekar +.\" : mDNSResponder man page format error +.\" +.\" Revision 1.1 2003/11/13 03:21:38 cheshire +.\" : No man page for mDNSResponder +.\" +.\" +.\" +.Dd April 2004 \" Date +.Dt mDNSResponder 8 \" Document Title +.Os Darwin \" Operating System +.\" +.Sh NAME +.Nm mDNSResponder +.Nd Multicast DNS daemon \" Name Description for whatis database +.\" +.Sh SYNOPSIS +.Nm +.\" +.Sh DESCRIPTION +.Nm +is a daemon invoked at boot time to implement Multicast DNS +and DNS Service Discovery. +.Pp +.Nm +listens UDP port 5353 for Multicast DNS Query packets. +When it receives a query for which it knows an answer, +.Nm +issues the appropriate Multicast DNS Reply packet. +.Pp +.Nm +also performs Multicast DNS Queries on behalf of client processes, +and maintains a cache of the replies. +.Pp +.Nm +has no user-specifiable command-line argument, and users should not run +.Nm +manually. +.Pp +To examine +.Nm Ns 's internal state, for debugging and disagnostic purposes, +send it a SIGINFO signal, and it will then dump a snapshot summary +of its internal state to +.Pa /var/log/system.log Ns , e.g. +.Pp +.Dl sudo killall -INFO mDNSResponder +.Sh FILES +.Pa /usr/sbin/mDNSResponder \" Pathname +.\" +.Sh SEE ALSO +.Xr mDNS 1 +.Pp +For information on Multicast DNS, see +.Pa http://www.multicastdns.org/ +.Pp +For information on DNS Service Discovery, see +.Pa http://www.dns-sd.org/ +.Pp +For information on how to use the Multicast DNS and the +DNS Service Discovery APIs on Mac OS X and other platforms, see +.Pa http://developer.apple.com/macosx/rendezvous/ +.Pp +For the source code to +.Nm , see +.Pa http://developer.apple.com/darwin/projects/rendezvous/ +.\" +.Sh BUGS +.Nm +bugs are tracked in Apple Radar component "mDNSResponder". +.\" +.Sh HISTORY +The +.Nm +daemon first appeared in Mac OS X 10.2 (Jaguar). +.Pp +Also available from the Darwin open source repository +(though not officially supported by Apple) are +.Nm +daemons for other platforms, including Mac OS 9, Microsoft Windows, +Linux, FreeBSD, NetBSD, Solaris, and other POSIX systems. diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c index 5cc63fc..e393f69 100644 --- a/mDNSShared/uds_daemon.c +++ b/mDNSShared/uds_daemon.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,11 +25,121 @@ Change History (most recent first): $Log: uds_daemon.c,v $ -Revision 1.22.2.1 2003/12/05 00:03:35 cheshire - Use buffer size MAX_ESCAPED_DOMAIN_NAME instead of 256 +Revision 1.55.2.1 2004/06/13 22:32:24 ksekar +WWDC Branch + +Revision 1.55 2004/06/05 00:04:27 cheshire +: wide-area domains should be returned in reg. domain enumeration + +Revision 1.54 2004/06/01 22:22:52 ksekar +: wide-area default registrations should be in +.local too + +Revision 1.53 2004/05/28 23:42:37 ksekar +: Feature: DNS server->client notification on record changes (#7805) + +Revision 1.52 2004/05/26 00:39:49 ksekar +: wide-area rendezvous servers don't appear in +Finder +Use local-only InterfaceID for GetDomains calls for sockets-API + +Revision 1.51 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.50 2004/05/14 16:39:47 ksekar +Browse for iChat locally for now. + +Revision 1.49 2004/05/13 21:33:52 ksekar +Clean up non-local registration control via config file. Force iChat +registrations to be local for now. + +Revision 1.48 2004/05/13 04:13:19 ksekar +Updated SIGINFO handler for multi-domain browses + +Revision 1.47 2004/05/12 22:04:01 ksekar +Implemented multi-domain browsing by default for uds_daemon. + +Revision 1.46 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +: Remove flags with zero value +: Passing in NULL causes a crash. + +Revision 1.45 2004/03/12 08:49:28 cheshire +#include + +Revision 1.44 2004/02/25 01:25:27 ksekar +: DNSServiceRegisterRecord flags not error-checked + +Revision 1.43 2004/02/24 01:46:40 cheshire +Manually reinstate lost checkin 1.36 + +Revision 1.42 2004/02/05 19:39:29 cheshire +Move creation of /var/run/mDNSResponder.pid to uds_daemon.c, +so that all platforms get this functionality + +Revision 1.41 2004/02/03 18:59:02 cheshire +Change "char *domain" parameter for format_enumeration_reply to "const char *domain" + +Revision 1.40 2004/01/28 03:41:00 cheshire +: Need ability to do targeted queries as well as multicast queries + +Revision 1.39 2004/01/25 00:03:21 cheshire +Change to use mDNSVal16() instead of private PORT_AS_NUM() macro + +Revision 1.38 2004/01/19 19:51:46 cheshire +Fix compiler error (mixed declarations and code) on some versions of Linux + +Revision 1.37 2003/12/08 21:11:42 rpantos +Changes necessary to support mDNSResponder on Linux. + +Revision 1.36 2003/12/04 23:40:57 cheshire +: Security: Crashing bug in mDNSResponder +Fix some more code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. + +Revision 1.35 2003/12/03 19:10:22 ksekar +: malloc'd data not zero'd + +Revision 1.34 2003/12/03 02:00:01 ksekar +: malloc'd data not zero'd + +Revision 1.33 2003/11/22 01:18:46 ksekar +: config change handler not called for dns-sd services + +Revision 1.32 2003/11/20 21:46:12 ksekar +: leak: DNSServiceRegisterRecord + +Revision 1.31 2003/11/20 20:33:05 ksekar +: leak: DNSServiceRegisterRecord + +Revision 1.30 2003/11/20 02:10:55 ksekar +: cleanup DNSServiceAdd/RemoveRecord + +Revision 1.29 2003/11/14 21:18:32 cheshire +: Security: Crashing bug in mDNSResponder +Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. + +Revision 1.28 2003/11/08 22:18:29 cheshire +: Don't need to show process ID in *every* mDNSResponder syslog message + +Revision 1.27 2003/11/05 22:44:57 ksekar +: No bounds checking when reading data from client +Reviewed by: Stuart Cheshire + +Revision 1.26 2003/10/23 17:51:04 ksekar +: handle blocked clients more efficiently +Changed gettimeofday() to mDNSPlatformTimeNow() + +Revision 1.25 2003/10/22 23:37:49 ksekar +: crash/hang in abort_client + +Revision 1.24 2003/10/21 20:59:40 ksekar +: handle blocked clients moreefficiently + +Revision 1.23 2003/09/23 02:12:43 cheshire +Also include port number in list of services registered via new UDS API Revision 1.22 2003/08/19 16:03:55 ksekar -Bug #: : ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord +: 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 @@ -45,17 +157,17 @@ We want to avoid touching the rdata pages, so we don't page them in. compute a hash of the rdata and store the hash in the ResourceRecord object. Revision 1.18 2003/08/15 00:38:00 ksekar -Bug #: : Bug: buffer overrun when reading long rdata from client +: Bug: buffer overrun when reading long rdata from client Revision 1.17 2003/08/14 02:18:21 cheshire Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord Revision 1.16 2003/08/13 23:58:52 ksekar -Bug #: : Bug: UDS Sub-type browsing works, but not sub-type registration +: Bug: UDS Sub-type browsing works, but not sub-type registration Fixed pointer increment error, moved subtype reading for-loop for easier error bailout. Revision 1.15 2003/08/13 17:30:33 ksekar -Bug #: : DNSServiceAddRecord doesn't work +: 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 @@ -63,16 +175,21 @@ Update to APSL 2.0 */ -#include "mDNSClientAPI.h" -#include "mDNSMacOSX.h" -#include "dns_sd.h" -#include "dnssd_ipc.h" #include +#include #include #include #include #include +#include +#include "mDNSClientAPI.h" +#include "uds_daemon.h" +#include "dns_sd.h" +#include "dnssd_ipc.h" + +// convenience definition +#define _UNUSED __attribute__ ((unused)) // Types and Data Structures // ---------------------------------------------------------------------- @@ -94,10 +211,16 @@ typedef struct registered_record_entry AuthRecord *rr; struct registered_record_entry *next; } registered_record_entry; + +typedef struct extra_record_entry + { + int key; + struct extra_record_entry *next; + ExtraResourceRecord e; + } extra_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 @@ -105,8 +228,15 @@ typedef struct registered_service ServiceRecordSet *srs; struct request_state *request; AuthRecord *subtypes; + extra_record_entry *extras; } registered_service; - + +typedef struct + { + registered_service *local; + registered_service *global; + } servicepair_t; + typedef struct { mStatus err; @@ -117,8 +247,6 @@ typedef struct typedef struct request_state { // connection structures - CFRunLoopSourceRef rls; - CFSocketRef sr; int sd; int errfd; @@ -133,6 +261,7 @@ typedef struct request_state // reply, termination, error, and client context info int no_reply; // don't send asynchronous replies to client + int time_blocked; // record time of a blocked 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; @@ -142,7 +271,7 @@ typedef struct request_state //!!!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 + servicepair_t servicepair; struct resolve_result_t *resolve_results; struct request_state *next; @@ -218,10 +347,22 @@ typedef struct client_context_t client_context; } regrecord_callback_context; +// for multi-domain browsing +typedef struct qlist_t + { + DNSQuestion q; + struct qlist_t *next; + } qlist_t; +typedef struct + { + qlist_t *qlist; + request_state *rstate; + } browse_termination_context; // globals +static mDNS *gmDNS = NULL; 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 @@ -229,14 +370,16 @@ static request_state *all_requests = NULL; #define MAX_OPENFILES 1024 - - +#define MAX_TIME_BLOCKED 60 * mDNSPlatformOneSecond // try to send data to a blocked client for 60 seconds before + // terminating connection +#define MSG_PAD_BYTES 5 // pad message buffer (read from client) with n zero'd bytes to guarantee + // n get_string() calls w/o buffer overrun // private function prototypes -static void connect_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i); +static void connect_callback(void *info); 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 request_callback(void *info); 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); @@ -255,14 +398,13 @@ static int build_domainname_from_strings(domainname *srv, char *name, char *regt 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 reply_state *format_enumeration_reply(request_state *rstate, const 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 AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl, int validate_flags); 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); @@ -274,21 +416,48 @@ 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); +static int validate_message(request_state *rstate); +static mStatus remove_extra_rr_from_service(request_state *rstate); +static mStatus remove_record(request_state *rstate); +static void free_service_registration(registered_service *srv); // initialization, setup/teardown functions -int udsserver_init(void) +// If a platform specifies its own PID file name, we use that +#ifndef PID_FILE +#define PID_FILE "/var/run/mDNSResponder.pid" +#endif + +int udsserver_init( mDNS *globalInstance) { mode_t mask; struct sockaddr_un laddr; struct rlimit maxfds; + if ( !globalInstance) + goto error; + gmDNS = globalInstance; + + // If a particular platform wants to opt out of having a PID file, define PID_FILE to be "" + if (PID_FILE[0]) + { + FILE *fp = fopen(PID_FILE, "w"); + if (fp != NULL) + { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } + } + if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) - goto error; + goto error; unlink(MDNS_UDS_SERVERPATH); //OK if this fails bzero(&laddr, sizeof(laddr)); laddr.sun_family = AF_LOCAL; +#ifndef NOT_HAVE_SA_LEN // According to Stevens (section 3.2), there is no portable way to + // determine whether sa_len is defined on a particular platform. laddr.sun_len = sizeof(struct sockaddr_un); +#endif strcpy(laddr.sun_path, MDNS_UDS_SERVERPATH); mask = umask(0); if (bind(listenfd, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) @@ -302,6 +471,11 @@ int udsserver_init(void) } listen(listenfd, LISTENQ); + if (mStatus_NoError != udsSupportAddFDToEventLoop(listenfd, connect_callback, (void *) NULL)) + { + my_perror("ERROR: could not add listen socket to event loop"); + goto error; + } // set maximum file descriptor to 1024 if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) @@ -333,27 +507,6 @@ int udsserver_exit(void) } -// 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; @@ -361,7 +514,6 @@ mDNSs32 udsserver_idle(mDNSs32 nextevent) transfer_state result; mDNSs32 now = mDNSPlatformTimeNow(); - while(req) { result = t_uninitialized; @@ -373,27 +525,34 @@ mDNSs32 udsserver_idle(mDNSs32 nextevent) 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); + req->time_blocked = 0; // reset failure counter after successful send } 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 - } + else if (result == t_morecoming) break; // client's queues are full, move to next } } + if (result == t_morecoming) + { + if (!req->time_blocked) req->time_blocked = now; + debugf("udsserver_idle: client has been blocked for %d seconds", now - req->time_blocked); + if (now - req->time_blocked >= MAX_TIME_BLOCKED) + { + LogMsg("Could not write data to client after %d seconds - aborting connection", MAX_TIME_BLOCKED / mDNSPlatformOneSecond); + abort_request(req); + result = t_terminated; + } + else if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; // try again 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() { @@ -415,57 +574,60 @@ mDNSs32 udsserver_idle(mDNSs32 nextevent) void udsserver_info(void) { request_state *req; + qlist_t *qlist; 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); + LogMsgNoIdent("DNSServiceRegister %##s %u", ((registered_service *)t)->srs->RR_SRV.resrec.name.c, SRS_PORT(((registered_service *)t)->srs)); else if (req->terminate == browse_termination_callback) - LogMsg("DNSServiceBrowse %##s", ((DNSQuestion *) t)->qname.c); + { + for (qlist = ((browse_termination_context *)t)->qlist; qlist; qlist = qlist->next) + LogMsgNoIdent("DNSServiceBrowse %##s", qlist->q.qname.c); + } else if (req->terminate == resolve_termination_callback) - LogMsg("DNSServiceResolve %##s", ((resolve_termination_t *)t)->srv->question.qname.c); + LogMsgNoIdent("DNSServiceResolve %##s", ((resolve_termination_t *)t)->srv->question.qname.c); else if (req->terminate == question_termination_callback) - LogMsg("DNSServiceQueryRecord %##s", ((DNSQuestion *) t)->qname.c); + LogMsgNoIdent("DNSServiceQueryRecord %##s", ((DNSQuestion *) t)->qname.c); else if (req->terminate == enum_termination_callback) - LogMsg("DNSServiceEnumerateDomains %##s", ((enum_termination_t *) t)->all->question.qname.c); + LogMsgNoIdent("DNSServiceEnumerateDomains %##s", ((enum_termination_t *) t)->all->question.qname.c); } } + +static void rename_service(registered_service *srv) + { + mStatus err; + + if (srv->autoname && !SameDomainLabel(srv->name.c, gmDNS->nicelabel.c)) + { + srv->rename_on_memfree = 1; + err = mDNS_DeregisterService(gmDNS, srv->srs); + if (err) LogMsg("ERROR: udsserver_handle_configchange: DeregisterService returned error %d. Continuing.", err); + // error should never occur - safest to log and continue + } + } + 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 - } - } + if (req->servicepair.local) rename_service(req->servicepair.local); + if (req->servicepair.global) rename_service(req->servicepair.global); + } } - - - -// 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) +static void connect_callback(void *info _UNUSED) { 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); @@ -476,12 +638,15 @@ static void connect_callback(CFSocketRef s, CFSocketCallBackType t, CFDataRef dr return; } optval = 1; +#ifdef SO_NOSIGPIPE + // Some environments (e.g. OS X) support turning off SIGPIPE for a socket if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) { my_perror("ERROR: setsockopt - SOL_NOSIGPIPE - aborting client"); close(sd); return; } +#endif if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) { @@ -525,57 +690,21 @@ static void connect_callback(CFSocketRef s, CFSocketCallBackType t, CFDataRef dr 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); + if ( mStatus_NoError != udsSupportAddFDToEventLoop( sd, request_callback, rstate)) 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) +static void request_callback(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) { @@ -603,6 +732,16 @@ static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef d return; } + if (validate_message(rstate) < 0) + { + // note that we cannot deliver an error message if validation fails, since the path to the error socket + // may be contained in the (invalid) message body for some message types + abort_request(rstate); + unlink_request(rstate); + LogMsg("Invalid message sent by client - may indicate a malicious program running on this machine!"); + return; + } + // check if client wants silent operation if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1; @@ -668,13 +807,14 @@ static void request_callback(CFSocketRef sr, CFSocketCallBackType t, CFDataRef d static void handle_query_request(request_state *rstate) { DNSServiceFlags flags; - uint32_t interfaceIndex; + uint32_t ifi; char name[256]; uint16_t rrtype, rrclass; char *ptr; - domainname dname; mStatus result; - + mDNSInterfaceID InterfaceID; + DNSQuestion *q; + if (rstate->ts != t_complete) { LogMsg("ERROR: handle_query_request - transfer state != t_complete"); @@ -686,13 +826,37 @@ static void handle_query_request(request_state *rstate) LogMsg("ERROR: handle_query_request - NULL msgdata"); goto error; } + flags = get_flags(&ptr); - interfaceIndex = get_long(&ptr); + ifi = 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); + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi); + if (ifi && !InterfaceID) goto bad_param; + + q = mallocL("DNSQuestion", sizeof(DNSQuestion)); + if (!q) + { + my_perror("ERROR: handle_query - malloc"); + exit(1); + } + bzero(q, sizeof(DNSQuestion)); + + if (!MakeDomainNameFromDNSNameString(&q->qname, name)) { freeL("DNSQuestion", q); goto bad_param; } + q->QuestionContext = rstate; + q->QuestionCallback = question_result_callback; + q->qtype = rrtype; + q->qclass = rrclass; + q->InterfaceID = InterfaceID; + q->Target = zeroAddr; + if (flags & kDNSServiceFlagsLongLivedQuery) q->LongLived = mDNStrue; + rstate->termination_context = q; + rstate->terminate = question_termination_callback; + + result = mDNS_StartQuery(gmDNS, q); + if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result); + if (result) rstate->terminate = NULL; if (deliver_error(rstate, result) < 0) goto error; return; @@ -710,7 +874,8 @@ static void handle_resolve_request(request_state *rstate) { DNSServiceFlags flags; uint32_t interfaceIndex; - char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; + mDNSInterfaceID InterfaceID; + char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; char *ptr; // message data pointer domainname fqdn; resolve_t *srv, *txt; @@ -736,7 +901,7 @@ static void handle_resolve_request(request_state *rstate) } flags = get_flags(&ptr); interfaceIndex = get_long(&ptr); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex); if (interfaceIndex && !InterfaceID) goto bad_param; if (get_string(&ptr, name, 256) < 0 || get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || @@ -766,6 +931,7 @@ static void handle_resolve_request(request_state *rstate) srv->question.qtype = kDNSType_SRV; srv->question.qclass = kDNSClass_IN; srv->question.InterfaceID = InterfaceID; + srv->question.Target = zeroAddr; txt->question.QuestionContext = rstate; txt->question.QuestionCallback = resolve_result_callback; @@ -773,6 +939,7 @@ static void handle_resolve_request(request_state *rstate) txt->question.qtype = kDNSType_TXT; txt->question.qclass = kDNSClass_IN; txt->question.InterfaceID = InterfaceID; + txt->question.Target = zeroAddr; // set up termination info term = mallocL("handle_resolve_request", sizeof(resolve_termination_t)); @@ -789,8 +956,8 @@ static void handle_resolve_request(request_state *rstate) 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); + err = mDNS_StartQuery(gmDNS, &srv->question); + if (!err) err = mDNS_StartQuery(gmDNS, &txt->question); if (err) { @@ -830,8 +997,8 @@ static void resolve_termination_callback(void *context) } rs = term->rstate; - mDNS_StopQuery(&mDNSStorage, &term->txt->question); - mDNS_StopQuery(&mDNSStorage, &term->srv->question); + mDNS_StopQuery(gmDNS, &term->txt->question); + mDNS_StopQuery(gmDNS, &term->srv->question); freeL("resolve_termination_callback", term->txt); freeL("resolve_termination_callback", term->srv); @@ -843,7 +1010,7 @@ static void resolve_termination_callback(void *context) -static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +static void resolve_result_callback(mDNS *const m _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { int len = 0; char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME]; @@ -852,7 +1019,6 @@ static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const reply_state *rep; request_state *rs = question->QuestionContext; resolve_result_t *res = rs->resolve_results; - #pragma unused(m) if (!AddRecord) { @@ -881,7 +1047,7 @@ static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const // allocate/init reply header rep = create_reply(resolve_reply, len, rs); rep->rhdr->flags = 0; - rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, answer->InterfaceID); + rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID); rep->rhdr->error = kDNSServiceErr_NoError; data = rep->sdata; @@ -902,44 +1068,9 @@ static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const 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) +static void question_result_callback(mDNS *const m _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { char *data; char name[MAX_ESCAPED_DOMAIN_NAME]; @@ -947,7 +1078,6 @@ static void question_result_callback(mDNS *const m, DNSQuestion *question, const reply_state *rep; int len; - #pragma unused(m) //mDNS_StopQuery(m, question); req = question->QuestionContext; @@ -961,8 +1091,8 @@ static void question_result_callback(mDNS *const m, DNSQuestion *question, const 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->flags = AddRecord ? kDNSServiceFlagsAdd : 0; + rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID); rep->rhdr->error = kDNSServiceErr_NoError; data = rep->sdata; @@ -982,7 +1112,7 @@ static void question_termination_callback(void *context) DNSQuestion *q = context; - mDNS_StopQuery(&mDNSStorage, q); // no need to error check + mDNS_StopQuery(gmDNS, q); // no need to error check freeL("question_termination_callback", q); } @@ -991,12 +1121,15 @@ static void handle_browse_request(request_state *request) { DNSServiceFlags flags; uint32_t interfaceIndex; + mDNSInterfaceID InterfaceID; char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; - DNSQuestion *q; - domainname typedn, domdn; + qlist_t *qlist = NULL, *qlist_elem; + domainname typedn; char *ptr; mStatus result; - + DNameListElem *search_domain_list, *sdom, tmp; + browse_termination_context *term; + if (request->ts != t_complete) { LogMsg("ERROR: handle_browse_request - transfer state != t_complete"); @@ -1004,13 +1137,6 @@ static void handle_browse_request(request_state *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; @@ -1018,23 +1144,72 @@ static void handle_browse_request(request_state *request) interfaceIndex = get_long(&ptr); if (get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) - goto bad_param; - + goto bad_param; freeL("handle_browse_request", request->msgbuf); request->msgbuf = NULL; - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, 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; + + if (!MakeDomainNameFromDNSNameString(&typedn, regtype)) goto bad_param; + + //!!!KRS browse locally for ichat + if (!domain[0] && (!strcmp(regtype, "_ichat._tcp.") || !strcmp(regtype, "_presence._tcp."))) + strcpy(domain,"local."); + + if (domain[0]) + { + // generate a fake list of one elem to reduce number of code paths + if (!MakeDomainNameFromDNSNameString(&tmp.name, domain)) goto bad_param; + tmp.next = NULL; + search_domain_list = &tmp; + } + else search_domain_list = mDNSPlatformGetSearchDomainList(); + + for (sdom = search_domain_list; sdom; sdom = sdom->next) + { + qlist_elem = mallocL("handle_browse_request", sizeof(qlist_t)); + if (!qlist_elem) + { + my_perror("ERROR: handle_browse_request - malloc"); + exit(1); + } + bzero(qlist_elem, sizeof(qlist_t)); + qlist_elem->q.QuestionContext = request; + qlist_elem->q.QuestionCallback = browse_result_callback; + qlist_elem->next = qlist; + qlist = qlist_elem; + } + + // setup termination context + term = (browse_termination_context *)mallocL("handle_browse_request", sizeof(browse_termination_context)); + if (!term) + { + my_perror("ERROR: handle_browse_request - malloc"); + exit(1); + } + term->qlist = qlist; + term->rstate = request; + request->termination_context = term; request->terminate = browse_termination_callback; - result = mDNS_StartBrowse(&mDNSStorage, q, &typedn, &domdn, InterfaceID, browse_result_callback, request); - deliver_error(request, result); - return; + + // start the browses + sdom = search_domain_list; + for (qlist_elem = qlist; qlist_elem; qlist_elem = qlist_elem->next) + { + result = mDNS_StartBrowse(gmDNS, &qlist_elem->q, &typedn, &sdom->name, InterfaceID, browse_result_callback, request); + if (result) + { + // bail here on error. questions not yet issued are in no core lists, so they can be deallocated lazily + if (search_domain_list != &tmp) mDNS_FreeDNameList(search_domain_list); + deliver_error(request, result); + return; + } + sdom = sdom->next; + } + if (search_domain_list != &tmp) mDNS_FreeDNameList(search_domain_list); + deliver_error(request, mStatus_NoError); + return; bad_param: deliver_error(request, mStatus_BadParamErr); @@ -1042,13 +1217,12 @@ bad_param: unlink_request(request); } -static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +static void browse_result_callback(mDNS *const m _UNUSED, 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); @@ -1068,11 +1242,78 @@ static void browse_result_callback(mDNS *const m, DNSQuestion *question, const R static void browse_termination_callback(void *context) { - DNSQuestion *q = context; + browse_termination_context *t = context; + qlist_t *ptr, *fptr; + + if (!t) return; + + ptr = t->qlist; + t->qlist = NULL; + + while(ptr) + { + mDNS_StopBrowse(gmDNS, &ptr->q); // no need to error-check result + fptr = ptr; + ptr = ptr->next; + freeL("browse_termination_callback", fptr); + } + t->rstate->termination_context = NULL; + freeL("browse_termination_callback", t); + } + +static mStatus register_service(request_state *request, registered_service **srv_ptr, DNSServiceFlags flags, + uint16_t txtlen, void *txtdata, mDNSIPPort port, domainlabel *n, char *type_as_string, + domainname *t, domainname *d, domainname *h, mDNSBool autoname, int num_subtypes, mDNSInterfaceID InterfaceID) + { + registered_service *r_srv; + int srs_size, i; + char *sub; + mStatus result; + *srv_ptr = NULL; + + 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 = type_as_string + strlen(type_as_string) + 1; + for (i = 0; i < num_subtypes; i++) + { + if (!MakeDomainNameFromDNSNameString(&(r_srv->subtypes + i)->resrec.name, sub)) + { + free_service_registration(r_srv); + return mStatus_BadParamErr; + } + sub += strlen(sub) + 1; + } + } + else r_srv->subtypes = NULL; + r_srv->request = request; + + r_srv->extras = NULL; + r_srv->autoname = autoname; + r_srv->rename_on_memfree = 0; + r_srv->renameonconflict = !(flags & kDNSServiceFlagsNoAutoRename); + memcpy(r_srv->name.c, n->c, n->c[0]); + + result = mDNS_RegisterService(gmDNS, r_srv->srs, n, t, d, h, port, + txtdata, txtlen, r_srv->subtypes, num_subtypes, InterfaceID, regservice_callback, r_srv); + + if (result) + free_service_registration(r_srv); + else *srv_ptr = r_srv; + + return result; + +malloc_error: + my_perror("ERROR: malloc"); + exit(1); + } - 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) @@ -1083,18 +1324,15 @@ static void handle_regservice_request(request_state *request) uint16_t txtlen; mDNSIPPort port; void *txtdata; - char *ptr; + char *ptr, *sub; domainlabel n; - domainname t, d, h, srv; - registered_service *r_srv; - int srs_size; + domainname d, h, t, srv; mStatus result; - - char *sub, *rtype_ptr; - int i, num_subtypes; - + mDNSInterfaceID InterfaceID; + int num_subtypes; + char *rtype_ptr; - if (request->ts != t_complete) + if (request->ts != t_complete) { LogMsg("ERROR: handle_regservice_request - transfer state != t_complete"); abort_request(request); @@ -1106,7 +1344,7 @@ static void handle_regservice_request(request_state *request) ptr = request->msgdata; flags = get_flags(&ptr); ifi = get_long(&ptr); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi); + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi); if (ifi && !InterfaceID) goto bad_param; if (get_string(&ptr, name, 256) < 0 || get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || @@ -1118,56 +1356,37 @@ static void handle_regservice_request(request_state *request) txtlen = get_short(&ptr); txtdata = get_rdata(&ptr, txtlen); + if (!*regtype || !MakeDomainNameFromDNSNameString(&t, regtype)) goto bad_param; + // 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; + if (!name[0]) n = (gmDNS)->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))) + + if ((!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; + if (host[0] && !MakeDomainNameFromDNSNameString(&h, host)) goto bad_param; + + result = register_service(request, &request->servicepair.local, flags, txtlen, txtdata, port, &n, ®type[0], &t, &d, host[0] ? &h : NULL, !name[0], num_subtypes, InterfaceID); + + //!!!KRS if we got a dynamic reg domain from the config file, use it for default (except for iChat) + if (!domain[0] && gmDNS->uDNS_info.ServiceRegDomain[0] && strcmp(regtype, "_presence._tcp.") && strcmp(regtype, "_ichat._tcp.")) + { + MakeDomainNameFromDNSNameString(&d, gmDNS->uDNS_info.ServiceRegDomain); + register_service(request, &request->servicepair.global, flags, txtlen, txtdata, port, &n, ®type[0], &t, &d, host[0] ? &h : NULL, !name[0], num_subtypes, InterfaceID); + // don't return default global errors - it will confuse legacy clients, and we want .local to still work for them + } + + request->termination_context = &request->servicepair; 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) { @@ -1175,34 +1394,28 @@ static void handle_regservice_request(request_state *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) +static void regservice_callback(mDNS *const m _UNUSED, 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)) { @@ -1218,23 +1431,14 @@ static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mSta 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); + r_srv->name = gmDNS->nicelabel; + err = mDNS_RenameAndReregisterService(gmDNS, 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); + free_service_registration(r_srv); return; } } @@ -1242,17 +1446,13 @@ static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mSta { if (r_srv->autoname || r_srv->renameonconflict) { - mDNS_RenameAndReregisterService(&mDNSStorage, srs, mDNSNULL); + mDNS_RenameAndReregisterService(gmDNS, 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) + free_service_registration(r_srv); + if (deliver_async_error(rs, reg_service_reply, result) < 0) { abort_request(rs); unlink_request(rs); @@ -1262,7 +1462,7 @@ static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mSta } else { - LogMsg("ERROR: unknown result in regservice_callback"); + LogMsg("ERROR: unknown result in regservice_callback: %d", result); if (deliver_async_error(rs, reg_service_reply, result) < 0) { abort_request(rs); @@ -1271,25 +1471,61 @@ static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mSta return; } } + +static mStatus add_record_to_service(request_state *rstate, registered_service *r_srv, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl) + { + ServiceRecordSet *srs = r_srv->srs; + ExtraResourceRecord *extra; + extra_record_entry *ere; + mStatus result; + int size; + + if (rdlen > sizeof(RDataBody)) size = rdlen; + else size = sizeof(RDataBody); + + ere = mallocL("hanle_add_request", sizeof(extra_record_entry) - sizeof(RDataBody) + size); + if (!ere) + { + my_perror("ERROR: malloc"); + exit(1); + } + bzero(ere, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd + extra = &ere->e; + 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(gmDNS, srs , extra, &extra->r.rdatastorage, ttl); + if (result) { freeL("handle_add_request", ere); return result; } + + ere->key = rstate->hdr.reg_index; + ere->next = r_srv->extras; + r_srv->extras = ere; + return result; + } + + static void handle_add_request(request_state *rstate) { - registered_record_entry *re; - ExtraResourceRecord *extra; - uint32_t size, ttl; + uint32_t ttl; uint16_t rrtype, rdlen; char *ptr, *rdata; mStatus result; DNSServiceFlags flags; - ServiceRecordSet *srs = rstate->service->srs; - - if (!srs) + registered_service *local, *global; + + local = rstate->servicepair.local; + global = rstate->servicepair.global; + + if (!local) { - LogMsg("ERROR: handle_add_request - no service record set in request state"); + LogMsg("ERROR: handle_add_request - no service registered"); deliver_error(rstate, mStatus_UnknownErr); return; - } - + } + ptr = rstate->msgdata; flags = get_flags(&ptr); rrtype = get_short(&ptr); @@ -1297,95 +1533,108 @@ static void handle_add_request(request_state *rstate) 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); + result = add_record_to_service(rstate, local, rrtype, rdlen, rdata, ttl); + if (global) add_record_to_service(rstate, global, rrtype, rdlen, rdata, ttl); // don't report global errors to client + 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) + } + +static mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32_t ttl) + { + int rdsize; + RData *newrd; + mStatus result; + + 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); } - re->key = rstate->hdr.reg_index; - re->rr = &extra->r; - re->next = rstate->reg_recs; - rstate->reg_recs = re; - } - + newrd->MaxRDLength = rdsize; + memcpy(&newrd->u, rdata, rdlen); + result = mDNS_Update(gmDNS, rr, ttl, rdlen, newrd, update_callback); + if (result) { LogMsg("ERROR: mDNS_Update - %d", result); freeL("handle_update_request", newrd); } + return result; + } + +static mStatus find_extras_by_key(request_state *rstate, AuthRecord **lRR, AuthRecord **gRR) + { + extra_record_entry *e; + + // find the extra record for the local service + for (e = rstate->servicepair.local->extras; e; e = e->next) + if (e->key == rstate->hdr.reg_index) break; + if (!e) return mStatus_BadReferenceErr; + + *lRR = &e->e.r; + + // find the corresponding global record, if it exists + if (rstate->servicepair.global) + { + for (e = rstate->servicepair.global->extras; e; e = e->next) + if (e->key == rstate->hdr.reg_index) break; + if (e) *gRR = &e->e.r; + else *gRR = NULL; + } + return mStatus_NoError; + } + static void handle_update_request(request_state *rstate) { registered_record_entry *reptr; - AuthRecord *rr; - RData *newrd; - uint16_t rdlen, rdsize; + AuthRecord *lRR, *gRR = NULL; + uint16_t rdlen; char *ptr, *rdata; uint32_t ttl; mStatus result; - + if (rstate->hdr.reg_index == TXT_RECORD_INDEX) { - if (!rstate->service) + if (!rstate->servicepair.local) { deliver_error(rstate, mStatus_BadParamErr); return; } - rr = &rstate->service->srs->RR_TXT; + lRR = &rstate->servicepair.local->srs->RR_TXT; + if (rstate->servicepair.global) gRR = &rstate->servicepair.global->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; - } + if (rstate->servicepair.local) // registered service + { + if (find_extras_by_key(rstate, &lRR, &gRR)) + { deliver_error(rstate, mStatus_BadReferenceErr); return; } + } + else + { + // record created via RegisterRecord + reptr = rstate->reg_recs; + while(reptr && reptr->key != rstate->hdr.reg_index) reptr = reptr->next; + if (!reptr) { deliver_error(rstate, mStatus_BadReferenceErr); return; } + lRR = reptr->rr; + } + } - ptr = rstate->msgdata; + // get the message data + 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); + result = update_record(lRR, rdlen, rdata, ttl); + if (gRR) update_record(gRR, rdlen, rdata, ttl); // don't report errors for global registration + deliver_error(rstate, result); reset_connected_rstate(rstate); } -static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd) - { - #pragma unused(m) - +static void update_callback(mDNS *const m _UNUSED, AuthRecord *const rr, RData *oldrd) + { if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd); } @@ -1419,19 +1668,53 @@ static void process_service_registration(ServiceRecordSet *const srs) else append_reply(req, rep); } +static void free_service_registration(registered_service *srv) + { + request_state *rstate = srv->request; + extra_record_entry *extra; + + // clear pointers from parent struct + if (rstate) + { + if (rstate->servicepair.local == srv) rstate->servicepair.local = NULL; + else if (rstate->servicepair.global == srv) rstate->servicepair.global = NULL; + } + + while (srv->extras) + { + extra = srv->extras; + srv->extras = srv->extras->next; + if (extra->e.r.resrec.rdata != &extra->e.r.rdatastorage) + freeL("free_service_registration", extra->e.r.resrec.rdata); + freeL("regservice_callback", extra); + } + + if (srv->subtypes) { freeL("regservice_callback", srv->subtypes); srv->subtypes = NULL; } + freeL("regservice_callback", srv->srs); + srv->srs = NULL; + freeL("regservice_callback", srv); + } + static void regservice_termination_callback(void *context) { - registered_service *srv = context; + servicepair_t *pair = context; + + if (!pair->local && !pair->global) { LogMsg("ERROR: regservice_termination_callback called with null services"); return; } + // clear service pointers to parent request state + if (pair->local) pair->local->request = NULL; + if (pair->global) pair->global->request = NULL; + // 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); - } - } + if (pair->local && mDNS_DeregisterService(gmDNS, pair->local->srs) != mStatus_NoError) + free_service_registration(pair->local); + if (pair->global && mDNS_DeregisterService(gmDNS, pair->global->srs) != mStatus_NoError) + free_service_registration(pair->global); + + // clear pointers to services - they'll get cleaned by MemFree callback + pair->local = NULL; + pair->global = NULL; + } static void handle_regrecord_request(request_state *rstate) @@ -1449,13 +1732,13 @@ static void handle_regrecord_request(request_state *rstate) return; } - rr = read_rr_from_ipc_msg(rstate->msgdata, 1); + rr = read_rr_from_ipc_msg(rstate->msgdata, 1, 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; @@ -1477,7 +1760,7 @@ static void handle_regrecord_request(request_state *rstate) rstate->termination_context = rstate; } - result = mDNS_Register(&mDNSStorage, rr); + result = mDNS_Register(gmDNS, rr); deliver_error(rstate, result); reset_connected_rstate(rstate); return; @@ -1487,17 +1770,20 @@ malloc_error: return; } -static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus result) +static void regrecord_callback(mDNS *const m _UNUSED, AuthRecord *const rr, mStatus result) { - regrecord_callback_context *rcc; + regrecord_callback_context *rcc = rr->RecordContext; int len; reply_state *reply; transfer_state ts; - #pragma unused(m) - - if (result == mStatus_MemFree) { freeL("regrecord_callback", rr); return; } - rcc = rr->RecordContext; + if (result == mStatus_MemFree) + { + freeL("regrecord_callback", rcc); + rr->RecordContext = NULL; + freeL("regrecord_callback", rr); + return; + } // format result, add to the list for the request, including the client context in the header len = sizeof(DNSServiceFlags); @@ -1507,7 +1793,7 @@ static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus resu 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->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID); reply->rhdr->error = result; ts = send_msg(reply); @@ -1522,55 +1808,127 @@ static void regrecord_callback(mDNS *const m, AuthRecord *const rr, mStatus resu static void connected_registration_termination(void *context) { + int shared; registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs; while(ptr) - { - mDNS_Deregister(&mDNSStorage, ptr->rr); + { fptr = ptr; ptr = ptr->next; + shared = fptr->rr->resrec.RecordType == kDNSRecordTypeShared; + mDNS_Deregister(gmDNS, fptr->rr); + if (!shared) + // shared records free'd via callback w/ mStatus_MemFree + { + freeL("connected_registration_termination", fptr->rr->RecordContext); + fptr->rr->RecordContext = NULL; + freeL("connected_registration_termination", fptr->rr); + fptr->rr = NULL; + } freeL("connected_registration_termination", fptr); - } + } } static void handle_removerecord_request(request_state *rstate) { - registered_record_entry *reptr, *prev = NULL; - mStatus err = mStatus_UnknownErr; + mStatus err; char *ptr; - reptr = rstate->reg_recs; ptr = rstate->msgdata; get_flags(&ptr); // flags unused + if (rstate->servicepair.local) err = remove_extra_rr_from_service(rstate); + else err = remove_record(rstate); + + reset_connected_rstate(rstate); + if (deliver_error(rstate, err) < 0) + { + abort_request(rstate); + unlink_request(rstate); + } + } + +// remove a resource record registered via DNSServiceRegisterRecord() +static mStatus remove_record(request_state *rstate) + { + int shared; + registered_record_entry *reptr, *prev = NULL; + mStatus err = mStatus_UnknownErr; + reptr = rstate->reg_recs; + 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 + shared = reptr->rr->resrec.RecordType == kDNSRecordTypeShared; + err = mDNS_Deregister(gmDNS, reptr->rr); + if (err) + { + LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err); + return err; // this should not happen. don't try to free memory if there's an error + } + if (!shared) + // shared records free'd via callback w/ mStatus_MemFree + { + freeL("remove_record", reptr->rr->RecordContext); + reptr->rr->RecordContext = NULL; + freeL("remove_record", reptr->rr); + reptr->rr = NULL; + } + freeL("remove_record", reptr); break; } prev = reptr; reptr = reptr->next; } - reset_connected_rstate(rstate); - if (deliver_error(rstate, err) < 0) - { - abort_request(rstate); - unlink_request(rstate); - } + return err; + } + + +static mStatus remove_extra(request_state *rstate, registered_service *serv) + { + mStatus err = mStatus_BadReferenceErr; + extra_record_entry *ptr, *prev = NULL; + + ptr = serv->extras; + while (ptr) + { + if (ptr->key == rstate->hdr.reg_index) // found match + { + if (prev) prev->next = ptr->next; + else serv->extras = ptr->next; + err = mDNS_RemoveRecordFromService(gmDNS, serv->srs, &ptr->e); + if (err) return err; + freeL("remove_extra_rr_from_service", ptr); + break; + } + prev = ptr; + ptr = ptr->next; + } + return err; + } + +static mStatus remove_extra_rr_from_service(request_state *rstate) + { + mStatus err = mStatus_UnknownErr; + + err = remove_extra(rstate, rstate->servicepair.local); + if (rstate->servicepair.global) remove_extra(rstate, rstate->servicepair.global); // don't return error for global + + return err; } + // domain enumeration static void handle_enum_request(request_state *rstate) { DNSServiceFlags flags, add_default; uint32_t ifi; + mDNSInterfaceID InterfaceID; char *ptr = rstate->msgdata; domain_enum_t *def, *all; enum_termination_t *term; @@ -1589,7 +1947,7 @@ static void handle_enum_request(request_state *rstate) flags = get_flags(&ptr); ifi = get_long(&ptr); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, ifi); + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi); if (ifi && !InterfaceID) { deliver_error(rstate, mStatus_BadParamErr); @@ -1622,11 +1980,14 @@ static void handle_enum_request(request_state *rstate) all->question.QuestionContext = all; all->type = (flags & kDNSServiceFlagsRegistrationDomains) ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; - + + // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list. + if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly; + // make the calls - err = mDNS_GetDomains(&mDNSStorage, &all->question, all->type, InterfaceID, enum_result_callback, all); + err = mDNS_GetDomains(gmDNS, &all->question, all->type, NULL, InterfaceID, enum_result_callback, all); if (err == mStatus_NoError) - err = mDNS_GetDomains(&mDNSStorage, &def->question, def->type, InterfaceID, enum_result_callback, def); + err = mDNS_GetDomains(gmDNS, &def->question, def->type, NULL, InterfaceID, enum_result_callback, def); result = deliver_error(rstate, err); // send error *before* returning local domain if (result < 0 || err) @@ -1637,7 +1998,7 @@ static void handle_enum_request(request_state *rstate) } // provide local. as the first domain automatically - add_default = kDNSServiceFlagsDefault | kDNSServiceFlagsAdd | kDNSServiceFlagsFinished; + add_default = kDNSServiceFlagsDefault | kDNSServiceFlagsAdd; reply = format_enumeration_reply(rstate, "local.", add_default, ifi, 0); tr = send_msg(reply); if (tr == t_error || tr == t_terminated) @@ -1652,14 +2013,13 @@ static void handle_enum_request(request_state *rstate) 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) +static void enum_result_callback(mDNS *const m _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { char domain[MAX_ESCAPED_DOMAIN_NAME]; domain_enum_t *de = question->QuestionContext; DNSServiceFlags flags = 0; reply_state *reply; - #pragma unused(m) if (answer->rrtype != kDNSType_PTR) return; if (AddRecord) { @@ -1667,12 +2027,8 @@ static void enum_result_callback(mDNS *const m, DNSQuestion *question, const Res 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); + reply = format_enumeration_reply(de->rstate, domain, flags, mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID), kDNSServiceErr_NoError); if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); @@ -1683,7 +2039,7 @@ static void enum_result_callback(mDNS *const m, DNSQuestion *question, const Res return; } -static reply_state *format_enumeration_reply(request_state *rstate, char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err) +static reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err) { int len; reply_state *reply; @@ -1707,7 +2063,7 @@ static reply_state *format_enumeration_reply(request_state *rstate, char *domain static void enum_termination_callback(void *context) { enum_termination_t *t = context; - mDNS *coredata = &mDNSStorage; + mDNS *coredata = gmDNS; mDNS_StopGetDomains(coredata, &t->all->question); mDNS_StopGetDomains(coredata, &t->def->question); @@ -1721,9 +2077,9 @@ static void handle_reconfirm_request(request_state *rstate) { AuthRecord *rr; - rr = read_rr_from_ipc_msg(rstate->msgdata, 0); + rr = read_rr_from_ipc_msg(rstate->msgdata, 0, 1); if (!rr) return; - mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec); + mDNS_ReconfirmByValue(gmDNS, &rr->resrec); abort_request(rstate); unlink_request(rstate); freeL("handle_reconfirm_request", rr); @@ -1746,7 +2102,7 @@ static void reset_connected_rstate(request_state *rstate) // 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) +static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl, int validate_flags) { char *rdata, name[256]; AuthRecord *rr; @@ -1756,7 +2112,15 @@ static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl) int storage_size; flags = get_flags(&msgbuf); - interfaceIndex = get_long(&msgbuf); + if (validate_flags && + !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) && + !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique)) + { + LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)"); + return NULL; + } + + interfaceIndex = get_long(&msgbuf); if (get_string(&msgbuf, name, 256) < 0) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); @@ -1777,7 +2141,7 @@ static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl) } bzero(rr, sizeof(AuthRecord)); // ok if oversized rdata not zero'd rr->resrec.rdata = &rr->rdatastorage; - rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex); if (!MakeDomainNameFromDNSNameString(&rr->resrec.name, name)) { LogMsg("ERROR: bad name: %s", name); @@ -1785,7 +2149,7 @@ static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl) return NULL; } rr->resrec.rrtype = type; - if ((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) + if ((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) rr->resrec.RecordType = kDNSRecordTypeShared; if ((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique) rr->resrec.RecordType = kDNSRecordTypeUnique; @@ -1835,7 +2199,7 @@ static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, requ *rep = create_reply(query_reply, len, request); (*rep)->rhdr->flags = 0; - (*rep)->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id); + (*rep)->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, id); (*rep)->rhdr->error = kDNSServiceErr_NoError; data = (*rep)->sdata; @@ -1935,7 +2299,7 @@ static int read_msg(request_state *rs) if (!rs->msgbuf) // allocate the buffer first time through { - rs->msgbuf = mallocL("read_msg", rs->hdr.datalen); + rs->msgbuf = mallocL("read_msg", rs->hdr.datalen + MSG_PAD_BYTES); if (!rs->msgbuf) { my_perror("ERROR: malloc"); @@ -1944,6 +2308,7 @@ static int read_msg(request_state *rs) } rs->msgdata = rs->msgbuf; } + bzero(rs->msgbuf, rs->hdr.datalen + MSG_PAD_BYTES); 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; } @@ -2160,11 +2525,7 @@ static void abort_request(request_state *rs) 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); + udsSupportRemoveFDFromEventLoop(rs->sd); rs->sd = -1; if (rs->errfd >= 0) close(rs->errfd); rs->errfd = -1; @@ -2214,4 +2575,64 @@ static void my_perror(char *errmsg) LogMsg("%s: %s", errmsg, strerror(errno)); } +// check that the message delivered by the client is sufficiently long to extract the required data from the buffer +// without overrunning it. +// returns 0 on success, -1 on error. + +static int validate_message(request_state *rstate) + { + uint32_t min_size; + + switch(rstate->hdr.op.request_op) + { + case resolve_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + (3 * sizeof(char)); // name, regtype, domain + break; + case query_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + sizeof(char) + // fullname + (2 * sizeof(uint16_t)); // type, class + break; + case browse_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + (2 * sizeof(char)); // regtype, domain + break; + case reg_service_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + (4 * sizeof(char)) + // name, type, domain, host + (2 * sizeof(uint16_t)); // port, textlen + break; + case enumeration_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t); // interface + break; + case reg_record_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + sizeof(char) + // fullname + (3 * sizeof(uint16_t)) + // type, class, rdlen + sizeof(uint32_t); // ttl + break; + case add_record_request: min_size = sizeof(DNSServiceFlags) + // flags + (2 * sizeof(uint16_t)) + // type, rdlen + sizeof(uint32_t); // ttl + break; + case update_record_request: min_size = sizeof(DNSServiceFlags) + // flags + sizeof(uint16_t) + // rdlen + sizeof(uint32_t); // ttl + break; + case remove_record_request: min_size = sizeof(DNSServiceFlags); // flags + break; + case reconfirm_record_request: min_size=sizeof(DNSServiceFlags) + // flags + sizeof(uint32_t) + // interface + sizeof(char) + // fullname + (3 * sizeof(uint16_t)); // type, class, rdlen + default: + LogMsg("ERROR: validate_message - unsupported request type: %d", rstate->hdr.op.request_op); + return -1; + } + + return (rstate->data_bytes >= min_size ? 0 : -1); + + } + diff --git a/mDNSShared/uds_daemon.h b/mDNSShared/uds_daemon.h new file mode 100644 index 0000000..0e25022 --- /dev/null +++ b/mDNSShared/uds_daemon.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + File: uds_daemon.h + + Contains: Interfaces necessary to talk to uds_daemon.c. + + Version: 1.0 + + Change History (most recent first): + +$Log: uds_daemon.h,v $ +Revision 1.3 2004/01/25 00:03:21 cheshire +Change to use mDNSVal16() instead of private PORT_AS_NUM() macro + +Revision 1.2 2004/01/24 08:46:26 bradley +Added InterfaceID<->Index platform interfaces since they are now used by all platforms for the DNS-SD APIs. + +Revision 1.1 2003/12/08 21:11:42 rpantos; +Changes necessary to support mDNSResponder on Linux. + +*/ + +#include "mDNSClientAPI.h" + + +/* Client interface: */ + +#define SRS_PORT(S) mDNSVal16((S)->RR_SRV.resrec.rdata->u.srv.port) + +extern int udsserver_init( mDNS *globalInstance); + +// takes the next scheduled event time, does idle work, and returns the updated nextevent time +extern mDNSs32 udsserver_idle(mDNSs32 nextevent); + +extern void udsserver_info(void); // print out info about current state + +extern void udsserver_handle_configchange(void); + +extern int udsserver_exit(void); // should be called prior to app exit + + +/* Routines that uds_daemon expects to link against: */ + +typedef void (*udsEventCallback)(void *context); + +extern mStatus udsSupportAddFDToEventLoop( int fd, udsEventCallback callback, void *context); +extern mStatus udsSupportRemoveFDFromEventLoop( int fd); diff --git a/mDNSVxWorks/mDNSVxWorks.c b/mDNSVxWorks/mDNSVxWorks.c index eb6683e..049f41c 100644 --- a/mDNSVxWorks/mDNSVxWorks.c +++ b/mDNSVxWorks/mDNSVxWorks.c @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -22,16 +24,37 @@ Contains: mDNS platform plugin for VxWorks. - Copyright: Copyright (C) 2002-2003 Apple Computer, Inc., All Rights Reserved. + Copyright: Copyright (C) 2002-2004 Apple Computer, Inc., All Rights Reserved. Change History (most recent first): $Log: mDNSVxWorks.c,v $ -Revision 1.7.2.2 2004/04/09 17:57:31 cheshire -Make sure to set the TxAndRx field so that duplicate suppression works correctly +Revision 1.16 2004/04/22 05:11:28 bradley +Added mDNSPlatformUTC for TSIG signed dynamic updates. + +Revision 1.15 2004/04/21 02:49:12 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.14 2004/04/09 17:43:04 cheshire +Make sure to set the McastTxRx field so that duplicate suppression works correctly + +Revision 1.13 2004/01/27 20:15:24 cheshire +: Time to prune obsolete code for listening on port 53 -Revision 1.7.2.1 2004/04/03 21:31:20 bradley -Integrated changes from TOT to remove legacy port 53 support. +Revision 1.12 2004/01/24 09:12:37 bradley +Avoid TOS socket options to workaround a TOS routing problem with VxWorks and multiple interfaces +when sending unicast responses, which resulted in packets going out the wrong interface. + +Revision 1.11 2004/01/24 04:59:16 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.10 2003/11/14 21:27:09 cheshire +: Security: Crashing bug in mDNSResponder +Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. + +Revision 1.9 2003/11/14 20:59:09 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. Revision 1.8 2003/10/28 10:08:27 bradley Removed legacy port 53 support as it is no longer needed. @@ -110,7 +133,6 @@ mDNS platform plugin for VxWorks. #endif #include "mDNSClientAPI.h" -#include "mDNSPlatformFunctions.h" #include "mDNSVxWorks.h" @@ -442,7 +464,6 @@ mStatus const DNSMessage * const inMsg, const mDNSu8 * const inMsgEnd, mDNSInterfaceID inInterfaceID, - mDNSIPPort inSrcPort, const mDNSAddr * inDstIP, mDNSIPPort inDstPort ) { @@ -451,8 +472,6 @@ mStatus struct sockaddr_in addr; int n; - DEBUG_UNUSED( inSrcPort ); - dlog( kDebugLevelChatty, DEBUG_NAME "platform send UDP\n" ); // Check parameters. @@ -509,6 +528,43 @@ exit: return( err ); } +//=========================================================================================================================== +// Connection-oriented (TCP) functions +//=========================================================================================================================== + +mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, + TCPConnectionCallback callback, void *context, int *descriptor) + { + (void)dst; // Unused + (void)dstport; // Unused + (void)InterfaceID; // Unused + (void)callback; // Unused + (void)context; // Unused + (void)descriptor; // Unused + return(mStatus_UnsupportedErr); + } + +mDNSexport void mDNSPlatformTCPCloseConnection(int sd) + { + (void)sd; // Unused + } + +mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) + { + (void)sd; // Unused + (void)buf; // Unused + (void)buflen; // Unused + return(0); + } + +mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len) + { + (void)sd; // Unused + (void)msg; // Unused + (void)len; // Unused + return(0); + } + //=========================================================================================================================== // mDNSPlatformLock //=========================================================================================================================== @@ -668,6 +724,15 @@ mDNSs32 mDNSPlatformTimeNow( void ) return( (mDNSs32) tickGet() ); } +//=========================================================================================================================== +// mDNSPlatformUTC +//=========================================================================================================================== + +mDNSexport mDNSs32 mDNSPlatformUTC( void ) +{ + return( -1 ); +} + //=========================================================================================================================== // mDNSPlatformInterfaceNameToID //=========================================================================================================================== @@ -990,7 +1055,7 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inA item->hostSet.ip.type = mDNSAddrType_IPv4; item->hostSet.ip.ip.v4.NotAnInteger = ipv4->sin_addr.s_addr; item->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses; - item->hostSet.TxAndRx = mDNStrue; + item->hostSet.McastTxRx = mDNStrue; err = mDNS_RegisterInterface( inMDNS, &item->hostSet ); require_noerr( err, exit ); diff --git a/mDNSVxWorks/mDNSVxWorks.h b/mDNSVxWorks/mDNSVxWorks.h index de026a4..537c0ee 100644 --- a/mDNSVxWorks/mDNSVxWorks.h +++ b/mDNSVxWorks/mDNSVxWorks.h @@ -3,6 +3,8 @@ * * @APPLE_LICENSE_HEADER_START@ * + * 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 diff --git a/mDNSWindows/Applications/DLL/dllmain.c b/mDNSWindows/Applications/DLL/dllmain.c new file mode 100644 index 0000000..db9906e --- /dev/null +++ b/mDNSWindows/Applications/DLL/dllmain.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: dllmain.c,v $ +Revision 1.1 2004/02/21 04:16:50 bradley +DLL wrapper for DNS-SD API. + +*/ + +#include "DNSSD.h" + +BOOL APIENTRY DllMain( HANDLE inModule, DWORD inReason, LPVOID inReserved ) +{ + (void) inModule; + (void) inReserved; + + switch( inReason ) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return( TRUE ); +} diff --git a/mDNSWindows/Applications/DLL/dnssd.def b/mDNSWindows/Applications/DLL/dnssd.def new file mode 100644 index 0000000..587489e --- /dev/null +++ b/mDNSWindows/Applications/DLL/dnssd.def @@ -0,0 +1,57 @@ +; +; Copyright (c) 2003-2004 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.def,v $ +; Revision 1.2 2004/03/19 10:07:14 bradley +; Export all DNS-SD API symbols from the DLL so they can be used by clients. +; +; Revision 1.1 2004/02/21 04:16:50 bradley +; DLL wrapper for DNS-SD API. +; +; +; + +LIBRARY DNSSD + +EXPORTS + DNSServiceInitialize + DNSServiceFinalize + DNSServiceCheckVersion + DNSServiceCopyProperty + DNSServiceReleaseProperty + DNSServiceRefSockFD + DNSServiceProcessResult + DNSServiceRefDeallocate + DNSServiceEnumerateDomains + DNSServiceRegister + DNSServiceAddRecord + DNSServiceUpdateRecord + DNSServiceRemoveRecord + DNSServiceBrowse + DNSServiceResolve + DNSServiceConstructFullName + DNSServiceCreateConnection + DNSServiceRegisterRecord + DNSServiceQueryRecord + DNSServiceReconfirmRecord diff --git a/mDNSWindows/Applications/DLL/dnssd.vcproj b/mDNSWindows/Applications/DLL/dnssd.vcproj new file mode 100644 index 0000000..ad7c1d1 --- /dev/null +++ b/mDNSWindows/Applications/DLL/dnssd.vcproj @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.sln b/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.sln index fe6ce77..5ce1a0a 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.sln +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.sln @@ -3,15 +3,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Application", "ApplicationV EndProject Global GlobalSection(SolutionConfiguration) = preSolution - ConfigName.0 = All - ConfigName.1 = Debug - ConfigName.2 = Release + ConfigName.0 = Debug + ConfigName.1 = Release EndGlobalSection GlobalSection(ProjectDependencies) = postSolution EndGlobalSection GlobalSection(ProjectConfiguration) = postSolution - {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.All.ActiveCfg = All|Win32 - {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.All.Build.0 = All|Win32 {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 diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj b/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj index 89316d2..5d72e30 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj @@ -16,12 +16,12 @@ IntermediateDirectory="Debug" ConfigurationType="1" UseOfMFC="1" - CharacterSet="2"> + CharacterSet="1"> + CompileAs="2"/> @@ -85,17 +85,17 @@ IntermediateDirectory=".\Release" ConfigurationType="1" UseOfMFC="1" - CharacterSet="2" + CharacterSet="1" WholeProgramOptimization="FALSE"> + CompileAs="2"/> - - - - - - - - - - - - + + + + @@ -226,17 +205,47 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.sln b/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.sln index 0bdb334..bdaaa3b 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.sln +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.sln @@ -5,13 +5,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Application", "ApplicationV EndProject Global GlobalSection(SolutionConfiguration) = preSolution - All = All Debug = Debug Release = Release EndGlobalSection GlobalSection(ProjectConfiguration) = postSolution - {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.All.ActiveCfg = All|Win32 - {EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.All.Build.0 = All|Win32 {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 diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj b/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj index 4f9b2bd..f3b4e9a 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj @@ -16,12 +16,12 @@ IntermediateDirectory="Debug" ConfigurationType="1" UseOfMFC="1" - CharacterSet="2"> + CharacterSet="1"> @@ -91,17 +91,17 @@ IntermediateDirectory=".\Release" ConfigurationType="1" UseOfMFC="1" - CharacterSet="2" + CharacterSet="1" WholeProgramOptimization="FALSE"> - - - - - - - - - - - - - - - @@ -222,6 +189,12 @@ + + + + @@ -246,17 +219,47 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.ico b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.ico index a992327973c4f716e13ba43b7803f59885b99069..b0a86399055437af8f9244e39c5938ab534824d7 100644 GIT binary patch literal 3638 zcmeH~F^d~V6o8+i3pxg&-PPV&37Fi)MTqNZVhC}t0;(3=xOS=~jmpI-0+BJ;WdZ?{ z?OY@wDbn~CC>*4ShorNc7|vW_&@MzKjUa)^_hu#C85Kz#Ji52D^WMyR-@cihofGNE zYjSiXp?tqD@(y=#+_pb?UF27E@6tR%K14c#Ld#xTg=>eaN+>fC{$w_5E3Vwid_I@? z?{iuH@}2DM?aBWBzV!QjNs>e^T(}??FJ6>OmoCZW%a`TGjT@5Zxm>$;O|D$IBD2rD zT)*z+D|sxwaq{!yKjiWA=knzbKN83y1yUidZCZ2|okeF==i4p?`T~7{zCd4~*I^a< z3VlVRLSLb$5{KTQcjz4ohu+inw7mJof34?{f!eC*rFa&fMEDRO~3xkEhdUSADIIMDbA2NW3T7MPd5cK-UuoMI@2vp#- zfF+~>W5G=UO94wr3mgR;1snw&1sow&a1<~UFcdIo>Iygt7z!91u{_t1LgKVP+9AD~ z9jJ(1(JQ2y*cD6_K`R1OFjd5^FjhFJh+PpoOh<@~p7<5*Lq#5R z+Q>?HdNkBrbsEWmBc6lJ!QqG(rdBz~frG;-2M-pen?Vj12a5y35il$PSR4Z#og4}W z3x5cHm=gz!(|iOLT@5-5=wR?Lco;kk9tICXSWYlF7(5If1_uLf5caQNU``xNrFd@DC^-jrLnZpq!dcjf;5`|{|~BYF7n zp*(r=M4mom|49kHGVCCh8lWtd zMlw2Vur~EnA1|1VsS~qf&ywk;t1*jR`!I^m>^sqonVNI<9s2)bH^bpDP19-JG%q>R zu=Vsj@L*uZXE0cYq1BHgcIH8Asp~C7eSm5lnK7Q5rmm-fG-jeh*BW2rFzM^Zss|<+ zMw=w-=rKK;0ci;(C8BBKVIA^`w|*Fynn7^Lg&wAfIBLhK4;>$dQ=cuEb>!#X{iyo6UH&+E;5TMq|)>^f;}39s&^p>_noEpUF2}}vS9xkS&AH3{)(@*p+gUWaChm@+TZ@PQs= zQ`Lv`@&)pn$e(LD>9PNHMQ;Bj^5)kf@4q4P!FwWaeY)N%**kCF5%~ugzb5gg5S8n~qp;a#N$@HF^FSRK|zeCX_fu1Ub{D8vx)c%5Ad+A9O z=aggqLTI3cXqH`R7uJE2?W|-gA@+y?Y2VE3zPC?5_kO*49RwYE27{oE?^{9eCiwC0 z{qxjR&c?Sff?=CNAAdM5B_hUHZopkKibfNLTnLQydcpSoG>Z0jt+Z5t>v)-l)9G}7 zhx#QKDAy|n(Ps|Go$r+6x`fOaunD_}JA zp-HTZz%g#ynOvs`k8xPTQ)Z~ZXbNKtgZyDw^QKEI6i911w=$PXKfWiOOCeMNx+@1OCvzcM4usju_RJEj>s;WoL*JC$xx4RPyZhe4IiLNzCSSgA_d$@VAn0FTUH2}o z44hy7>74()?7+=c2Kq||Wj}+v!;9|X@GSV0o>6%x>ugbG-TB)L6wG^zkBk04=iGn& Cfwy4* diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc index 1d9c2eb..cf2f292 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc @@ -73,51 +73,72 @@ 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 +IDD_CHOOSER_DIALOG DIALOGEX 0, 0, 512, 316 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | + WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW -CAPTION "Rendezvous Browser for Windows" +CAPTION "Rendezvous Browser" MENU IDR_CHOOSER_DIALOG_MENU -FONT 8, "MS Sans Serif", 0, 0, 0x0 +FONT 8, "MS Shell Dlg", 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 + LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,8,8,268,256 + CONTROL "",IDC_CHOOSER_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | + LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,282,8,224,170 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 + LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,8,272,268,38 + GROUPBOX "Information",IDC_STATIC,282,182,224,128 + RTEXT "Name:",IDC_STATIC,288,195,38,8 + EDITTEXT IDC_INFO_NAME_TEXT,330,195,168,10,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER,WS_EX_STATICEDGE + RTEXT "IP address:",IDC_STATIC,288,208,38,8 + EDITTEXT IDC_INFO_IP_TEXT,330,208,168,10,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER,WS_EX_STATICEDGE + RTEXT "Interface:",IDC_STATIC,288,221,38,8 + EDITTEXT IDC_INFO_INTERFACE_TEXT,330,221,168,10,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER,WS_EX_STATICEDGE + RTEXT "Host Name:",IDC_STATIC,287,234,38,8 + EDITTEXT IDC_INFO_HOST_NAME_TEXT,330,234,168,10,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_BORDER,WS_EX_STATICEDGE + RTEXT "Text:",IDC_STATIC,288,247,38,8 + EDITTEXT IDC_INFO_TEXT_TEXT,330,247,168,57,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT + WS_BORDER,WS_EX_STATICEDGE END IDD_ABOUT_DIALOG DIALOGEX 0, 0, 244, 73 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION CAPTION "About Rendezvous Browser" -FONT 8, "MS Sans Serif", 0, 0, 0x0 +FONT 8, "MS Shell Dlg", 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.", + LTEXT "Rendezvous Browser",IDC_ABOUT_APP_NAME_TEXT,44,11,192, + 12 + LTEXT "Version 1.2d1",IDC_ABOUT_APP_VERSION_TEXT,44,25,192,8 + LTEXT "Copyright (C) 2002-2004 Apple Computer, Inc.", IDC_ABOUT_COPYRIGHT_TEXT,4,60,156,8 DEFPUSHBUTTON "OK",IDOK,192,52,44,14 END +IDD_LOGIN DIALOGEX 0, 0, 180, 89 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Login" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Enter a username and password. Leave blank to use the default username and/or password.", + IDC_STATIC,8,8,156,16,NOT WS_GROUP + RTEXT "Username:",IDC_STATIC,10,34,36,8,NOT WS_GROUP + EDITTEXT IDC_LOGIN_USERNAME_TEXT,50,32,118,12,ES_AUTOHSCROLL + RTEXT "Password:",IDC_STATIC,10,50,36,8,NOT WS_GROUP + EDITTEXT IDC_LOGIN_PASSWORD_TEXT,50,48,118,12,ES_PASSWORD | + ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,129,70,44,14 + PUSHBUTTON "Cancel",IDCANCEL,77,70,44,14 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -141,12 +162,11 @@ BEGIN 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 "LegalCopyright", "Copyright (C) 2002-2004 Apple Computer, Inc." VALUE "OriginalFilename", "RendezvousBrowser.exe" VALUE "ProductName", "Rendezvous Browser for Windows" VALUE "ProductVersion", "1, 0, 0, 1" @@ -237,6 +257,27 @@ BEGIN END +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_CHOOSER_DIALOG, DIALOG + BEGIN + RIGHTMARGIN, 468 + END + + IDD_LOGIN, DIALOG + BEGIN + BOTTOMMARGIN, 62 + END +END +#endif // APSTUDIO_INVOKED + + ///////////////////////////////////////////////////////////////////////////// // // String Table @@ -247,9 +288,10 @@ 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_SERVICE_COLUMN_TYPE "Services" IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME "Name" IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME "IP Address" + IDS_CHOOSER_SERVICE_COLUMN_DESC "Description" END #endif // English (U.S.) resources diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc2 b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc2 index bf1983f..9c76766 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc2 +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc2 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,6 +23,9 @@ Change History (most recent first): $Log: Application.rc2,v $ +Revision 1.2 2004/01/30 02:56:33 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:06:46 bradley Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Resource.h b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Resource.h index 5329fc1..62602a4 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Resource.h +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Resource.h @@ -6,8 +6,10 @@ #define IDS_CHOOSER_DOMAIN_COLUMN_NAME 102 #define IDP_SOCKETS_INIT_FAILED 103 #define IDS_CHOOSER_SERVICE_COLUMN_NAME 104 +#define IDS_CHOOSER_SERVICE_COLUMN_TYPE 104 #define IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME 105 #define IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME 106 +#define IDS_CHOOSER_SERVICE_COLUMN_DESC 107 #define IDC_NAME_TEXT2 124 #define IDC_INFO_NAME_TEXT 124 #define IDC_DESCRIPTION_TEXT2 125 @@ -17,9 +19,12 @@ #define IDC_IP_TEXT3 127 #define IDC_INFO_INTERFACE_TEXT 127 #define IDR_MAIN_ICON 128 +#define IDC_INFO_INTERFACE_TEXT2 128 +#define IDC_INFO_HOST_NAME_TEXT 128 #define IDR_CHOOSER_DIALOG_MENU 136 #define IDD_CHOOSER_DIALOG 143 #define IDD_ABOUT_DIALOG 144 +#define IDD_LOGIN 145 #define IDR_CHOOSER_DIALOG_MENU_ACCELERATORS 146 #define IDC_CHOOSER_LIST 1000 #define IDC_SERVICE_LIST2 1001 @@ -30,6 +35,9 @@ #define IDC_ABOUT_APP_VERSION_TEXT 1106 #define IDC_ABOUT_COPYRIGHT_TEXT 1107 #define IDC_ABOUT_APP_ICON 1108 +#define IDC_LOGIN_USERNAME_TEXT 1182 +#define IDC_EDIT2 1183 +#define IDC_LOGIN_PASSWORD_TEXT 1183 #define ID_FILE_EXIT 32771 #define ID_HELP_ABOUT 32806 @@ -39,7 +47,7 @@ #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_CONTROL_VALUE 1185 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp index cd8cafb..50142c2 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: AboutDialog.cpp,v $ +Revision 1.2 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:06:47 bradley Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.h b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.h index 11c9149..ee97731 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.h +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: AboutDialog.h,v $ +Revision 1.2 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:06:47 bradley Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.cpp b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.cpp index 396b7e9..1fb0723 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.cpp +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.cpp @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: Application.cpp,v $ +Revision 1.2 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:06:47 bradley Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. @@ -45,6 +50,8 @@ Rendezvous Browser for Windows #include +#include "StdAfx.h" + #include "DNSServices.h" #include "Application.h" @@ -94,14 +101,6 @@ BOOL Application::InitInstance() { DNSStatus err; - // WinSock initialization. - - if( !AfxSocketInit() ) - { - AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); - return( FALSE ); - } - // Standard MFC initialization. #if( !defined( AFX_DEPRECATED ) ) diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.h b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.h index e174e4f..30705d3 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.h +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: Application.h,v $ +Revision 1.2 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:06:47 bradley Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp index ddd81f9..162acda 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,36 @@ Change History (most recent first): $Log: ChooserDialog.cpp,v $ +Revision 1.10 2004/04/23 01:19:41 bradley +Changed TXT record new line delimiter from \n to \r\n so it works now that it is an edit text. + +Revision 1.9 2004/03/07 05:51:04 bradley +Updated service type list table to include all service types from dns-sd.org as of 2004-03-06. +Added separate Service Type and Service Description columns so both are show in the window. + +Revision 1.8 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.7 2003/12/25 03:42:04 bradley +Added login dialog to get username/password when going to FTP sites. Added more services. + +Revision 1.6 2003/10/31 12:18:30 bradley +Added display of the resolved host name. Show separate TXT record entries on separate lines. + +Revision 1.5 2003/10/16 09:21:56 bradley +Ignore non-IPv4 resolves until mDNS on Windows supports IPv6. + +Revision 1.4 2003/10/10 03:41:29 bradley +Changed HTTP double-click handling to work with or without the path= prefix in the TXT record. + +Revision 1.3 2003/10/09 19:50:40 bradley +Sort service type list by description. + +Revision 1.2 2003/10/09 19:41:29 bradley +Changed quit handling to go through normal close path so dialog is freed on quit. Integrated changes +from Andrew van der Stock for the addition of an _rfb._tcp service type for a VNC Remote Framebuffer +Server for KDE support. Widened service type list to handle larger service type descriptions. + Revision 1.1 2003/08/21 02:06:47 bradley Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. @@ -65,6 +97,7 @@ Rendezvous Browser for Windows #include "Application.h" #include "AboutDialog.h" +#include "LoginDialog.h" #include "Resource.h" #include "ChooserDialog.h" @@ -93,29 +126,25 @@ enum // Domain List -#define kDomainListDefaultDomainColumnIndex 0 -#define kDomainListDefaultDomainColumnWidth 140 +#define kDomainListDefaultDomainColumnWidth 164 // Service List - -#define kServiceListDefaultServiceColumnIndex 0 -#define kServiceListDefaultServiceColumnWidth 140 + +#define kServiceListDefaultServiceColumnTypeWidth 146 +#define kServiceListDefaultServiceColumnDescWidth 230 // Chooser List -#define kChooserListDefaultNameColumnIndex 0 -#define kChooserListDefaultNameColumnWidth 162 - -#define kChooserListDefaultIPColumnIndex 1 -#define kChooserListDefaultIPColumnWidth 126 +#define kChooserListDefaultNameColumnWidth 190 +#define kChooserListDefaultIPColumnWidth 120 // 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 ) +#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 == @@ -135,18 +164,138 @@ struct KnownServiceEntry 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 }, + { "_accountedge._tcp.", "MYOB AccountEdge", "", false }, + { "_aecoretech._tcp.", "Apple Application Engineering Services", "", false }, + { "_afpovertcp._tcp.", "Apple File Sharing (AFP)", "afp://", false }, + { "_airport._tcp.", "AirPort Base Station", "", false }, + { "_apple-sasl._tcp.", "Apple Password Server", "", false }, + { "_aquamon._tcp.", "AquaMon", "", false }, + { "_async._tcp", "address-o-sync", "", false }, + { "_auth._tcp.", "Authentication Service", "", false }, + { "_bootps._tcp.", "Bootstrap Protocol Server", "", false }, + { "_bousg._tcp.", "Bag Of Unusual Strategy Games", "", false }, + { "_browse._udp.", "DNS Service Discovery", "", false }, + { "_cheat._tcp.", "The Cheat", "", false }, + { "_chess._tcp", "Project Gridlock", "", false }, + { "_chfts._tcp", "Fluid Theme Server", "", false }, + { "_clipboard._tcp", "Clipboard Sharing", "", false }, + { "_contactserver._tcp.", "Now Up-to-Date & Contact", "", false }, + { "_cvspserver._tcp", "CVS PServer", "", false }, + { "_cytv._tcp.", "CyTV Network streaming for Elgato EyeTV", "", false }, + { "_daap._tcp.", "Digital Audio Access Protocol (iTunes)", "daap://", false }, + { "_distcc._tcp", "Distributed Compiler", "", false }, + { "_dns-sd._udp", "DNS Service Discovery", "", false }, + { "_dpap._tcp.", "Digital Picture Access Protocol (iPhoto)", "", false }, + { "_earphoria._tcp.", "Earphoria", "", false }, + { "_ecbyesfsgksc._tcp.", "Net Monitor Anti-Piracy Service", "", false }, + { "_eheap._tcp.", "Interactive Room Software", "", false }, + { "_embrace._tcp.", "DataEnvoy", "", false }, + { "_eppc._tcp.", "Remote AppleEvents", "eppc://", false }, + { "_exec._tcp.", "Remote Process Execution", "", false }, + { "_facespan._tcp.", "FaceSpan", "", false }, + { "_fjork._tcp.", "Fjork", "", false }, + { "_ftp._tcp.", "File Transfer (FTP)", "ftp://", false }, + { "_ftpcroco._tcp.", "Crocodile FTP Server", "", false }, + { "_gbs-smp._tcp.", "SnapMail", "", false }, + { "_gbs-stp._tcp.", "SnapTalk", "", false }, + { "_grillezvous._tcp.", "Roxio ToastAnywhere(tm) Recorder Sharing", "", false }, + { "_h323._tcp.", "H.323", "", false }, + { "_hotwayd._tcp", "Hotwayd", "", false }, + { "_http._tcp.", "Web Server (HTTP)", "http://", true }, + { "_hydra._tcp", "SubEthaEdit", "", false }, + { "_ica-networking._tcp.", "Image Capture Networking", "", false }, + { "_ichalkboard._tcp.", "iChalk", "", false }, + { "_ichat._tcp.", "iChat", "ichat://", false }, + { "_iconquer._tcp.", "iConquer", "", false }, + { "_imap._tcp.", "Internet Message Access Protocol", "", false }, + { "_imidi._tcp.", "iMidi", "", false }, + { "_ipp._tcp.", "Printer (IPP)", "ipp://", false }, + { "_ishare._tcp.", "iShare", "", false }, + { "_isparx._tcp.", "iSparx", "", false }, + { "_istorm._tcp", "iStorm", "", false }, + { "_iwork._tcp.", "iWork Server", "", false }, + { "_liaison._tcp.", "Liaison", "", false }, + { "_login._tcp.", "Remote Login a la Telnet", "", false }, + { "_lontalk._tcp.", "LonTalk over IP (ANSI 852)", "", false }, + { "_lonworks._tcp.", "Echelon LNS Remote Client", "", false }, + { "_macfoh-remote._tcp.", "MacFOH Remote", "", false }, + { "_moneyworks._tcp.", "MoneyWorks", "", false }, + { "_mp3sushi._tcp", "MP3 Sushi", "", false }, + { "_mttp._tcp.", "MenuTunes Sharing", "", false }, + { "_ncbroadcast._tcp.", "Network Clipboard Broadcasts", "", false }, + { "_ncdirect._tcp.", "Network Clipboard Direct Transfers", "", false }, + { "_ncsyncserver._tcp.", "Network Clipboard Sync Server", "", false }, + { "_newton-dock._tcp.", "Escale", "", false }, + { "_nfs._tcp", "NFS", "", false }, + { "_nssocketport._tcp.", "DO over NSSocketPort", "", false }, + { "_omni-bookmark._tcp.", "OmniWeb", "", false }, + { "_openbase._tcp.", "OpenBase SQL", "", false }, + { "_p2pchat._tcp.", "Peer-to-Peer Chat", "", false }, + { "_pdl-datastream._tcp.", "Printer (PDL)", "pdl://", false }, + { "_poch._tcp.", "Parallel OperatiOn and Control Heuristic", "", false }, + { "_pop_2_ambrosia._tcp.", "Pop-Pop", "", false }, + { "_pop3._tcp", "POP3 Server", "", false }, + { "_postgresql._tcp", "PostgreSQL Server", "", false }, + { "_presence._tcp", "iChat AV", "", false }, + { "_printer._tcp.", "Printer (LPR)", "lpr://", false }, + { "_ptp._tcp.", "Picture Transfer (PTP)", "ptp://", false }, + { "_register._tcp", "DNS Service Discovery", "", false }, + { "_rendezvouspong._tcp", "RendezvousPong", "", false }, + { "_rfb._tcp.", "Remote Frame Buffer", "", false }, + { "_riousbprint._tcp.", "Remote I/O USB Printer Protocol", "", false }, + { "_rtsp._tcp.", "Real Time Stream Control Protocol", "", false }, + { "_safarimenu._tcp", "Safari Menu", "", false }, + { "_scone._tcp", "Scone", "", false }, + { "_sdsharing._tcp.", "Speed Download", "", false }, + { "_seeCard._tcp.", "seeCard", "", false }, + { "_services._udp.", "DNS Service Discovery", "", false }, + { "_shell._tcp.", "like exec, but automatic authentication", "", false }, + { "_shout._tcp.", "Shout", "", false }, + { "_shoutcast._tcp", "Nicecast", "", false }, + { "_smb._tcp.", "Windows File Sharing (SMB)", "smb://", false }, + { "_soap._tcp.", "Simple Object Access Protocol", "", false }, + { "_spincrisis._tcp.", "Spin Crisis", "", false }, + { "_spl-itunes._tcp.", "launchTunes", "", false }, + { "_spr-itunes._tcp.", "netTunes", "", false }, + { "_ssh._tcp.", "Secure Shell (SSH)", "ssh://", false }, + { "_ssscreenshare._tcp", "Screen Sharing", "", false }, + { "_sge-exec._tcp", "Sun Grid Engine (Execution Host)", "", false }, + { "_sge-qmaster._tcp", "Sun Grid Engine (Master)", "", false }, + { "_stickynotes._tcp", "Sticky Notes", "", false }, + { "_strateges._tcp", "Strateges", "", false }, + { "_sxqdea._tcp", "Synchronize! Pro X", "", false }, + { "_sybase-tds._tcp", "Sybase Server", "", false }, + { "_tce._tcp", "Power Card", "", false }, + { "_teamlist._tcp", "ARTIS Team Task", "", false }, + { "_teleport._tcp", "teleport", "", false }, + { "_telnet._tcp.", "Telnet", "telnet://", false }, + { "_tftp._tcp.", "Trivial File Transfer (TFTP)", "tftp://", false }, + { "_tinavigator._tcp.", "TI Navigator", "", false }, + { "_tivo_servemedia._tcp", "TiVo", "", false }, + { "_upnp._tcp.", "Universal Plug and Play", "", false }, + { "_utest._tcp.", "uTest", "", false }, + { "_vue4rendercow._tcp", "VueProRenderCow", "", false }, + { "_webdav._tcp.", "WebDAV", "webdav://", false }, + { "_whamb._tcp.", "Whamb", "", false }, + { "_workstation._tcp", "Macintosh Manager", "", false }, + { "_ws._tcp", "Web Services", "", false }, + { "_xserveraid._tcp.", "Xserve RAID", "xsr://", false }, + { "_xsync._tcp.", "Xserve RAID Synchronization", "", false }, + + { "", "", "", false }, + + // Unofficial and invalid service types that will be phased out: + + { "_clipboardsharing._tcp.", "ClipboardSharing", "", false }, + { "_MacOSXDupSuppress._tcp.", "Mac OS X Duplicate Suppression", "", false }, + { "_netmonitorserver._tcp.", "Net Monitor Server", "", false }, + { "_networkclipboard._tcp.", "Network Clipboard", "", false }, + { "_slimdevices_slimp3_cli._tcp.", "SliMP3 Server Command-Line Interface", "", false }, + { "_slimdevices_slimp3_http._tcp.", "SliMP3 Server Web Interface", "", false }, + { "_tieducationalhandhelddevice._tcp.", "TI Connect Manager", "", false }, + { "_tivo_servemedia._tcp.", "TiVo", "", false }, + + { NULL, NULL, NULL, false }, }; #if 0 @@ -191,6 +340,7 @@ static void static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString ); static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject ); +static DWORD StringObjectToUTF8String( CString &inObject, std::string &outUTF8 ); #if 0 #pragma mark == Message Map == @@ -282,6 +432,7 @@ void ChooserDialog::DoDataExchange( CDataExchange *pDX ) BOOL ChooserDialog::OnInitDialog( void ) { + HICON icon; BOOL result; CString tempString; DNSStatus err; @@ -289,7 +440,17 @@ BOOL ChooserDialog::OnInitDialog( void ) // Initialize our parent. CDialog::OnInitDialog(); - + + // Set up the window icon. + + icon = AfxGetApp()->LoadIcon( IDR_MAIN_ICON ); + assert( icon ); + if( icon ) + { + SetIcon( icon, TRUE ); // Set big icon + SetIcon( icon, FALSE ); // Set small icon + } + // Set up the Domain List. result = tempString.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME ); @@ -298,9 +459,13 @@ BOOL ChooserDialog::OnInitDialog( void ) // Set up the Service List. - result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_NAME ); + result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE ); assert( result ); - mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnWidth ); + mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnTypeWidth ); + + result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC ); + assert( result ); + mServiceList.InsertColumn( 1, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnDescWidth ); PopulateServicesList(); @@ -411,7 +576,7 @@ void ChooserDialog::OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMe void ChooserDialog::OnExit() { - AfxPostQuitMessage( 0 ); + OnClose(); } //=========================================================================================================================== @@ -494,13 +659,17 @@ void ChooserDialog::OnServiceListChanged( NMHDR *pNMHDR, LRESULT *pResult ) if( ( selectedType >= 0 ) && ( selectedDomain >= 0 ) ) { - CString type; - CString domain; - - type = mServiceTypes[ selectedType ].serviceType.c_str(); - domain = mDomainList.GetItemText( selectedDomain, 0 ); + CString s; + std::string utf8; + const char * type; - StartBrowsing( type, domain ); + s = mDomainList.GetItemText( selectedDomain, 0 ); + StringObjectToUTF8String( s, utf8 ); + type = mServiceTypes[ selectedType ].serviceType.c_str(); + if( *type != '\0' ) + { + StartBrowsing( type, utf8.c_str() ); + } } if( pResult ) @@ -554,25 +723,81 @@ void ChooserDialog::OnChooserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult ) } if( service->serviceType ) { - // Create a URL representing the service instance. Special case for SMB (no port number). + const char * text; - if( strcmp( service->serviceType, "_smb._tcp" ) == 0 ) + // Create a URL representing the service instance. + + if( strcmp( service->serviceType, "_smb._tcp." ) == 0 ) { - url.Format( "%s%s/", service->urlScheme, (const char *) p->ip.c_str() ); + // Special case for SMB (no port number). + + url.Format( TEXT( "%s%s/" ), service->urlScheme, (const char *) p->ip.c_str() ); } - else + else if( strcmp( service->serviceType, "_ftp._tcp." ) == 0 ) { - const char * text; + // Special case for FTP to get login info. + + LoginDialog dialog; + CString username; + CString password; + + if( !dialog.GetLogin( username, password ) ) + { + goto exit; + } + + // Build URL in the following format: + // + // ftp://[username[:password]@] + + url += service->urlScheme; + if( username.GetLength() > 0 ) + { + url += username; + if( password.GetLength() > 0 ) + { + url += ':'; + url += password; + } + url += '@'; + } + url += p->ip.c_str(); + } + else if( strcmp( service->serviceType, "_http._tcp." ) == 0 ) + { + // Special case for HTTP to exclude "path=" if present. text = service->useText ? p->text.c_str() : ""; - url.Format( "%s%s/%s", service->urlScheme, (const char *) p->ip.c_str(), text ); + if( strncmp( text, "path=", 5 ) == 0 ) + { + text += 5; + } + if( *text != '/' ) + { + url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text ); + } + else + { + url.Format( TEXT( "%s%s%s" ), service->urlScheme, (const char *) p->ip.c_str(), text ); + } + } + else + { + text = service->useText ? p->text.c_str() : ""; + url.Format( TEXT( "%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 ); + { + CWaitCursor waitCursor; + + ShellExecute( NULL, TEXT( "open" ), url, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL ); + } } } + +exit: *pResult = 0; } @@ -592,7 +817,9 @@ void ChooserDialog::OnCancel() void ChooserDialog::PopulateServicesList( void ) { ServiceTypeVector::iterator i; - CString name; + CString type; + CString desc; + std::string tmp; // Add a fixed list of known services. @@ -615,8 +842,22 @@ void ChooserDialog::PopulateServicesList( void ) for( i = mServiceTypes.begin(); i != mServiceTypes.end(); ++i ) { - UTF8StringToStringObject( ( *i ).description.c_str(), name ); - mServiceList.InsertItem( mServiceList.GetItemCount(), name ); + const char * p; + const char * q; + + p = ( *i ).serviceType.c_str(); + if( *p == '_' ) ++p; // Skip leading '_'. + q = strchr( p, '.' ); // Find first '.'. + if( q ) tmp.assign( p, (size_t)( q - p ) ); // Use only up to the first '.'. + else tmp.assign( p ); // No '.' so use the entire string. + UTF8StringToStringObject( tmp.c_str(), type ); + UTF8StringToStringObject( ( *i ).description.c_str(), desc ); + + int n; + + n = mServiceList.GetItemCount(); + mServiceList.InsertItem( n, type ); + mServiceList.SetItemText( n, 1, desc ); } // Select the first service type by default. @@ -633,13 +874,16 @@ void ChooserDialog::PopulateServicesList( void ) void ChooserDialog::UpdateInfoDisplay( void ) { - int selectedItem; - std::string name; - CString s; - std::string ip; - std::string ifIP; - std::string text; - CWnd * item; + int selectedItem; + std::string name; + CString s; + std::string ip; + std::string ifIP; + std::string text; + std::string textNewLines; + std::string hostName; + CWnd * item; + std::string::iterator i; // Display the service instance if it is selected. Otherwise, clear all the info. @@ -651,14 +895,16 @@ void ChooserDialog::UpdateInfoDisplay( void ) assert( selectedItem < (int) mServiceInstances.size() ); p = &mServiceInstances[ selectedItem ]; - name = p->name; - ip = p->ip; - ifIP = p->ifIP; - text = p->text; - + name = p->name; + ip = p->ip; + ifIP = p->ifIP; + text = p->text; + hostName = p->hostName; + // Sync up the list items with the actual data (IP address may change). - mChooserList.SetItemText( selectedItem, 1, ip.c_str() ); + UTF8StringToStringObject( ip.c_str(), s ); + mChooserList.SetItemText( selectedItem, 1, s ); } // Name @@ -672,23 +918,39 @@ void ChooserDialog::UpdateInfoDisplay( void ) item = (CWnd *) this->GetDlgItem( IDC_INFO_IP_TEXT ); assert( item ); - item->SetWindowText( ip.c_str() ); + UTF8StringToStringObject( ip.c_str(), s ); + item->SetWindowText( s ); // Interface item = (CWnd *) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT ); assert( item ); - item->SetWindowText( ifIP.c_str() ); + UTF8StringToStringObject( ifIP.c_str(), s ); + item->SetWindowText( s ); + + item = (CWnd *) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT ); + assert( item ); + UTF8StringToStringObject( hostName.c_str(), s ); + item->SetWindowText( s ); + // Text - if( text.size() > 255 ) - { - text.resize( 255 ); - } item = (CWnd *) this->GetDlgItem( IDC_INFO_TEXT_TEXT ); assert( item ); - item->SetWindowText( text.c_str() ); + for( i = text.begin(); i != text.end(); ++i ) + { + if( *i == '\1' ) + { + textNewLines += "\r\n"; + } + else + { + textNewLines += *i; + } + } + UTF8StringToStringObject( textNewLines.c_str(), s ); + item->SetWindowText( s ); } #if 0 @@ -914,7 +1176,9 @@ LONG ChooserDialog::OnResolve( WPARAM inWParam, LPARAM inLParam ) mServiceInstances.push_back( *p ); UTF8StringToStringObject( p->name.c_str(), s ); mChooserList.InsertItem( n, s ); - mChooserList.SetItemText( n, 1, p->ip.c_str() ); + + UTF8StringToStringObject( p->ip.c_str(), s ); + mChooserList.SetItemText( n, 1, s ); // If this is the only item, select it. @@ -1065,29 +1329,31 @@ static void // 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 ) + if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4 ) { - serviceInstanceAutoPtr.release(); + 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; + serviceInstance->hostName = inEvent->data.resolved->hostName; + + posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_RESOLVE, 0, (LPARAM) serviceInstance ); + assert( posted ); + if( posted ) + { + serviceInstanceAutoPtr.release(); + } } break; - } default: break; @@ -1169,3 +1435,60 @@ exit: } return( err ); } + +//=========================================================================================================================== +// StringObjectToUTF8String +//=========================================================================================================================== + +static DWORD StringObjectToUTF8String( CString &inObject, std::string &outUTF8 ) +{ + DWORD err; + BSTR unicode; + int nUnicode; + int n; + char * utf8; + + unicode = NULL; + utf8 = NULL; + + nUnicode = inObject.GetLength(); + if( nUnicode > 0 ) + { + unicode = inObject.AllocSysString(); + n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, NULL, 0, NULL, NULL ); + assert( n > 0 ); + + utf8 = (char *) malloc( (size_t) n ); + assert( utf8 ); + if( !utf8 ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; } + + n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, utf8, n, NULL, NULL ); + assert( n > 0 ); + + try + { + outUTF8.assign( utf8, n ); + } + catch( ... ) + { + err = ERROR_NO_UNICODE_TRANSLATION; + goto exit; + } + } + else + { + outUTF8.clear(); + } + err = 0; + +exit: + if( unicode ) + { + SysFreeString( unicode ); + } + if( utf8 ) + { + free( utf8 ); + } + return( err ); +} diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.h b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.h index 7acb0b5..a98e5b6 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.h +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,12 @@ Change History (most recent first): $Log: ChooserDialog.h,v $ +Revision 1.3 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.2 2003/10/31 12:18:30 bradley +Added display of the resolved host name. Show separate TXT record entries on separate lines. + Revision 1.1 2003/08/21 02:06:47 bradley Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. @@ -68,6 +76,7 @@ struct ServiceInstanceInfo std::string ip; std::string text; std::string ifIP; + std::string hostName; }; struct ServiceTypeInfo diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp new file mode 100644 index 0000000..b35e76b --- /dev/null +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: LoginDialog.cpp,v $ +Revision 1.2 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.1 2003/12/25 03:47:28 bradley +Login dialog to get the username/password from the user. + +*/ + +#include +#include + +#include "stdafx.h" + +#include "LoginDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP( LoginDialog, CDialog ) +END_MESSAGE_MAP() + +//=========================================================================================================================== +// LoginDialog +//=========================================================================================================================== + +LoginDialog::LoginDialog( CWnd *inParent ) + : CDialog( LoginDialog::IDD, inParent ) +{ + // +} + +//=========================================================================================================================== +// OnInitDialog +//=========================================================================================================================== + +BOOL LoginDialog::OnInitDialog( void ) +{ + CDialog::OnInitDialog(); + return( TRUE ); +} + +//=========================================================================================================================== +// DoDataExchange +//=========================================================================================================================== + +void LoginDialog::DoDataExchange( CDataExchange *inDX ) +{ + CDialog::DoDataExchange( inDX ); +} + +//=========================================================================================================================== +// OnOK +//=========================================================================================================================== + +void LoginDialog::OnOK( void ) +{ + const CWnd * control; + + // Username + + control = GetDlgItem( IDC_LOGIN_USERNAME_TEXT ); + assert( control ); + if( control ) + { + control->GetWindowText( mUsername ); + } + + // Password + + control = GetDlgItem( IDC_LOGIN_PASSWORD_TEXT ); + assert( control ); + if( control ) + { + control->GetWindowText( mPassword ); + } + + CDialog::OnOK(); +} + +//=========================================================================================================================== +// GetLogin +//=========================================================================================================================== + +BOOL LoginDialog::GetLogin( CString &outUsername, CString &outPassword ) +{ + if( DoModal() == IDOK ) + { + outUsername = mUsername; + outPassword = mPassword; + return( TRUE ); + } + return( FALSE ); +} diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.h b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.h new file mode 100644 index 0000000..2b31e6a --- /dev/null +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: LoginDialog.h,v $ +Revision 1.2 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.1 2003/12/25 03:47:28 bradley +Login dialog to get the username/password from the user. + +*/ + +#ifndef __LOGIN_DIALOG__ +#define __LOGIN_DIALOG__ + +#pragma once + +#include "Resource.h" + +//=========================================================================================================================== +// LoginDialog +//=========================================================================================================================== + +class LoginDialog : public CDialog +{ + protected: + + CString mUsername; + CString mPassword; + + public: + + enum { IDD = IDD_LOGIN }; + + LoginDialog( CWnd *inParent = NULL ); + + virtual BOOL GetLogin( CString &outUsername, CString &outPassword ); + + protected: + + virtual BOOL OnInitDialog( void ); + virtual void DoDataExchange( CDataExchange *inDX ); + virtual void OnOK( void ); + + DECLARE_MESSAGE_MAP() +}; + +#endif // __LOGIN_DIALOG__ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.cpp b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.cpp index a26a1e7..f1932eb 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.cpp +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.cpp @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: StdAfx.cpp,v $ +Revision 1.2 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:06:47 bradley Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.h b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.h index 3c16ff7..35fee48 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.h +++ b/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,12 @@ Change History (most recent first): $Log: StdAfx.h,v $ +Revision 1.3 2004/01/30 02:56:32 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.2 2003/10/09 02:31:55 bradley +Define WINVER if not already defined to avoid warning with Visual Studio .NET 2003. + Revision 1.1 2003/08/21 02:06:47 bradley Moved Rendezvous Browser for non-Windows CE into Windows sub-folder. @@ -49,17 +57,18 @@ Rendezvous Browser for Windows #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers -#include // MFC core and standard components -#include // MFC extensions -#include // MFC support for Internet Explorer 4 Common Controls +#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later. + #define WINVER 0x0400 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later. +#endif + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC support for Internet Explorer 4 Common Controls #ifndef _AFX_NO_AFXCMN_SUPPORT -#include // MFC support for Windows Common Controls + #include // MFC support for Windows Common Controls #endif // _AFX_NO_AFXCMN_SUPPORT -#include // MFC socket extensions - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. +#include #include diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcp b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcp index 7f62479..9c73567 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcp +++ b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcp @@ -52,7 +52,7 @@ RSC=rc.exe # 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 +# ADD CPP /nologo /W3 /WX /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "_i386_" /D "_X86_" /D "x86" /D "NDEBUG" /D "_WIN32_WCE_CEPC" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /D DNS_SD_CLIENT_ENABLED=0 /Gs8192 /GF /O2 /c # SUBTRACT CPP /YX /Yc /Yu MTL=midl.exe # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 @@ -86,7 +86,7 @@ RSC=rc.exe # 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 +# ADD CPP /nologo /W3 /WX /Zi /Od /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "_i386_" /D "_X86_" /D "x86" /D "_WIN32_WCE_CEPC" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /D DNS_SD_CLIENT_ENABLED=0 /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 @@ -119,7 +119,7 @@ RSC=rc.exe # 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 +# ADD CPP /nologo /W3 /WX /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "ARM" /D "_ARM_" /D "ARMV4" /D "NDEBUG" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /D DNS_SD_CLIENT_ENABLED=0 /O2 /M$(CECrtMT) /c # SUBTRACT CPP /YX /Yc /Yu MTL=midl.exe # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 @@ -153,7 +153,7 @@ RSC=rc.exe # 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 +# ADD CPP /nologo /W3 /WX /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" /D DNS_SD_CLIENT_ENABLED=0 /FR /M$(CECrtMTDebug) /c # SUBTRACT CPP /YX /Yc /Yu MTL=midl.exe # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 @@ -230,6 +230,7 @@ SOURCE=.\Sources\BrowserDialog.cpp !IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" DEP_CPP_BROWS=\ + "..\..\..\DNSServices\DNSServices.h"\ ".\Sources\Application.h"\ ".\Sources\BrowserDialog.h"\ ".\Sources\StdAfx.h"\ @@ -247,6 +248,7 @@ DEP_CPP_BROWS=\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" DEP_CPP_BROWS=\ + "..\..\..\DNSServices\DNSServices.h"\ ".\Sources\Application.h"\ ".\Sources\BrowserDialog.h"\ ".\Sources\StdAfx.h"\ @@ -255,6 +257,7 @@ DEP_CPP_BROWS=\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" DEP_CPP_BROWS=\ + "..\..\..\DNSServices\DNSServices.h"\ ".\Sources\Application.h"\ ".\Sources\BrowserDialog.h"\ ".\Sources\StdAfx.h"\ @@ -345,47 +348,334 @@ SOURCE=.\Resources\newres.h SOURCE=.\Resources\Resource.h # End Source File # End Group -# Begin Group "Rendezvous" +# Begin Group "Support" # PROP Default_Filter "" # Begin Source File +SOURCE=..\..\..\CommonServices.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\DebugServices.c + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_DEBUG=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + +NODEP_CPP_DEBUG=\ + "..\..\..\intLib.h"\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_DEBUG=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + +NODEP_CPP_DEBUG=\ + "..\..\..\intLib.h"\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_DEBUG=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + +NODEP_CPP_DEBUG=\ + "..\..\..\intLib.h"\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_DEBUG=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + +NODEP_CPP_DEBUG=\ + "..\..\..\intLib.h"\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\..\DebugServices.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\mDNSCore\DNSCommon.c + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_DNSCO=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_DNSCO=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_DNSCO=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_DNSCO=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\mDNSCore\DNSCommon.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\mDNSCore\DNSDigest.c + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_DNSDI=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_DNSDI=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_DNSDI=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_DNSDI=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\..\DNSSD.c + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_DNSSD=\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + "..\..\..\DNSSD.h"\ + "..\..\..\DNSSDDirect.h"\ + "..\..\..\RMxClient.h"\ + +NODEP_CPP_DNSSD=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_DNSSD=\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + "..\..\..\DNSSD.h"\ + "..\..\..\DNSSDDirect.h"\ + "..\..\..\RMxClient.h"\ + +NODEP_CPP_DNSSD=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_DNSSD=\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + "..\..\..\DNSSD.h"\ + "..\..\..\DNSSDDirect.h"\ + "..\..\..\RMxClient.h"\ + +NODEP_CPP_DNSSD=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_DNSSD=\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + "..\..\..\DNSSD.h"\ + "..\..\..\DNSSDDirect.h"\ + "..\..\..\RMxClient.h"\ + +NODEP_CPP_DNSSD=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\..\DNSSD.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\DNSSDDirect.c + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_DNSSDD=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + "..\..\..\DNSSD.h"\ + "..\..\..\DNSSDDirect.h"\ + +NODEP_CPP_DNSSDD=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_DNSSDD=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + "..\..\..\DNSSD.h"\ + "..\..\..\DNSSDDirect.h"\ + +NODEP_CPP_DNSSDD=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_DNSSDD=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + "..\..\..\DNSSD.h"\ + "..\..\..\DNSSDDirect.h"\ + +NODEP_CPP_DNSSDD=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_DNSSDD=\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ + "..\..\..\DNSSD.h"\ + "..\..\..\DNSSDDirect.h"\ + +NODEP_CPP_DNSSDD=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\..\DNSSDDirect.h +# End Source File +# 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"\ + "..\..\..\DNSServices\DNSServices.h"\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" DEP_CPP_DNSSE=\ - "..\..\..\DNSServices\DNSServices.h"\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\DNSServices\DNSServices.h"\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" DEP_CPP_DNSSE=\ - "..\..\..\DNSServices\DNSServices.h"\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\DNSServices\DNSServices.h"\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" DEP_CPP_DNSSE=\ - "..\..\..\DNSServices\DNSServices.h"\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\DNSServices\DNSServices.h"\ !ENDIF @@ -402,33 +692,37 @@ SOURCE=..\..\..\..\mDNSCore\mDNS.c !IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" DEP_CPP_MDNS_=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\..\mDNSCore\uDNS.h"\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" DEP_CPP_MDNS_=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\..\mDNSCore\uDNS.h"\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" DEP_CPP_MDNS_=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\..\mDNSCore\uDNS.h"\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" DEP_CPP_MDNS_=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\..\mDNSCore\uDNS.h"\ !ENDIF @@ -444,10 +738,6 @@ 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" @@ -455,8 +745,16 @@ SOURCE=..\..\..\mDNSWin32.c DEP_CPP_MDNSW=\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ "..\..\..\mDNSWin32.h"\ + {$(INCLUDE)}"ipexport.h"\ + {$(INCLUDE)}"Iphlpapi.h"\ + {$(INCLUDE)}"iptypes.h"\ + +NODEP_CPP_MDNSW=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" @@ -464,8 +762,16 @@ DEP_CPP_MDNSW=\ DEP_CPP_MDNSW=\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ "..\..\..\mDNSWin32.h"\ + {$(INCLUDE)}"ipexport.h"\ + {$(INCLUDE)}"Iphlpapi.h"\ + {$(INCLUDE)}"iptypes.h"\ + +NODEP_CPP_MDNSW=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ # ADD CPP /W3 @@ -474,8 +780,16 @@ DEP_CPP_MDNSW=\ DEP_CPP_MDNSW=\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ "..\..\..\mDNSWin32.h"\ + {$(INCLUDE)}"ipexport.h"\ + {$(INCLUDE)}"Iphlpapi.h"\ + {$(INCLUDE)}"iptypes.h"\ + +NODEP_CPP_MDNSW=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ !ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" @@ -483,8 +797,16 @@ DEP_CPP_MDNSW=\ DEP_CPP_MDNSW=\ "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ "..\..\..\..\mDNSCore\mDNSDebug.h"\ - "..\..\..\..\mDNSCore\mDNSPlatformFunctions.h"\ + "..\..\..\CommonServices.h"\ + "..\..\..\DebugServices.h"\ "..\..\..\mDNSWin32.h"\ + {$(INCLUDE)}"ipexport.h"\ + {$(INCLUDE)}"Iphlpapi.h"\ + {$(INCLUDE)}"iptypes.h"\ + +NODEP_CPP_MDNSW=\ + "..\..\..\logLib.h"\ + "..\..\..\vxWorks.h"\ !ENDIF @@ -494,6 +816,53 @@ DEP_CPP_MDNSW=\ SOURCE=..\..\..\mDNSWin32.h # End Source File +# Begin Source File + +SOURCE=..\..\..\..\mDNSCore\uDNS.c + +!IF "$(CFG)" == "Application - Win32 (WCE emulator) Release" + +DEP_CPP_UDNS_=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\uDNS.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE emulator) Debug" + +DEP_CPP_UDNS_=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\uDNS.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Release" + +DEP_CPP_UDNS_=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\uDNS.h"\ + + +!ELSEIF "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug" + +DEP_CPP_UDNS_=\ + "..\..\..\..\mDNSCore\DNSCommon.h"\ + "..\..\..\..\mDNSCore\mDNSClientAPI.h"\ + "..\..\..\..\mDNSCore\mDNSDebug.h"\ + "..\..\..\..\mDNSCore\uDNS.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\mDNSCore\uDNS.h +# End Source File # End Group # End Target # End Project diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.ico b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.ico index 51a182ea7cb7ad75ea832d1119ab539537ae2ac6..50fb08fcd4c459f28088db2fd9c5134021a1b21b 100644 GIT binary patch literal 1406 zcmZvcziU%b6vw~CiXns~kLODiia$Ep`7Rbjq!ls+x^(V#aq0M|gF}kYRY54;t%C{< zF8vGQ;$SbhwTlHIL!k(^xC9)opL0_!f=SN3?|k3A=lgl*CXwcNce~23Cs(B&SN8~$ z?htcoFiMZ+^7Nl!B=ON`G@BT?)ObAB`0H3_pWkVIeqN16L#uF(OL5quv zT3TAt`ue(t!=YAIR^8E*l9N~#T#98Xl9GXLO zrlr{ukM7Yux<~iuwk@Cs^gt5O13Hr;dPI-t5h0={@gUq2ZMvb!Qf!Xm@qgP91IQy2SY}O!NK5Qa4`k8AYu$kg@4%|uYPm%fGOY|xEUxC@D6zAVTrIrAR>nOD*#Jm zV`Nc8h_LV>sKc2=SfceKv)H%6P5}*u1Ve%$!H{4`FyyZj3=xI|LxLf~KpBGdFeDff z3<(BG6X-A`7!nK#fP{afSYl&hk*9_y_i&_K+tzs9)mv?BY-nq1OFKI|+TGpN!NGy{ z_xE*lbfn|sV@)O#ot~cR{QO+g=~QRGlHTm?@jD*TUHc8#M7^9@T}zXi_Nwd6sw(qk zP6va!+b;eJlfj_hEQ{jLf_gHno6XC-s_f2XzWQ%obh~9)UN0^R7Dc;VRn;oFITWq3 zY}*FJ>t!=vTd9huhjrPt+U;u7wEkc+%bVaK_XqviUdq_Ax?eXR4+q1|4)=?)=yXfg z4EjY?mYr6+)7`W+{o0H{vT1r-I_nTGh%eI5i}-ep|Gz8J_DAXFYw5ua>EV6p)>Hl_ M>EVd~((UKcZ-^d295F95d3@D(?o`tJJp5a18iad(f2?$_+g~b&Hwh;lBe1u9@Yn2AB(9Kdd zaz%hUAVtcq0nm-3QA5~_o`=s94p6r za%ZRjv^*r-}wc+(xDIJwkC`(>t$(Le61p{3N`*jy<&mr#PbQAHXF zIfPt%uDlV+A?M(WXKyOauaCSs@+97e;JN8&&k8XXeD)e~u-B7)y&x;tQx9Q^`QUHQ zBvx#3d#0ZYy9*z*I{u~Q1$Jn*glVP_rVAo%YKT-CLi2wiq diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.rc b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.rc index c453f27..71af68d 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.rc +++ b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.rc @@ -116,7 +116,7 @@ BEGIN 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 "LegalCopyright", "Copyright (C) 2003-2004 Apple Computer, Inc.\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "Application.exe\0" VALUE "PrivateBuild", "\0" diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.cpp b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.cpp index ba28f41..9bcde04 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.cpp +++ b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.cpp @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: Application.cpp,v $ +Revision 1.2 2004/01/30 02:56:33 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:16:10 bradley Rendezvous Browser for HTTP services for Windows CE/PocketPC. @@ -80,12 +85,6 @@ BOOL Application::InitInstance() BOOL dnsInitialized; dnsInitialized = FALSE; - - if( !AfxSocketInit() ) - { - AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); - goto exit; - } err = DNSServicesInitialize( kDNSFlagAdvertise, 0 ); if( err ) diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.h b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.h index 1f4fe13..5e5b507 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.h +++ b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: Application.h,v $ +Revision 1.2 2004/01/30 02:56:33 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:16:10 bradley Rendezvous Browser for HTTP services for Windows CE/PocketPC. diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp index 991fc08..63208aa 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp +++ b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,19 @@ Change History (most recent first): $Log: BrowserDialog.cpp,v $ +Revision 1.5 2004/01/30 02:56:33 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.4 2003/10/16 09:21:56 bradley +Ignore non-IPv4 resolves until mDNS on Windows supports IPv6. + +Revision 1.3 2003/10/14 03:28:50 bradley +Insert services in sorted order to make them easier to find. Defer service adds/removes to the main +thread to avoid potential problems with multi-threaded MFC message map access. Added some asserts. + +Revision 1.2 2003/10/10 03:43:34 bradley +Added support for launching a web browser to go to the browsed web site on a single-tap. + Revision 1.1 2003/08/21 02:16:10 bradley Rendezvous Browser for HTTP services for Windows CE/PocketPC. @@ -42,12 +57,22 @@ Rendezvous Browser for HTTP services for Windows CE/PocketPC. static char THIS_FILE[] = __FILE__; #endif +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define WM_USER_SERVICE_ADD ( WM_USER + 0x100 ) +#define WM_USER_SERVICE_REMOVE ( WM_USER + 0x101 ) + //=========================================================================================================================== // Message Map //=========================================================================================================================== BEGIN_MESSAGE_MAP(BrowserDialog, CDialog) //{{AFX_MSG_MAP(BrowserDialog) + ON_NOTIFY(NM_CLICK, IDC_BROWSE_LIST, OnBrowserListDoubleClick) + ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd ) + ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove ) //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -67,6 +92,7 @@ BrowserDialog::BrowserDialog( CWnd *inParent ) // Note that LoadIcon does not require a subsequent DestroyIcon in Win32. mIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME ); + ASSERT( mIcon ); } //=========================================================================================================================== @@ -110,14 +136,14 @@ BOOL BrowserDialog::OnInitDialog() DNSStatus err; - err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser ); + err = DNSBrowserCreate( 0, OnBrowserCallBack, this, &mBrowser ); if( err ) { AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); goto exit; } - err = DNSBrowserStartServiceSearch( mBrowser, 0, "_http._tcp", NULL ); + err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, "_http._tcp", NULL ); if( err ) { AfxMessageBox( IDP_SOCKETS_INIT_FAILED ); @@ -128,30 +154,124 @@ exit: return( TRUE ); } + //=========================================================================================================================== -// BrowserCallBack [static] +// OnBrowserListDoubleClick +//=========================================================================================================================== + +void BrowserDialog::OnBrowserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult ) +{ + int selectedItem; + + (void) pNMHDR; // Unused + + selectedItem = mBrowserList.GetNextItem( -1, LVNI_SELECTED ); + if( selectedItem >= 0 ) + { + BrowserEntry * entry; + CString temp; + CString url; + + // Build the URL from the IP and optional TXT record. + + entry = &mBrowserEntries[ selectedItem ]; + url += "http://" + entry->ip; + temp = entry->text; + if( temp.Find( TEXT( "path=" ) ) == 0 ) + { + temp.Delete( 0, 5 ); + } + if( temp.Find( '/' ) != 0 ) + { + url += '/'; + } + url += temp; + + // Let the system open the URL in the correct app. + + SHELLEXECUTEINFO info; + + info.cbSize = sizeof( info ); + info.fMask = 0; + info.hwnd = NULL; + info.lpVerb = NULL; + info.lpFile = url; + info.lpParameters = NULL; + info.lpDirectory = NULL; + info.nShow = SW_SHOWNORMAL; + info.hInstApp = NULL; + + ShellExecuteEx( &info ); + } + *pResult = 0; +} + +//=========================================================================================================================== +// OnBrowserCallBack [static] //=========================================================================================================================== void - BrowserDialog::BrowserCallBack( + BrowserDialog::OnBrowserCallBack( void * inContext, DNSBrowserRef inRef, DNSStatus inStatusCode, const DNSBrowserEvent * inEvent ) { BrowserDialog * dialog; + BrowserEntry * entry; + BOOL posted; DNS_UNUSED( inStatusCode ); dialog = reinterpret_cast < BrowserDialog * > ( inContext ); + ASSERT( dialog ); switch( inEvent->type ) { - case kDNSBrowserEventTypeAddService: - dialog->BrowserAddService( inEvent->data.addService.name ); + case kDNSBrowserEventTypeResolved: + if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4 ) + { + char ip[ 64 ]; + + sprintf( ip, "%u.%u.%u.%u:%u", + inEvent->data.resolved->address.u.ipv4.addr.v8[ 0 ], + inEvent->data.resolved->address.u.ipv4.addr.v8[ 1 ], + inEvent->data.resolved->address.u.ipv4.addr.v8[ 2 ], + inEvent->data.resolved->address.u.ipv4.addr.v8[ 3 ], + ( inEvent->data.resolved->address.u.ipv4.port.v8[ 0 ] << 8 ) | + inEvent->data.resolved->address.u.ipv4.port.v8[ 1 ] ); + + entry = new BrowserEntry; + ASSERT( entry ); + if( entry ) + { + UTF8StringToStringObject( inEvent->data.resolved->name, entry->name ); + UTF8StringToStringObject( ip, entry->ip ); + UTF8StringToStringObject( inEvent->data.resolved->textRecord, entry->text ); + + posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_ADD, 0, (LPARAM) entry ); + ASSERT( posted ); + if( !posted ) + { + delete entry; + } + } + } break; - + case kDNSBrowserEventTypeRemoveService: - dialog->BrowserRemoveService( inEvent->data.removeService.name ); + entry = new BrowserEntry; + ASSERT( entry ); + if( entry ) + { + UTF8StringToStringObject( inEvent->data.removeService.name, entry->name ); + + posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_REMOVE, 0, (LPARAM) entry ); + ASSERT( posted ); + if( !posted ) + { + delete entry; + } + } break; default: @@ -163,58 +283,103 @@ void // BrowserAddService //=========================================================================================================================== -void BrowserDialog::BrowserAddService( const char *inName ) +LONG BrowserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam ) { - BrowserEntry newEntry; - INT_PTR n; - INT_PTR i; + BrowserEntry * entry; + INT_PTR lo; + INT_PTR hi; + INT_PTR mid; + int result; - UTF8StringToStringObject( inName, newEntry.name ); - - n = mBrowserEntries.GetSize(); - for( i = 0; i < n; ++i ) + (void) inWParam; // Unused + + entry = reinterpret_cast < BrowserEntry * > ( inLParam ); + ASSERT( entry ); + + result = -1; + mid = 0; + lo = 0; + hi = mBrowserEntries.GetSize() - 1; + while( lo <= hi ) { - BrowserEntry & entry = mBrowserEntries.ElementAt( i ); - - if( entry.name.CompareNoCase( newEntry.name ) == 0 ) + mid = ( lo + hi ) / 2; + result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name ); + if( result == 0 ) { break; } + else if( result < 0 ) + { + hi = mid - 1; + } + else + { + lo = mid + 1; + } } - if( i >= n ) + if( result == 0 ) { - mBrowserEntries.Add( newEntry ); - mBrowserList.InsertItem( i, newEntry.name ); + mBrowserEntries[ mid ].ip = entry->ip; + mBrowserEntries[ mid ].text = entry->text; } + else + { + if( result > 0 ) + { + mid += 1; + } + mBrowserEntries.InsertAt( mid, *entry ); + mBrowserList.InsertItem( mid, entry->name ); + } + delete entry; + return( 0 ); } //=========================================================================================================================== -// BrowserRemoveService +// OnServiceRemove //=========================================================================================================================== -void BrowserDialog::BrowserRemoveService( const char *inName ) +LONG BrowserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam ) { - BrowserEntry newEntry; - INT_PTR n; - INT_PTR i; - - UTF8StringToStringObject( inName, newEntry.name ); + BrowserEntry * entry; + INT_PTR hi; + INT_PTR lo; + INT_PTR mid; + int result; - n = mBrowserEntries.GetSize(); - for( i = 0; i < n; ++i ) + (void) inWParam; // Unused + + entry = reinterpret_cast < BrowserEntry * > ( inLParam ); + ASSERT( entry ); + + result = -1; + mid = 0; + lo = 0; + hi = mBrowserEntries.GetSize() - 1; + while( lo <= hi ) { - BrowserEntry & entry = mBrowserEntries.ElementAt( i ); - - if( entry.name.CompareNoCase( newEntry.name ) == 0 ) + mid = ( lo + hi ) / 2; + result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name ); + if( result == 0 ) { break; } + else if( result < 0 ) + { + hi = mid - 1; + } + else + { + lo = mid + 1; + } } - if( i < n ) + if( result == 0 ) { - mBrowserEntries.RemoveAt( i ); - mBrowserList.DeleteItem( i ); + mBrowserList.DeleteItem( mid ); + mBrowserEntries.RemoveAt( mid ); } + delete entry; + return( 0 ); } #if 0 diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h index 7777510..e2f56c9 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h +++ b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,16 @@ Change History (most recent first): $Log: BrowserDialog.h,v $ +Revision 1.4 2004/01/30 02:56:33 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.3 2003/10/14 03:28:50 bradley +Insert services in sorted order to make them easier to find. Defer service adds/removes to the main +thread to avoid potential problems with multi-threaded MFC message map access. Added some asserts. + +Revision 1.2 2003/10/10 03:43:34 bradley +Added support for launching a web browser to go to the browsed web site on a single-tap. + Revision 1.1 2003/08/21 02:16:10 bradley Rendezvous Browser for HTTP services for Windows CE/PocketPC. @@ -62,23 +74,21 @@ class BrowserDialog : public CDialog //}}AFX_VIRTUAL static void - BrowserCallBack( + OnBrowserCallBack( void * inContext, DNSBrowserRef inRef, DNSStatus inStatusCode, const DNSBrowserEvent * inEvent ); - void BrowserAddService( const char *inName ); - void BrowserRemoveService( const char *inName ); - protected: struct BrowserEntry { CString name; + CString ip; + CString text; }; - HICON mIcon; DNSBrowserRef mBrowser; CArray < BrowserEntry, BrowserEntry > mBrowserEntries; @@ -86,6 +96,9 @@ class BrowserDialog : public CDialog // Generated message map functions //{{AFX_MSG(BrowserDialog) virtual BOOL OnInitDialog(); + afx_msg void OnBrowserListDoubleClick(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg LONG OnServiceAdd( WPARAM inWParam, LPARAM inLParam ); + afx_msg LONG OnServiceRemove( WPARAM inWParam, LPARAM inLParam ); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp index 6622a5d..33ea0a3 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp +++ b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: StdAfx.cpp,v $ +Revision 1.2 2004/01/30 02:56:33 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:16:10 bradley Rendezvous Browser for HTTP services for Windows CE/PocketPC. diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h index 3672152..e3be196 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h +++ b/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: StdAfx.h,v $ +Revision 1.2 2004/01/30 02:56:33 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.1 2003/08/21 02:16:10 bradley Rendezvous Browser for HTTP services for Windows CE/PocketPC. @@ -50,7 +55,8 @@ Rendezvous Browser for HTTP services for Windows CE/PocketPC. #include // MFC support for Windows Common Controls #endif // _AFX_NO_AFXCMN_SUPPORT -#include // MFC socket extensions +#include +//#include // MFC socket extensions //{{AFX_INSERT_LOCATION}} // Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line. diff --git a/mDNSWindows/Applications/DNSServiceTest/Tool.c b/mDNSWindows/Applications/DNSServiceTest/Tool.c index 7e49566..46e8e0c 100644 --- a/mDNSWindows/Applications/DNSServiceTest/Tool.c +++ b/mDNSWindows/Applications/DNSServiceTest/Tool.c @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,21 @@ Change History (most recent first): $Log: Tool.c,v $ +Revision 1.12 2004/04/09 21:03:15 bradley +Changed port numbers to use network byte order for consistency with other platforms. + +Revision 1.11 2004/01/30 03:04:32 bradley +Updated for latest changes to mDNSWindows. + +Revision 1.10 2003/10/31 12:18:31 bradley +Added display of the resolved host name. Show separate TXT record entries on separate lines. + +Revision 1.9 2003/10/22 02:00:20 bradley +Fixed proxy IP setup to be in network byte order so it works on Mac and Windows. + +Revision 1.8 2003/10/04 04:47:08 bradley +Changed DNSServiceRegistrationCreate to treat the port in network byte order for end-to-end consistency. + Revision 1.7 2003/08/20 07:06:34 bradley Update to APSL 2.0. Updated change history to match other mDNSResponder files. @@ -289,7 +306,7 @@ static int ProcessArgs( int argc, char* argv[] ) const char * name; const char * type; const char * domain; - int port; + uint16_t port; const char * text; size_t textSize; DNSBrowserRef browser; @@ -433,10 +450,10 @@ static int ProcessArgs( int argc, char* argv[] ) name = argv[ i++ ]; type = argv[ i++ ]; domain = argv[ i++ ]; - port = atoi( argv[ i++ ] ); + port = (uint16_t) atoi( argv[ i++ ] ); text = argv[ i ]; textSize = strlen( text ); - if( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) { domain = "local."; } @@ -450,7 +467,7 @@ static int ProcessArgs( int argc, char* argv[] ) { DNSHostRegistrationFlags hostFlags; - // 'r'egister 'p'roxy 's'ervice + // 'r'egister 'p'roxy 's'ervice require_action_string( argc > ( i + 7 ), exit, err = kDNSBadParamErr, "missing arguments" ); ++i; @@ -459,7 +476,7 @@ static int ProcessArgs( int argc, char* argv[] ) name = argv[ i++ ]; type = argv[ i++ ]; domain = argv[ i++ ]; - port = atoi( argv[ i++ ] ); + port = (uint16_t) atoi( argv[ i++ ] ); text = argv[ i ]; textSize = strlen( text ); if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) @@ -468,8 +485,11 @@ static int ProcessArgs( int argc, char* argv[] ) } 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 ) ); + addr.addressType = kDNSNetworkAddressTypeIPv4; + addr.u.ipv4.addr.v8[ 0 ] = (DNSUInt8) b[ 0 ]; + addr.u.ipv4.addr.v8[ 1 ] = (DNSUInt8) b[ 1 ]; + addr.u.ipv4.addr.v8[ 2 ] = (DNSUInt8) b[ 2 ]; + addr.u.ipv4.addr.v8[ 3 ] = (DNSUInt8) b[ 3 ]; fprintf( stdout, "registering proxy service \"%s.%s.%s\" port %d text \"%s\"\n", name, type, domain, port, text ); @@ -570,16 +590,16 @@ static int ProcessArgs( int argc, char* argv[] ) name = argv[ i++ ]; type = argv[ i++ ]; domain = argv[ i++ ]; - port = atoi( argv[ i++ ] ); + port = (uint16_t) atoi( argv[ i++ ] ); text = argv[ i ]; textSize = strlen( text ); - if( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) + if( ( domain[ 0 ] == '\0' ) || ( ( 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, + emulatedRef = DNSServiceRegistrationCreate( name, type, domain, htons( port ), text, EmulatedRegistrationCallBack, NULL ); require_action_string( emulatedRef, exit, err = kDNSUnknownErr, "create emulated registration failed" ); } @@ -758,10 +778,11 @@ static void BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inS const uint8_t * end; int i; - fprintf( stdout, "resolved \"%s.%s%s\" to %s:%u on interface 0x%08X (%s)%s\n", + fprintf( stdout, "resolved \"%s.%s%s\" to \"%s\" (%s:%u) on interface 0x%08X (%s)%s\n", inEvent->data.resolved->name, inEvent->data.resolved->type, inEvent->data.resolved->domain, + inEvent->data.resolved->hostName, 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 ], @@ -820,10 +841,11 @@ static void ResolverCallBack( void *inContext, DNSResolverRef inRef, DNSStatus i const uint8_t * end; int i; - fprintf( stdout, "resolved \"%s.%s%s\" to %s:%u on interface 0x%08X (%s)%s\n", + fprintf( stdout, "resolved \"%s.%s%s\" to \"%s\" (%s:%u) on interface 0x%08X (%s)%s\n", inEvent->data.resolved.name, inEvent->data.resolved.type, inEvent->data.resolved.domain, + inEvent->data.resolved.hostName, 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 ], diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindows.h b/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindows.h index eaa6a70..901df49 100644 --- a/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindows.h +++ b/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindows.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: ToolPrefixWindows.h,v $ +Revision 1.3 2004/01/30 03:04:32 bradley +Updated for latest changes to mDNSWindows. + Revision 1.2 2003/08/20 07:06:34 bradley Update to APSL 2.0. Updated change history to match other mDNSResponder files. diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindowsDebug.h b/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindowsDebug.h index 1f76fc2..5858503 100644 --- a/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindowsDebug.h +++ b/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindowsDebug.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,9 @@ Change History (most recent first): $Log: ToolPrefixWindowsDebug.h,v $ +Revision 1.3 2004/01/30 03:04:32 bradley +Updated for latest changes to mDNSWindows. + Revision 1.2 2003/08/20 07:06:34 bradley Update to APSL 2.0. Updated change history to match other mDNSResponder files. diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolWin32.mcp b/mDNSWindows/Applications/DNSServiceTest/ToolWin32.mcp index 064aba024384e04fd43cd810abd4688ce02028e7..21a4ed63564df5c789d5ab609f17095235e893fe 100644 GIT binary patch delta 3140 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 - - - - - - - - - - - - + + + + + + @@ -166,15 +149,18 @@ - - + + + + diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.sln b/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.sln index 53a2ba2..e5e0122 100644 --- a/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.sln +++ b/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.sln @@ -5,13 +5,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tool", "ToolWin32VS2003.vcp EndProject Global GlobalSection(SolutionConfiguration) = preSolution - all = all Debug = Debug Release = Release EndGlobalSection GlobalSection(ProjectConfiguration) = postSolution - {F66EFE7E-50A6-44D4-87C7-742B303BA852}.all.ActiveCfg = all|Win32 - {F66EFE7E-50A6-44D4-87C7-742B303BA852}.all.Build.0 = all|Win32 {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 diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.vcproj b/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.vcproj index 2349cb9..c0ed0ee 100644 --- a/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.vcproj +++ b/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.vcproj @@ -126,38 +126,6 @@ - - - - - - - - - - - - - - - @@ -165,6 +133,15 @@ + + + + + + @@ -186,15 +163,18 @@ - - + + + + diff --git a/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.cpp b/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.cpp new file mode 100644 index 0000000..44c9fb2 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ClassFactory.cpp,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" + +#include "DebugServices.h" + +#include "ExplorerBar.h" +#include "ExplorerPlugin.h" + +#include "ClassFactory.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// ClassFactory +//=========================================================================================================================== + +ClassFactory::ClassFactory( CLSID inCLSID ) +{ + mCLSIDObject = inCLSID; + mRefCount = 1; + ++gDLLRefCount; +} + +//=========================================================================================================================== +// ~ClassFactory +//=========================================================================================================================== + +ClassFactory::~ClassFactory( void ) +{ + check( gDLLRefCount > 0 ); + + --gDLLRefCount; +} + +#if 0 +#pragma mark - +#pragma mark == IUnknown methods == +#endif + +//=========================================================================================================================== +// QueryInterface +//=========================================================================================================================== + +STDMETHODIMP ClassFactory::QueryInterface( REFIID inID, LPVOID *outResult ) +{ + HRESULT err; + + check( outResult ); + + if( IsEqualIID( inID, IID_IUnknown ) ) + { + *outResult = this; + } + else if( IsEqualIID( inID, IID_IClassFactory ) ) + { + *outResult = (IClassFactory *) this; + } + else + { + *outResult = NULL; + err = E_NOINTERFACE; + goto exit; + } + + ( *( (LPUNKNOWN *) outResult ) )->AddRef(); + err = S_OK; + +exit: + return( err ); +} + +//=========================================================================================================================== +// AddRef +//=========================================================================================================================== + +STDMETHODIMP_( DWORD ) ClassFactory::AddRef( void ) +{ + return( ++mRefCount ); +} + +//=========================================================================================================================== +// Release +//=========================================================================================================================== + +STDMETHODIMP_( DWORD ) ClassFactory::Release( void ) +{ + DWORD count; + + count = --mRefCount; + if( count == 0 ) + { + delete this; + } + return( count ); +} + +#if 0 +#pragma mark - +#pragma mark == IClassFactory methods == +#endif + +//=========================================================================================================================== +// CreateInstance +//=========================================================================================================================== + +STDMETHODIMP ClassFactory::CreateInstance( LPUNKNOWN inUnknown, REFIID inID, LPVOID *outObject ) +{ + HRESULT err; + LPVOID obj; + + check( outObject ); + + obj = NULL; + *outObject = NULL; + require_action( !inUnknown, exit, err = CLASS_E_NOAGGREGATION ); + + // Create the object based on the CLSID. + + if( IsEqualCLSID( mCLSIDObject, CLSID_ExplorerBar ) ) + { + try + { + obj = new ExplorerBar(); + } + catch( ... ) + { + // Don't let exception escape. + } + require_action( obj, exit, err = E_OUTOFMEMORY ); + } + else + { + err = E_FAIL; + goto exit; + } + + // Query for the specified interface. Release the factory since QueryInterface retains it. + + err = ( (LPUNKNOWN ) obj )->QueryInterface( inID, outObject ); + ( (LPUNKNOWN ) obj )->Release(); + +exit: + return( err ); +} + +//=========================================================================================================================== +// LockServer +//=========================================================================================================================== + +STDMETHODIMP ClassFactory::LockServer( BOOL inLock ) +{ + DEBUG_UNUSED( inLock ); + + return( E_NOTIMPL ); +} diff --git a/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.h b/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.h new file mode 100644 index 0000000..2361129 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ClassFactory.h,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ + +#ifndef __CLASS_FACTORY__ +#define __CLASS_FACTORY__ + +#include "StdAfx.h" + +//=========================================================================================================================== +// ClassFactory +//=========================================================================================================================== + +class ClassFactory : public IClassFactory +{ + protected: + + DWORD mRefCount; + CLSID mCLSIDObject; + + public: + + ClassFactory( CLSID inCLSID ); + ~ClassFactory( void ); + + // IUnknown methods + + STDMETHODIMP QueryInterface( REFIID inID, LPVOID *outResult ); + STDMETHODIMP_( DWORD ) AddRef( void ); + STDMETHODIMP_( DWORD ) Release( void ); + + // IClassFactory methods + + STDMETHODIMP CreateInstance( LPUNKNOWN inUnknown, REFIID inID, LPVOID *outObject ); + STDMETHODIMP LockServer( BOOL inLock ); +}; + +#endif // __CLASS_FACTORY__ diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.cpp b/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.cpp new file mode 100644 index 0000000..4052d77 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.cpp @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerBar.cpp,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" + +#include "comutil.h" +#include "ShObjIdl.h" + +#include "DebugServices.h" + +#include "Resource.h" + +#include "ExplorerBar.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define MIN_SIZE_X 10 +#define MIN_SIZE_Y 10 + +//=========================================================================================================================== +// ExplorerBar +//=========================================================================================================================== + +ExplorerBar::ExplorerBar( void ) +{ + ++gDLLRefCount; + + mRefCount = 1; + mSite = NULL; + mWebBrowser = NULL; + mParentWindow = NULL; + mFocus = FALSE; + mViewMode = 0; + mBandID = 0; +} + +//=========================================================================================================================== +// ~ExplorerBar +//=========================================================================================================================== + +ExplorerBar::~ExplorerBar( void ) +{ + if( mWebBrowser ) + { + mWebBrowser->Release(); + mWebBrowser = NULL; + } + if( mSite ) + { + mSite->Release(); + mSite = NULL; + } + + --gDLLRefCount; +} + +#if 0 +#pragma mark - +#pragma mark == IUnknown implementation == +#endif + +//=========================================================================================================================== +// QueryInterface +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::QueryInterface( REFIID inID, LPVOID *outResult ) +{ + HRESULT err; + + if( IsEqualIID( inID, IID_IUnknown ) ) // IUnknown + { + *outResult = this; + } + else if( IsEqualIID( inID, IID_IOleWindow ) ) // IOleWindow + { + *outResult = (IOleWindow *) this; + } + else if( IsEqualIID( inID, IID_IDockingWindow ) ) // IDockingWindow + { + *outResult = (IDockingWindow *) this; + } + else if( IsEqualIID( inID, IID_IDeskBand ) ) // IDeskBand + { + *outResult = (IDeskBand *) this; + } + else if( IsEqualIID( inID, IID_IInputObject ) ) // IInputObject + { + *outResult = (IInputObject *) this; + } + else if( IsEqualIID( inID, IID_IObjectWithSite ) ) // IObjectWithSite + { + *outResult = (IObjectWithSite *) this; + } + else if( IsEqualIID( inID, IID_IPersistStream ) ) // IPersistStream + { + *outResult = (IPersistStream *) this; + } + else + { + *outResult = NULL; + err = E_NOINTERFACE; + goto exit; + } + + ( *( (LPUNKNOWN *) outResult ) )->AddRef(); + err = S_OK; + +exit: + return( err ); +} + +//=========================================================================================================================== +// AddRef +//=========================================================================================================================== + +STDMETHODIMP_( DWORD ) ExplorerBar::AddRef( void ) +{ + return( ++mRefCount ); +} + +//=========================================================================================================================== +// Release +//=========================================================================================================================== + +STDMETHODIMP_( DWORD ) ExplorerBar::Release( void ) +{ + DWORD count; + + count = --mRefCount; + if( count == 0 ) + { + delete this; + } + return( count ); +} + +#if 0 +#pragma mark - +#pragma mark == IOleWindow implementation == +#endif + +//=========================================================================================================================== +// GetWindow +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetWindow( HWND *outWindow ) +{ + *outWindow = mWindow.GetSafeHwnd(); + return( S_OK ); +} + +//=========================================================================================================================== +// ContextSensitiveHelp +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::ContextSensitiveHelp( BOOL inEnterMode ) +{ + DEBUG_UNUSED( inEnterMode ); + + return( E_NOTIMPL ); +} + +#if 0 +#pragma mark - +#pragma mark == IDockingWindow implementation == +#endif + +//=========================================================================================================================== +// ShowDW +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::ShowDW( BOOL inShow ) +{ + if( mWindow.GetSafeHwnd() ) + { + mWindow.ShowWindow( inShow ? SW_SHOW : SW_HIDE ); + } + return( S_OK ); +} + +//=========================================================================================================================== +// CloseDW +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::CloseDW( DWORD inReserved ) +{ + DEBUG_UNUSED( inReserved ); + + ShowDW( FALSE ); + if( mWindow.GetSafeHwnd() ) + { + mWindow.SendMessage( WM_CLOSE ); + } + return( S_OK ); +} + +//=========================================================================================================================== +// ResizeBorderDW +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::ResizeBorderDW( LPCRECT inBorder, IUnknown *inPunkSite, BOOL inReserved ) +{ + DEBUG_UNUSED( inBorder ); + DEBUG_UNUSED( inPunkSite ); + DEBUG_UNUSED( inReserved ); + + return( E_NOTIMPL ); +} + +#if 0 +#pragma mark - +#pragma mark == IDeskBand implementation == +#endif + +//=========================================================================================================================== +// GetBandInfo +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetBandInfo( DWORD inBandID, DWORD inViewMode, DESKBANDINFO *outInfo ) +{ + HRESULT err; + + require_action( outInfo, exit, err = E_INVALIDARG ); + + mBandID = inBandID; + mViewMode = inViewMode; + + if( outInfo->dwMask & DBIM_MINSIZE ) + { + outInfo->ptMinSize.x = 100; + outInfo->ptMinSize.y = 100; + } + if( outInfo->dwMask & DBIM_MAXSIZE ) + { + // Unlimited max size. + + outInfo->ptMaxSize.x = -1; + outInfo->ptMaxSize.y = -1; + } + if( outInfo->dwMask & DBIM_INTEGRAL ) + { + outInfo->ptIntegral.x = 1; + outInfo->ptIntegral.y = 1; + } + if( outInfo->dwMask & DBIM_ACTUAL ) + { + outInfo->ptActual.x = 0; + outInfo->ptActual.y = 0; + } + if( outInfo->dwMask & DBIM_TITLE ) + { + CString s; + BOOL ok; + + ok = s.LoadString( IDS_NAME ); + require_action( ok, exit, err = kNoResourcesErr ); + + #ifdef UNICODE + lstrcpyn( outInfo->wszTitle, s, sizeof_array( outInfo->wszTitle ) ); + #else + DWORD nChars; + + nChars = MultiByteToWideChar( CP_ACP, 0, s, -1, outInfo->wszTitle, sizeof_array( outInfo->wszTitle ) ); + err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + #endif + } + if( outInfo->dwMask & DBIM_MODEFLAGS ) + { + outInfo->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT; + } + + // Force the default background color. + + outInfo->dwMask &= ~DBIM_BKCOLOR; + err = S_OK; + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == IInputObject implementation == +#endif + +//=========================================================================================================================== +// UIActivateIO +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::UIActivateIO( BOOL inActivate, LPMSG inMsg ) +{ + DEBUG_UNUSED( inMsg ); + + if( inActivate ) + { + mWindow.SetFocus(); + } + return( S_OK ); +} + +//=========================================================================================================================== +// HasFocusIO +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::HasFocusIO( void ) +{ + if( mWindow.GetFocus()->GetSafeHwnd() == mWindow.GetSafeHwnd() ) + { + return( S_OK ); + } + return( S_FALSE ); +} + +//=========================================================================================================================== +// TranslateAcceleratorIO +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::TranslateAcceleratorIO( LPMSG inMsg ) +{ + DEBUG_UNUSED( inMsg ); + + return( S_FALSE ); +} + +#if 0 +#pragma mark - +#pragma mark == IObjectWithSite implementation == +#endif + +//=========================================================================================================================== +// SetSite +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::SetSite( IUnknown *inPunkSite ) +{ + AFX_MANAGE_STATE( AfxGetStaticModuleState() ); + + HRESULT err; + + // Release the old interfaces. + + if( mWebBrowser ) + { + mWebBrowser->Release(); + mWebBrowser = NULL; + } + if( mSite ) + { + mSite->Release(); + mSite = NULL; + } + + // A non-NULL site means we're setting the site. Otherwise, the site is being released (done above). + + if( !inPunkSite ) + { + err = S_OK; + goto exit; + } + + // Get the parent window. + + IOleWindow * oleWindow; + + mParentWindow = NULL; + err = inPunkSite->QueryInterface( IID_IOleWindow, (LPVOID *) &oleWindow ); + require( SUCCEEDED( err ), exit ); + + err = oleWindow->GetWindow( &mParentWindow ); + oleWindow->Release(); + require_noerr( err, exit ); + require_action( mParentWindow, exit, err = E_FAIL ); + + // Get the IInputObject interface. + + err = inPunkSite->QueryInterface( IID_IInputObjectSite, (LPVOID *) &mSite ); + require( SUCCEEDED( err ), exit ); + check( mSite ); + + // Get the IWebBrowser2 interface. + + IOleCommandTarget * oleCommandTarget; + + err = inPunkSite->QueryInterface( IID_IOleCommandTarget, (LPVOID *) &oleCommandTarget ); + require( SUCCEEDED( err ), exit ); + + IServiceProvider * serviceProvider; + + err = oleCommandTarget->QueryInterface( IID_IServiceProvider, (LPVOID *) &serviceProvider ); + oleCommandTarget->Release(); + require( SUCCEEDED( err ), exit ); + + err = serviceProvider->QueryService( SID_SWebBrowserApp, IID_IWebBrowser2, (LPVOID *) &mWebBrowser ); + serviceProvider->Release(); + require( SUCCEEDED( err ), exit ); + + // Create the main window. + + err = SetupWindow(); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// GetSite +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetSite( REFIID inID, LPVOID *outResult ) +{ + HRESULT err; + + *outResult = NULL; + require_action( mSite, exit, err = E_FAIL ); + + err = mSite->QueryInterface( inID, outResult ); + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == IPersistStream implementation == +#endif + +// +// IPersistStream implementation +// +// This is only supported to allow the desk band to be dropped on the desktop and to prevent multiple instances of +// the desk band from showing up in the context menu. This desk band doesn't actually persist any data. +// + +//=========================================================================================================================== +// GetClassID +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetClassID( LPCLSID outClassID ) +{ + *outClassID = CLSID_ExplorerBar; + return( S_OK ); +} + +//=========================================================================================================================== +// IsDirty +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::IsDirty( void ) +{ + return( S_FALSE ); +} + +//=========================================================================================================================== +// Load +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::Load( LPSTREAM inStream ) +{ + DEBUG_UNUSED( inStream ); + + return( S_OK ); +} + +//=========================================================================================================================== +// Save +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::Save( LPSTREAM inStream, BOOL inClearDirty ) +{ + DEBUG_UNUSED( inStream ); + DEBUG_UNUSED( inClearDirty ); + + return( S_OK ); +} + +//=========================================================================================================================== +// GetSizeMax +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::GetSizeMax( ULARGE_INTEGER *outSizeMax ) +{ + DEBUG_UNUSED( outSizeMax ); + + return( E_NOTIMPL ); +} + +#if 0 +#pragma mark - +#pragma mark == Other == +#endif + +//=========================================================================================================================== +// SetupWindow +//=========================================================================================================================== + +OSStatus ExplorerBar::SetupWindow( void ) +{ + OSStatus err; + CWnd * window; + CRect rect; + CString s; + BOOL ok; + + window = CWnd::FromHandle( mParentWindow ); + check( window ); + window->GetClientRect( rect ); + + ok = s.LoadString( IDS_NAME ); + require_action( ok, exit, err = kNoResourcesErr ); + + ok = mWindow.Create( NULL, s, WS_CHILD | WS_VISIBLE, rect, window, 100 ) != 0; + require_action( ok, exit, err = kNoResourcesErr ); + + mWindow.SetOwner( this ); + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// GoToURL +//=========================================================================================================================== + +OSStatus ExplorerBar::GoToURL( const CString &inURL ) +{ + OSStatus err; + BSTR s; + VARIANT empty; + + s = inURL.AllocSysString(); + require_action( s, exit, err = kNoMemoryErr ); + + VariantInit( &empty ); + err = mWebBrowser->Navigate( s, &empty, &empty, &empty, &empty ); + SysFreeString( s ); + require_noerr( err, exit ); + +exit: + return( err ); +} diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.h b/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.h new file mode 100644 index 0000000..1ab9df9 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerBar.h,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ + +#ifndef __EXPLORER_BAR__ +#define __EXPLORER_BAR__ + +#include "StdAfx.h" + +#include "ExplorerBarWindow.h" +#include "ExplorerPlugin.h" + +//=========================================================================================================================== +// ExplorerBar +//=========================================================================================================================== + +class ExplorerBar : public IDeskBand, + public IInputObject, + public IObjectWithSite, + public IPersistStream +{ + protected: + + DWORD mRefCount; + IInputObjectSite * mSite; + IWebBrowser2 * mWebBrowser; + HWND mParentWindow; + BOOL mFocus; + DWORD mViewMode; + DWORD mBandID; + ExplorerBarWindow mWindow; + + public: + + ExplorerBar( void ); + ~ExplorerBar( void ); + + // IUnknown methods + + STDMETHODIMP QueryInterface( REFIID inID, LPVOID *outResult ); + STDMETHODIMP_( DWORD ) AddRef( void ); + STDMETHODIMP_( DWORD ) Release( void ); + + // IOleWindow methods + + STDMETHOD( GetWindow )( HWND *outWindow ); + STDMETHOD( ContextSensitiveHelp )( BOOL inEnterMode ); + + // IDockingWindow methods + + STDMETHOD( ShowDW )( BOOL inShow ); + STDMETHOD( CloseDW )( DWORD inReserved ); + STDMETHOD( ResizeBorderDW )( LPCRECT inBorder, IUnknown *inPunkSite, BOOL inReserved ); + + // IDeskBand methods + + STDMETHOD( GetBandInfo )( DWORD inBandID, DWORD inViewMode, DESKBANDINFO *outInfo ); + + // IInputObject methods + + STDMETHOD( UIActivateIO )( BOOL inActivate, LPMSG inMsg ); + STDMETHOD( HasFocusIO )( void ); + STDMETHOD( TranslateAcceleratorIO )( LPMSG inMsg ); + + // IObjectWithSite methods + + STDMETHOD( SetSite )( IUnknown *inPunkSite ); + STDMETHOD( GetSite )( REFIID inID, LPVOID *outResult ); + + // IPersistStream methods + + STDMETHOD( GetClassID )( LPCLSID outClassID ); + STDMETHOD( IsDirty )( void ); + STDMETHOD( Load )( LPSTREAM inStream ); + STDMETHOD( Save )( LPSTREAM inStream, BOOL inClearDirty ); + STDMETHOD( GetSizeMax )( ULARGE_INTEGER *outSizeMax ); + + // Other + + OSStatus SetupWindow( void ); + OSStatus GoToURL( const CString &inURL ); +}; + +#endif // __EXPLORER_BAR__ diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.cpp b/mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.cpp new file mode 100644 index 0000000..7ca9f2a --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.cpp @@ -0,0 +1,751 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerBarWindow.cpp,v $ +Revision 1.5 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.4 2004/04/09 21:03:15 bradley +Changed port numbers to use network byte order for consistency with other platforms. + +Revision 1.3 2004/04/08 09:43:43 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.2 2004/02/21 04:36:19 bradley +Enable dot local name lookups now that the NSP is being installed. + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" + +#include "CommonServices.h" +#include "DebugServices.h" +#include "DNSSD.h" + +#include "ExplorerBar.h" +#include "LoginDialog.h" +#include "Resource.h" + +#include "ExplorerBarWindow.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +// Control IDs + +#define IDC_EXPLORER_TREE 1234 + +// Private Messages + +#define WM_PRIVATE_SERVICE_ADD ( WM_USER + 0x100 ) +#define WM_PRIVATE_SERVICE_REMOVE ( WM_USER + 0x101 ) +#define WM_PRIVATE_RESOLVE ( WM_USER + 0x102 ) + +// TXT records + +#define kTXTRecordKeyPath "path=" +#define kTXTRecordKeyPathSize sizeof_string( kTXTRecordKeyPath ) + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex ); +DEBUG_LOCAL OSStatus UTF8StringToStringObject( const char *inUTF8, CString &inObject ); + +#if 0 +#pragma mark == Message Map == +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP( ExplorerBarWindow, CWnd ) + ON_WM_CREATE() + ON_WM_DESTROY() + ON_WM_SIZE() + ON_NOTIFY( NM_DBLCLK, IDC_EXPLORER_TREE, OnDoubleClick ) + ON_MESSAGE( WM_PRIVATE_SERVICE_ADD, OnServiceAdd ) + ON_MESSAGE( WM_PRIVATE_SERVICE_REMOVE, OnServiceRemove ) + ON_MESSAGE( WM_PRIVATE_RESOLVE, OnResolve ) +END_MESSAGE_MAP() + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// ExplorerBarWindow +//=========================================================================================================================== + +ExplorerBarWindow::ExplorerBarWindow( void ) +{ + mOwner = NULL; + mResolveServiceRef = NULL; +} + +//=========================================================================================================================== +// ~ExplorerBarWindow +//=========================================================================================================================== + +ExplorerBarWindow::~ExplorerBarWindow( void ) +{ + // +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// OnCreate +//=========================================================================================================================== + +int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) +{ + AFX_MANAGE_STATE( AfxGetStaticModuleState() ); + + OSStatus err; + CRect rect; + CString s; + + err = CWnd::OnCreate( inCreateStruct ); + require_noerr( err, exit ); + + GetClientRect( rect ); + mTree.Create( WS_TABSTOP | WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES, rect, this, + IDC_EXPLORER_TREE ); + + err = DNSServiceInitialize( kDNSServiceInitializeFlagsNone, 0 ); + if( err != kNoErr ) + { + // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it). + + s.LoadString( IDS_RENDEZVOUS_NOT_AVAILABLE ); + mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST ); + err = kNoErr; + goto exit; + } + + ServiceHandlerEntry * e; + + // Web Site Handler + + e = new ServiceHandlerEntry; + check( e ); + e->type = "_http._tcp"; + e->urlScheme = "http://"; + e->ref = NULL; + e->treeItem = NULL; + e->treeFirst = true; + e->obj = this; + e->needsLogin = false; + mServiceHandlers.Add( e ); + + s.LoadString( IDS_WEB_SITES ); + e->treeItem = mTree.InsertItem( s, 0, 0 ); + mTree.Expand( e->treeItem, TVE_EXPAND ); + + err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); + require_noerr( err, exit ); + + // FTP Site Handler + + e = new ServiceHandlerEntry; + check( e ); + e->type = "_ftp._tcp"; + e->urlScheme = "ftp://"; + e->ref = NULL; + e->treeItem = NULL; + e->treeFirst = true; + e->obj = this; + e->needsLogin = true; + mServiceHandlers.Add( e ); + + s.LoadString( IDS_FTP_SITES ); + e->treeItem = mTree.InsertItem( s, 0, 0 ); + mTree.Expand( e->treeItem, TVE_EXPAND ); + + err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// OnDestroy +//=========================================================================================================================== + +void ExplorerBarWindow::OnDestroy( void ) +{ + // Stop any resolves that may still be pending (shouldn't be any). + + StopResolve(); + + // Clean up the service handlers. + + int i; + int n; + + n = (int) mServiceHandlers.GetSize(); + for( i = 0; i < n; ++i ) + { + delete mServiceHandlers[ i ]; + } + + DNSServiceFinalize(); + CWnd::OnDestroy(); +} + +//=========================================================================================================================== +// OnSize +//=========================================================================================================================== + +void ExplorerBarWindow::OnSize( UINT inType, int inX, int inY ) +{ + CWnd::OnSize( inType, inX, inY ); + mTree.MoveWindow( 0, 0, inX, inY ); +} + +//=========================================================================================================================== +// OnDoubleClick +//=========================================================================================================================== + +void ExplorerBarWindow::OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult ) +{ + HTREEITEM item; + ServiceInfo * service; + OSStatus err; + + DEBUG_UNUSED( inNMHDR ); + + item = mTree.GetSelectedItem(); + require( item, exit ); + + service = reinterpret_cast < ServiceInfo * > ( mTree.GetItemData( item ) ); + require_quiet( service, exit ); + + err = StartResolve( service ); + require_noerr( err, exit ); + +exit: + *outResult = 0; +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// BrowseCallBack +//=========================================================================================================================== + +void CALLBACK_COMPAT + ExplorerBarWindow::BrowseCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + ServiceHandlerEntry * obj; + ServiceInfo * service; + OSStatus err; + + DEBUG_UNUSED( inRef ); + + service = NULL; + + require_noerr( inErrorCode, exit ); + obj = reinterpret_cast < ServiceHandlerEntry * > ( inContext ); + check( obj ); + check( obj->obj ); + + try + { + // Post a message to the main thread so it can handle it since MFC is not thread safe. + + service = new ServiceInfo; + require_action( service, exit, err = kNoMemoryErr ); + + err = UTF8StringToStringObject( inName, service->displayName ); + check_noerr( err ); + + service->name = strdup( inName ); + require_action( service->name, exit, err = kNoMemoryErr ); + + service->type = strdup( inType ); + require_action( service->type, exit, err = kNoMemoryErr ); + + service->domain = strdup( inDomain ); + require_action( service->domain, exit, err = kNoMemoryErr ); + + service->ifi = inInterfaceIndex; + service->handler = obj; + + DWORD message; + BOOL ok; + + message = ( inFlags & kDNSServiceFlagsAdd ) ? WM_PRIVATE_SERVICE_ADD : WM_PRIVATE_SERVICE_REMOVE; + ok = ::PostMessage( obj->obj->GetSafeHwnd(), message, 0, (LPARAM) service ); + check( ok ); + if( ok ) + { + service = NULL; + } + } + catch( ... ) + { + dlog( kDebugLevelError, "BrowseCallBack: exception thrown\n" ); + } + +exit: + if( service ) + { + delete service; + } +} + +//=========================================================================================================================== +// OnServiceAdd +//=========================================================================================================================== + +LONG ExplorerBarWindow::OnServiceAdd( WPARAM inWParam, LPARAM inLParam ) +{ + ServiceInfo * service; + ServiceHandlerEntry * handler; + int cmp; + int index; + + DEBUG_UNUSED( inWParam ); + + service = reinterpret_cast < ServiceInfo * > ( inLParam ); + check( service ); + handler = service->handler; + check( handler ); + + cmp = FindServiceArrayIndex( handler->array, *service, index ); + if( cmp == 0 ) + { + // Found a match so update the item. The index is index + 1 so subtract 1. + + index -= 1; + check( index < handler->array.GetSize() ); + + service->item = handler->array[ index ]->item; + delete handler->array[ index ]; + handler->array[ index ] = service; + mTree.SetItemText( service->item, service->displayName ); + mTree.SetItemData( service->item, (DWORD_PTR) service ); + } + else + { + HTREEITEM afterItem; + + // Insert the new item in sorted order. + + afterItem = ( index > 0 ) ? handler->array[ index - 1 ]->item : TVI_FIRST; + handler->array.InsertAt( index, service ); + service->item = mTree.InsertItem( service->displayName, handler->treeItem, afterItem ); + mTree.SetItemData( service->item, (DWORD_PTR) service ); + + // Make sure the item is visible if this is the first time a service was added. + + if( handler->treeFirst ) + { + handler->treeFirst = false; + mTree.EnsureVisible( service->item ); + } + } + return( 0 ); +} + +//=========================================================================================================================== +// OnServiceRemove +//=========================================================================================================================== + +LONG ExplorerBarWindow::OnServiceRemove( WPARAM inWParam, LPARAM inLParam ) +{ + ServiceInfo * service; + ServiceHandlerEntry * handler; + int cmp; + int index; + + DEBUG_UNUSED( inWParam ); + + service = reinterpret_cast < ServiceInfo * > ( inLParam ); + check( service ); + handler = service->handler; + check( handler ); + + // Search to see if we know about this service instance. If so, remove it from the list. + + cmp = FindServiceArrayIndex( handler->array, *service, index ); + check( cmp == 0 ); + if( cmp == 0 ) + { + // Found a match remove the item. The index is index + 1 so subtract 1. + + index -= 1; + check( index < handler->array.GetSize() ); + + mTree.DeleteItem( handler->array[ index ]->item ); + delete handler->array[ index ]; + handler->array.RemoveAt( index ); + } + delete service; + return( 0 ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// StartResolve +//=========================================================================================================================== + +OSStatus ExplorerBarWindow::StartResolve( ServiceInfo *inService ) +{ + OSStatus err; + + check( inService ); + + // Stop any current resolve that may be in progress. + + StopResolve(); + + // Resolve the service. + + err = DNSServiceResolve( &mResolveServiceRef, kDNSServiceFlagsNone, inService->ifi, + inService->name, inService->type, inService->domain, ResolveCallBack, inService->handler ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// StopResolve +//=========================================================================================================================== + +void ExplorerBarWindow::StopResolve( void ) +{ + if( mResolveServiceRef ) + { + DNSServiceRefDeallocate( mResolveServiceRef ); + mResolveServiceRef = NULL; + } +} + +//=========================================================================================================================== +// ResolveCallBack +//=========================================================================================================================== + +void CALLBACK_COMPAT + ExplorerBarWindow::ResolveCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ) +{ + ExplorerBarWindow * obj; + ServiceHandlerEntry * handler; + OSStatus err; + + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inErrorCode ); + DEBUG_UNUSED( inFullName ); + + require_noerr( inErrorCode, exit ); + handler = (ServiceHandlerEntry *) inContext; + check( handler ); + obj = handler->obj; + check( obj ); + + try + { + ResolveInfo * resolve; + BOOL ok; + + dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName ); + + // Stop resolving after the first good result. + + obj->StopResolve(); + + // Post a message to the main thread so it can handle it since MFC is not thread safe. + + resolve = new ResolveInfo; + require_action( resolve, exit, err = kNoMemoryErr ); + + UTF8StringToStringObject( inHostName, resolve->host ); + resolve->port = ntohs( inPort ); + resolve->ifi = inInterfaceIndex; + resolve->handler = handler; + + err = resolve->txt.SetData( inTXT, inTXTSize ); + check_noerr( err ); + + ok = ::PostMessage( obj->GetSafeHwnd(), WM_PRIVATE_RESOLVE, 0, (LPARAM) resolve ); + check( ok ); + if( !ok ) + { + delete resolve; + } + } + catch( ... ) + { + dlog( kDebugLevelError, "ResolveCallBack: exception thrown\n" ); + } + +exit: + return; +} + +//=========================================================================================================================== +// OnResolve +//=========================================================================================================================== + +LONG ExplorerBarWindow::OnResolve( WPARAM inWParam, LPARAM inLParam ) +{ + ResolveInfo * resolve; + CString url; + uint8_t * path; + size_t pathSize; + char * pathPrefix; + CString username; + CString password; + + DEBUG_UNUSED( inWParam ); + + resolve = reinterpret_cast < ResolveInfo * > ( inLParam ); + check( resolve ); + + // Get login info if needed. + + if( resolve->handler->needsLogin ) + { + LoginDialog dialog; + + if( !dialog.GetLogin( username, password ) ) + { + goto exit; + } + } + + // If the HTTP TXT record is a "path=" entry, use it as the resource path. Otherwise, use "/". + + pathPrefix = ""; + if( strcmp( resolve->handler->type, "_http._tcp" ) == 0 ) + { + resolve->txt.GetData( &path, &pathSize ); + if( pathSize > 0 ) + { + pathSize = *path++; + } + if( ( pathSize > kTXTRecordKeyPathSize ) && ( memicmp( path, kTXTRecordKeyPath, kTXTRecordKeyPathSize ) == 0 ) ) + { + path += kTXTRecordKeyPathSize; + pathSize -= kTXTRecordKeyPathSize; + } + else if( pathSize == 0 ) + { + path = (uint8_t *) "/"; + pathSize = 1; + } + if( *path != '/' ) + { + pathPrefix = "/"; + } + } + else + { + path = (uint8_t *) ""; + pathSize = 1; + } + + // Build the URL in the following format: + // + // [[:]@][] + + url.AppendFormat( TEXT( "%S" ), resolve->handler->urlScheme ); // URL Scheme + if( username.GetLength() > 0 ) + { + url.AppendFormat( TEXT( "%s" ), username ); // Username + if( password.GetLength() > 0 ) + { + url.AppendFormat( TEXT( ":%s" ), password ); // Password + } + url.AppendFormat( TEXT( "@" ) ); + } + + url += resolve->host; // Host + url.AppendFormat( TEXT( ":%d" ), resolve->port ); // :Port + url.AppendFormat( TEXT( "%S" ), pathPrefix ); // Path Prefix ("/" or empty). + url.AppendFormat( TEXT( "%.*S" ), (int) pathSize, (char *) path ); // Path (possibly empty). + + // Tell Internet Explorer to go to the URL. + + check( mOwner ); + mOwner->GoToURL( url ); + +exit: + delete resolve; + return( 0 ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// FindServiceArrayIndex +//=========================================================================================================================== + +DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const ServiceInfo &inService, int &outIndex ) +{ + int result; + int lo; + int hi; + int mid; + + result = -1; + mid = 0; + lo = 0; + hi = (int)( inArray.GetSize() - 1 ); + while( lo <= hi ) + { + mid = ( lo + hi ) / 2; + result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName ); + if( result == 0 ) + { + result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi ); + } + if( result == 0 ) + { + break; + } + else if( result < 0 ) + { + hi = mid - 1; + } + else + { + lo = mid + 1; + } + } + if( result == 0 ) + { + mid += 1; // Bump index so new item is inserted after matching item. + } + else if( result > 0 ) + { + mid += 1; + } + outIndex = mid; + return( result ); +} + +//=========================================================================================================================== +// UTF8StringToStringObject +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus UTF8StringToStringObject( const char *inUTF8, CString &inObject ) +{ + OSStatus 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 = ERROR_SUCCESS; + +exit: + if( unicode ) + { + free( unicode ); + } + return( err ); +} diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.h b/mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.h new file mode 100644 index 0000000..ac234f5 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.h @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerBarWindow.h,v $ +Revision 1.3 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.2 2004/04/08 09:43:43 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ + +#ifndef __EXPLORER_BAR_WINDOW__ +#define __EXPLORER_BAR_WINDOW__ + +#pragma once + +#include "afxtempl.h" + +#include "DNSSD.h" + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +// Forward Declarations + +struct ServiceHandlerEntry; +class ExplorerBarWindow; + +// ServiceInfo + +struct ServiceInfo +{ + CString displayName; + char * name; + char * type; + char * domain; + uint32_t ifi; + HTREEITEM item; + ServiceHandlerEntry * handler; + + ServiceInfo( void ) + { + item = NULL; + type = NULL; + domain = NULL; + handler = NULL; + } + + ~ServiceInfo( void ) + { + if( name ) + { + free( name ); + } + if( type ) + { + free( type ); + } + if( domain ) + { + free( domain ); + } + } +}; + +typedef CArray < ServiceInfo *, ServiceInfo * > ServiceInfoArray; + +// TextRecord + +struct TextRecord +{ + uint8_t * mData; + size_t mSize; + + TextRecord( void ) + { + mData = NULL; + mSize = 0; + } + + ~TextRecord( void ) + { + if( mData ) + { + free( mData ); + } + } + + void GetData( void *outData, size_t *outSize ) + { + if( outData ) + { + *( (void **) outData ) = mData; + } + if( outSize ) + { + *outSize = mSize; + } + } + + OSStatus SetData( const void *inData, size_t inSize ) + { + OSStatus err; + uint8_t * newData; + + newData = (uint8_t *) malloc( inSize ); + require_action( newData, exit, err = kNoMemoryErr ); + memcpy( newData, inData, inSize ); + + if( mData ) + { + free( mData ); + } + mData = newData; + mSize = inSize; + err = kNoErr; + + exit: + return( err ); + } +}; + +// ResolveInfo + +struct ResolveInfo +{ + CString host; + uint16_t port; + uint32_t ifi; + TextRecord txt; + ServiceHandlerEntry * handler; +}; + +// ServiceHandlerEntry + +struct ServiceHandlerEntry +{ + const char * type; + const char * urlScheme; + DNSServiceRef ref; + ServiceInfoArray array; + HTREEITEM treeItem; + bool treeFirst; + ExplorerBarWindow * obj; + bool needsLogin; + + ServiceHandlerEntry( void ) + { + type = NULL; + urlScheme = NULL; + ref = NULL; + treeItem = NULL; + treeFirst = true; + obj = NULL; + needsLogin = false; + } + + ~ServiceHandlerEntry( void ) + { + if( ref ) + { + DNSServiceRefDeallocate( ref ); + } + + int i; + int n; + + n = (int) array.GetSize(); + for( i = 0; i < n; ++i ) + { + delete array[ i ]; + } + } +}; + +typedef CArray < ServiceHandlerEntry *, ServiceHandlerEntry * > ServiceHandlerArray; + +//=========================================================================================================================== +// ExplorerBarWindow +//=========================================================================================================================== + +class ExplorerBar; // Forward Declaration + +class ExplorerBarWindow : public CWnd +{ + protected: + + ExplorerBar * mOwner; + CTreeCtrl mTree; + + ServiceHandlerArray mServiceHandlers; + DNSServiceRef mResolveServiceRef; + + public: + + ExplorerBarWindow( void ); + virtual ~ExplorerBarWindow( void ); + + protected: + + // General + + afx_msg int OnCreate( LPCREATESTRUCT inCreateStruct ); + afx_msg void OnDestroy( void ); + afx_msg void OnSize( UINT inType, int inX, int inY ); + afx_msg void OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult ); + + // Browsing + + static void CALLBACK_COMPAT + BrowseCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + afx_msg LONG OnServiceAdd( WPARAM inWParam, LPARAM inLParam ); + afx_msg LONG OnServiceRemove( WPARAM inWParam, LPARAM inLParam ); + + // Resolving + + OSStatus StartResolve( ServiceInfo *inService ); + void StopResolve( void ); + + static void CALLBACK_COMPAT + ResolveCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ); + afx_msg LONG OnResolve( WPARAM inWParam, LPARAM inLParam ); + + // Accessors + + public: + + ExplorerBar * GetOwner( void ) const { return( mOwner ); } + void SetOwner( ExplorerBar *inOwner ) { mOwner = inOwner; } + + DECLARE_MESSAGE_MAP() +}; + +#endif // __EXPLORER_BAR_WINDOW__ diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.cpp b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.cpp new file mode 100644 index 0000000..9528577 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.cpp @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: ExplorerPlugin.cpp,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ + +#include "StdAfx.h" + +// The following 2 includes have to be in this order and INITGUID must be defined here, before including the file +// that specifies the GUID(s), and nowhere else. The reason for this is that initguid.h doesn't provide separate +// define and declare macros for GUIDs so you have to #define INITGUID in the single file where you want to define +// your GUID then in all the other files that just need the GUID declared, INITGUID must not be defined. + +#define INITGUID +#include +#include "ExplorerPlugin.h" + +#include +#include + +#include "CommonServices.h" +#include "DebugServices.h" + +#include "ClassFactory.h" +#include "Resource.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// DLL Exports + +extern "C" BOOL WINAPI DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ); + +// MFC Support + +DEBUG_LOCAL OSStatus MFCDLLProcessAttach( HINSTANCE inInstance ); +DEBUG_LOCAL void MFCDLLProcessDetach( HINSTANCE inInstance ); +DEBUG_LOCAL void MFCDLLThreadDetach( HINSTANCE inInstance ); + +// Utilities + +DEBUG_LOCAL OSStatus RegisterServer( HINSTANCE inInstance, CLSID inCLSID, LPCTSTR inName ); +DEBUG_LOCAL OSStatus RegisterCOMCategory( CLSID inCLSID, CATID inCategoryID, BOOL inRegister ); + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +HINSTANCE gInstance = NULL; +int gDLLRefCount = 0; +CWinApp gApp; + +#if 0 +#pragma mark - +#pragma mark == DLL Exports == +#endif + +//=========================================================================================================================== +// DllMain +//=========================================================================================================================== + +BOOL WINAPI DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) +{ + BOOL ok; + OSStatus err; + + DEBUG_UNUSED( inReserved ); + + ok = TRUE; + switch( inReason ) + { + case DLL_PROCESS_ATTACH: + gInstance = inInstance; + debug_initialize( kDebugOutputTypeWindowsEventLog, "RendezvousBar", inInstance ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelTrace ); + dlog( kDebugLevelTrace, "\nDllMain: process attach\n" ); + + err = MFCDLLProcessAttach( inInstance ); + ok = ( err == kNoErr ); + require_noerr( err, exit ); + break; + + case DLL_PROCESS_DETACH: + dlog( kDebugLevelTrace, "DllMain: process detach\n" ); + MFCDLLProcessDetach( inInstance ); + break; + + case DLL_THREAD_ATTACH: + dlog( kDebugLevelTrace, "DllMain: thread attach\n" ); + break; + + case DLL_THREAD_DETACH: + dlog( kDebugLevelTrace, "DllMain: thread detach\n" ); + MFCDLLThreadDetach( inInstance ); + break; + + default: + dlog( kDebugLevelTrace, "DllMain: unknown reason code (%d)\n",inReason ); + break; + } + +exit: + return( ok ); +} + +//=========================================================================================================================== +// DllCanUnloadNow +//=========================================================================================================================== + +STDAPI DllCanUnloadNow( void ) +{ + dlog( kDebugLevelTrace, "DllCanUnloadNow (refCount=%d)\n", gDLLRefCount ); + + return( gDLLRefCount == 0 ); +} + +//=========================================================================================================================== +// DllGetClassObject +//=========================================================================================================================== + +STDAPI DllGetClassObject( REFCLSID inCLSID, REFIID inIID, LPVOID *outResult ) +{ + HRESULT err; + BOOL ok; + ClassFactory * factory; + + dlog( kDebugLevelTrace, "DllGetClassObject\n" ); + + *outResult = NULL; + + // Check if the class ID is supported. + + ok = IsEqualCLSID( inCLSID, CLSID_ExplorerBar ); + require_action_quiet( ok, exit, err = CLASS_E_CLASSNOTAVAILABLE ); + + // Create the ClassFactory object. + + factory = NULL; + try + { + factory = new ClassFactory( inCLSID ); + } + catch( ... ) + { + // Do not let exception escape. + } + require_action( factory, exit, err = E_OUTOFMEMORY ); + + // Query for the specified interface. Release the factory since QueryInterface retains it. + + err = factory->QueryInterface( inIID, outResult ); + factory->Release(); + +exit: + return( err ); +} + +//=========================================================================================================================== +// DllRegisterServer +//=========================================================================================================================== + +STDAPI DllRegisterServer( void ) +{ + HRESULT err; + BOOL ok; + CString s; + + dlog( kDebugLevelTrace, "DllRegisterServer\n" ); + + ok = s.LoadString( IDS_NAME ); + require_action( ok, exit, err = E_UNEXPECTED ); + + err = RegisterServer( gInstance, CLSID_ExplorerBar, s ); + require_noerr( err, exit ); + + err = RegisterCOMCategory( CLSID_ExplorerBar, CATID_InfoBand, TRUE ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// DllUnregisterServer +//=========================================================================================================================== + +STDAPI DllUnregisterServer( void ) +{ + HRESULT err; + + dlog( kDebugLevelTrace, "DllUnregisterServer\n" ); + + err = RegisterCOMCategory( CLSID_ExplorerBar, CATID_InfoBand, FALSE ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == MFC Support == +#endif + +//=========================================================================================================================== +// MFCDLLProcessAttach +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus MFCDLLProcessAttach( HINSTANCE inInstance ) +{ + OSStatus err; + _AFX_THREAD_STATE * threadState; + AFX_MODULE_STATE * previousModuleState; + BOOL ok; + CWinApp * app; + + app = NULL; + + // Simulate what is done in dllmodul.cpp. + + threadState = AfxGetThreadState(); + check( threadState ); + previousModuleState = threadState->m_pPrevModuleState; + + ok = AfxWinInit( inInstance, NULL, TEXT( "" ), 0 ); + require_action( ok, exit, err = kUnknownErr ); + + app = AfxGetApp(); + require_action( ok, exit, err = kNotInitializedErr ); + + ok = app->InitInstance(); + require_action( ok, exit, err = kUnknownErr ); + + threadState->m_pPrevModuleState = previousModuleState; + threadState = NULL; + AfxInitLocalData( inInstance ); + err = kNoErr; + +exit: + if( err ) + { + if( app ) + { + app->ExitInstance(); + } + AfxWinTerm(); + } + if( threadState ) + { + threadState->m_pPrevModuleState = previousModuleState; + } + return( err ); +} + +//=========================================================================================================================== +// MFCDLLProcessDetach +//=========================================================================================================================== + +DEBUG_LOCAL void MFCDLLProcessDetach( HINSTANCE inInstance ) +{ + CWinApp * app; + + // Simulate what is done in dllmodul.cpp. + + app = AfxGetApp(); + if( app ) + { + app->ExitInstance(); + } + +#if( DEBUG ) + if( AfxGetModuleThreadState()->m_nTempMapLock != 0 ) + { + dlog( kDebugLevelWarning, "Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock ); + } +#endif + + AfxLockTempMaps(); + AfxUnlockTempMaps( -1 ); + + // Terminate the library before destructors are called. + + AfxWinTerm(); + AfxTermLocalData( inInstance, TRUE ); +} + +//=========================================================================================================================== +// MFCDLLFinalize +//=========================================================================================================================== + +DEBUG_LOCAL void MFCDLLThreadDetach( HINSTANCE inInstance ) +{ + // Simulate what is done in dllmodul.cpp. + +#if( DEBUG ) + if( AfxGetModuleThreadState()->m_nTempMapLock != 0 ) + { + dlog( kDebugLevelWarning, "Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock ); + } +#endif + + AfxLockTempMaps(); + AfxUnlockTempMaps( -1 ); + AfxTermThread( inInstance ); +} + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +//=========================================================================================================================== +// RegisterServer +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus RegisterServer( HINSTANCE inInstance, CLSID inCLSID, LPCTSTR inName ) +{ + typedef struct RegistryBuilder RegistryBuilder; + struct RegistryBuilder + { + HKEY rootKey; + LPCTSTR subKey; + LPCTSTR valueName; + LPCTSTR data; + }; + + OSStatus err; + LPWSTR clsidWideString; + TCHAR clsidString[ 64 ]; + DWORD nChars; + size_t n; + size_t i; + HKEY key; + TCHAR keyName[ MAX_PATH ]; + TCHAR moduleName[ MAX_PATH ] = TEXT( "" ); + TCHAR data[ MAX_PATH ]; + RegistryBuilder entries[] = + { + { HKEY_CLASSES_ROOT, TEXT( "CLSID\\%s" ), NULL, inName }, + { HKEY_CLASSES_ROOT, TEXT( "CLSID\\%s\\InprocServer32" ), NULL, moduleName }, + { HKEY_CLASSES_ROOT, TEXT( "CLSID\\%s\\InprocServer32" ), TEXT( "ThreadingModel" ), TEXT( "Apartment" ) } + }; + DWORD size; + OSVERSIONINFO versionInfo; + + // Convert the CLSID to a string based on the encoding of this code (ANSI or Unicode). + + err = StringFromIID( inCLSID, &clsidWideString ); + require_noerr( err, exit ); + require_action( clsidWideString, exit, err = kNoMemoryErr ); + + #ifdef UNICODE + lstrcpyn( clsidString, clsidWideString, sizeof_array( clsidString ) ); + CoTaskMemFree( clsidWideString ); + #else + nChars = WideCharToMultiByte( CP_ACP, 0, clsidWideString, -1, clsidString, sizeof_array( clsidString ), NULL, NULL ); + err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr ); + CoTaskMemFree( clsidWideString ); + require_noerr( err, exit ); + #endif + + // Register the CLSID entries. + + nChars = GetModuleFileName( inInstance, moduleName, sizeof_array( moduleName ) ); + err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + n = sizeof_array( entries ); + for( i = 0; i < n; ++i ) + { + wsprintf( keyName, entries[ i ].subKey, clsidString ); + err = RegCreateKeyEx( entries[ i ].rootKey, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL ); + require_noerr( err, exit ); + + size = (DWORD)( ( lstrlen( entries[ i ].data ) + 1 ) * sizeof( TCHAR ) ); + err = RegSetValueEx( key, entries[ i ].valueName, 0, REG_SZ, (LPBYTE) entries[ i ].data, size ); + RegCloseKey( key ); + require_noerr( err, exit ); + } + + // If running on NT, register the extension as approved. + + versionInfo.dwOSVersionInfoSize = sizeof( versionInfo ); + GetVersionEx( &versionInfo ); + if( versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) + { + lstrcpyn( keyName, TEXT( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved" ), sizeof_array( keyName ) ); + err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL ); + require_noerr( err, exit ); + + lstrcpyn( data, inName, sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + err = RegSetValueEx( key, clsidString, 0, REG_SZ, (LPBYTE) data, size ); + RegCloseKey( key ); + } + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// RegisterCOMCategory +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus RegisterCOMCategory( CLSID inCLSID, CATID inCategoryID, BOOL inRegister ) +{ + HRESULT err; + ICatRegister * cat; + + err = CoInitialize( NULL ); + require( SUCCEEDED( err ), exit ); + + err = CoCreateInstance( CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (LPVOID *) &cat ); + check( SUCCEEDED( err ) ); + if( SUCCEEDED( err ) ) + { + if( inRegister ) + { + err = cat->RegisterClassImplCategories( inCLSID, 1, &inCategoryID ); + check_noerr( err ); + } + else + { + err = cat->UnRegisterClassImplCategories( inCLSID, 1, &inCategoryID ); + check_noerr( err ); + } + cat->Release(); + } + CoUninitialize(); + +exit: + return( err ); +} diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.def b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.def new file mode 100644 index 0000000..028856c --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.def @@ -0,0 +1,37 @@ +; +; Copyright (c) 2003-2004 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: ExplorerPlugin.def,v $ +; Revision 1.1 2004/01/30 03:01:56 bradley +; Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. +; +; + +LIBRARY ExplorerPlugin + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/mDNSMacOS9/mDNSPrefixCarbon.h b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.h similarity index 54% rename from mDNSMacOS9/mDNSPrefixCarbon.h rename to mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.h index 95f1a48..e072af0 100644 --- a/mDNSMacOS9/mDNSPrefixCarbon.h +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -21,23 +23,20 @@ * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): + +$Log: ExplorerPlugin.h,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. -$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 +//=========================================================================================================================== +// Globals +//=========================================================================================================================== -#define TARGET_API_MAC_CARBON 1 -#define OTCARBONAPPLICATION 1 +// {9999A076-A9E2-4c99-8A2B-632FC9429223} +DEFINE_GUID(CLSID_ExplorerBar, +0x9999a076, 0xa9e2, 0x4c99, 0x8a, 0x2b, 0x63, 0x2f, 0xc9, 0x42, 0x92, 0x23); -#define __ONLYSYSTEMTASK__ 1 -#define MDNS_DEBUGMSGS 0 +extern HINSTANCE gInstance; +extern int gDLLRefCount; diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.rc b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.rc new file mode 100644 index 0000000..f9e79ba --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.rc @@ -0,0 +1,173 @@ +// 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" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// 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 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Apple Computer, Inc." + VALUE "FileDescription", "Rendezvous Bar" + VALUE "FileVersion", "1.0.0.1" + VALUE "InternalName", "ExplorerPlugin.dll" + VALUE "LegalCopyright", "Copyright (C) 2003-2004 Apple Computer, Inc." + VALUE "OriginalFilename", "ExplorerPlugin.dll" + VALUE "ProductName", "Rendezvous Bar" + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_LOGIN DIALOGEX 0, 0, 180, 89 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION +CAPTION "Login" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Enter a username and password. Leave blank to use the default username and/or password.", + IDC_STATIC,8,8,156,16,NOT WS_GROUP + RTEXT "Username:",IDC_STATIC,10,34,36,8,NOT WS_GROUP + EDITTEXT IDC_LOGIN_USERNAME_TEXT,50,32,118,12,ES_AUTOHSCROLL + RTEXT "Password:",IDC_STATIC,10,50,36,8,NOT WS_GROUP + EDITTEXT IDC_LOGIN_PASSWORD_TEXT,50,48,118,12,ES_PASSWORD | + ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,129,70,44,14 + PUSHBUTTON "Cancel",IDCANCEL,77,70,44,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_LOGIN, DIALOG + BEGIN + BOTTOMMARGIN, 62 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_NAME "Rendezvous" + IDS_WEB_SITES "Web Sites" + IDS_FTP_SITES "FTP Sites" + IDS_PRINTERS "Printers" + IDS_RENDEZVOUS_NOT_AVAILABLE "Rendezvous Not Available" +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) +LANGUAGE 9, 1 +#pragma code_page(1252) +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.sln b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.sln new file mode 100644 index 0000000..24abed3 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPlugin", "ExplorerPlugin.vcproj", "{BB8AC1B5-6587-4163-BDC6-788B157705CA}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {BB8AC1B5-6587-4163-BDC6-788B157705CA}.Debug.ActiveCfg = Debug|Win32 + {BB8AC1B5-6587-4163-BDC6-788B157705CA}.Debug.Build.0 = Debug|Win32 + {BB8AC1B5-6587-4163-BDC6-788B157705CA}.Release.ActiveCfg = Release|Win32 + {BB8AC1B5-6587-4163-BDC6-788B157705CA}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.vcproj b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.vcproj new file mode 100644 index 0000000..ec8b589 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.vcproj @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.cpp b/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.cpp new file mode 100644 index 0000000..1947947 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: LoginDialog.cpp,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ + +#include +#include + +#include "stdafx.h" + +#include "LoginDialog.h" + +// MFC Debugging + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +//=========================================================================================================================== +// Message Map +//=========================================================================================================================== + +BEGIN_MESSAGE_MAP( LoginDialog, CDialog ) +END_MESSAGE_MAP() + +//=========================================================================================================================== +// LoginDialog +//=========================================================================================================================== + +LoginDialog::LoginDialog( CWnd *inParent ) + : CDialog( LoginDialog::IDD, inParent ) +{ + // +} + +//=========================================================================================================================== +// OnInitDialog +//=========================================================================================================================== + +BOOL LoginDialog::OnInitDialog( void ) +{ + CDialog::OnInitDialog(); + return( TRUE ); +} + +//=========================================================================================================================== +// DoDataExchange +//=========================================================================================================================== + +void LoginDialog::DoDataExchange( CDataExchange *inDX ) +{ + CDialog::DoDataExchange( inDX ); +} + +//=========================================================================================================================== +// OnOK +//=========================================================================================================================== + +void LoginDialog::OnOK( void ) +{ + const CWnd * control; + + // Username + + control = GetDlgItem( IDC_LOGIN_USERNAME_TEXT ); + assert( control ); + if( control ) + { + control->GetWindowText( mUsername ); + } + + // Password + + control = GetDlgItem( IDC_LOGIN_PASSWORD_TEXT ); + assert( control ); + if( control ) + { + control->GetWindowText( mPassword ); + } + + CDialog::OnOK(); +} + +//=========================================================================================================================== +// GetLogin +//=========================================================================================================================== + +BOOL LoginDialog::GetLogin( CString &outUsername, CString &outPassword ) +{ + if( DoModal() == IDOK ) + { + outUsername = mUsername; + outPassword = mPassword; + return( TRUE ); + } + return( FALSE ); +} diff --git a/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.h b/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.h new file mode 100644 index 0000000..11d9d1c --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: LoginDialog.h,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ + +#ifndef __LOGIN_DIALOG__ +#define __LOGIN_DIALOG__ + +#pragma once + +#include "Resource.h" + +//=========================================================================================================================== +// LoginDialog +//=========================================================================================================================== + +class LoginDialog : public CDialog +{ + protected: + + CString mUsername; + CString mPassword; + + public: + + enum { IDD = IDD_LOGIN }; + + LoginDialog( CWnd *inParent = NULL ); + + virtual BOOL GetLogin( CString &outUsername, CString &outPassword ); + + protected: + + virtual BOOL OnInitDialog( void ); + virtual void DoDataExchange( CDataExchange *inDX ); + virtual void OnOK( void ); + + DECLARE_MESSAGE_MAP() +}; + +#endif // __LOGIN_DIALOG__ diff --git a/mDNSWindows/Applications/ExplorerPlugin/ReadMe.txt b/mDNSWindows/Applications/ExplorerPlugin/ReadMe.txt new file mode 100644 index 0000000..ed73c22 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/ReadMe.txt @@ -0,0 +1,9 @@ +The Rendezvous Explorer Plugin is a vertical Explorer bar. It lets you browse for Rendezvous-enabled services directly within Internet Explorer. + +This DLL needs to be registered to work. The Visual Studio project automatically registers the DLL after each successful build. If you need to manually register the DLL for some reason, you can execute the following line from the DOS command line prompt ("" is the actual parent path of the DLL): + +regsvr32.exe /s /c \ExplorerPlugin.dll + +For more information, see the Band Objects topic in the Microsoft Platform SDK documentation and check the following URL: + + diff --git a/mDNSWindows/Applications/ExplorerPlugin/Resource.h b/mDNSWindows/Applications/ExplorerPlugin/Resource.h new file mode 100644 index 0000000..744ae44 --- /dev/null +++ b/mDNSWindows/Applications/ExplorerPlugin/Resource.h @@ -0,0 +1,23 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ExplorerPlugin.rc +// +#define IDS_NAME 106 +#define IDS_WEB_SITES 107 +#define IDS_FTP_SITES 108 +#define IDS_PRINTERS 109 +#define IDS_RENDEZVOUS_NOT_AVAILABLE 110 +#define IDD_LOGIN 145 +#define IDC_LOGIN_USERNAME_TEXT 1182 +#define IDC_LOGIN_PASSWORD_TEXT 1183 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 115 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h b/mDNSWindows/Applications/ExplorerPlugin/StdAfx.cpp similarity index 74% rename from mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h rename to mDNSWindows/Applications/ExplorerPlugin/StdAfx.cpp index 7bb3e90..673073f 100644 --- a/mDNSMacOSX/Applications/HAAutomounter/HAAutomounter.h +++ b/mDNSWindows/Applications/ExplorerPlugin/StdAfx.cpp @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -21,20 +23,11 @@ * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): + +$Log: StdAfx.cpp,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. -$Log: HAAutomounter.h,v $ -Revision 1.4 2003/08/12 19:55:08 cheshire -Update to APSL 2.0 - - */ - -#import - - -@interface HAAutomounter : NSObject { - - NSNetServiceBrowser *browser; - NSNetService *resolver; -} +*/ -@end +#include "StdAfx.h" diff --git a/mDNSMacOS9/mDNSPrefixCarbonDebug.h b/mDNSWindows/Applications/ExplorerPlugin/StdAfx.h similarity index 53% rename from mDNSMacOS9/mDNSPrefixCarbonDebug.h rename to mDNSWindows/Applications/ExplorerPlugin/StdAfx.h index 2c31a97..45020c1 100644 --- a/mDNSMacOS9/mDNSPrefixCarbonDebug.h +++ b/mDNSWindows/Applications/ExplorerPlugin/StdAfx.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -21,23 +23,30 @@ * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): + +$Log: StdAfx.h,v $ +Revision 1.1 2004/01/30 03:01:56 bradley +Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within Internet Explorer. + +*/ -$Log: mDNSPrefixCarbonDebug.h,v $ -Revision 1.5 2003/08/12 19:56:24 cheshire -Update to APSL 2.0 +#ifndef __STDAFX__ +#define __STDAFX__ - */ +#pragma once -// 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 +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#endif -// 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 +#include // MFC core and standard components +#include // MFC extensions +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT + #include // MFC support for Windows Common Controls +#endif -#define TARGET_API_MAC_CARBON 1 -#define OTCARBONAPPLICATION 1 +#include +#include // MFC socket extensions -#define __ONLYSYSTEMTASK__ 1 -#define MDNS_DEBUGMSGS 1 +#endif // __STDAFX__ diff --git a/mDNSWindows/Applications/Java/makefile b/mDNSWindows/Applications/Java/makefile new file mode 100755 index 0000000..3ed3f9f --- /dev/null +++ b/mDNSWindows/Applications/Java/makefile @@ -0,0 +1,134 @@ +# Copyright (c) 2002-2004 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@ +# +# $Log: makefile,v $ +# Revision 1.2 2004/05/01 00:31:41 rpantos +# Change line endings for CVS. +# +# Revision 1.1 2004/04/30 16:32:34 rpantos +# First checked in. +# +# This Makefile builds a .jar file and accompanying JNI support library +# containing the DNSSD implementation for Java and support classes. +# +# Prior to building Java support, you must build DNSSD.dll. +# +# nmake with no arguments builds all production targets. +# 'nmake DEBUG=1' to build debugging targets. +# 'nmake clean' or 'nmake clean DEBUG=1' to delete prod/debug objects & targets +# +# To run nmake, you may need to set up your PATH correctly, using a script +# such as: "\Program Files\Microsoft Visual Studio .NET\Vc7\bin\vcvars32.bat" +# +# The default location of the JDK is d:\javasdk. You can override this on the +# command line (e.g. 'nmake JDK=\j2dk1.4.2_03'). + +############################################################################ + +COREDIR = ..\..\..\mDNSCore +SHAREDDIR = ..\..\..\mDNSShared + +JDK = d:\javasdk + +CC = cl +LD = ld +CP = copy +RM = del /Q +RMDIR = rmdir /S /Q +JAVAC = $(JDK)\bin\javac +JAVAH = $(JDK)\bin\javah +JAR = $(JDK)\bin\jar +CFLAGS_COMMON = -LD -DAUTO_CALLBACKS=1 -DUSE_WIN_API=1 -I. -I..\.. \ + -I$(COREDIR) -I$(SHAREDDIR) -I$(JDK)\include -I$(JDK)\include\win32 + +# Set up diverging paths for debug vs. prod builds +DEBUG=0 +!if $(DEBUG) == 1 +CFLAGS_DEBUG = -Zi -DMDNS_DEBUGMSGS=2 +OBJDIR = objects\debug +BUILDDIR = build\debug +LIBDIR = ..\DLL\Debug +!else +CFLAGS_DEBUG = -Os -DMDNS_DEBUGMSGS=0 +OBJDIR = objects\prod +BUILDDIR = build\prod +LIBDIR = ..\DLL\Release +!endif + +CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_DEBUG) +JAVACFLAGS = $(CFLAGS) $(JAVACFLAGS_OS) + +############################################################################# + +all: setup Java + +# 'setup' sets up the build directory structure the way we want +setup: + @if not exist objects mkdir objects + @if not exist build mkdir build + @if not exist $(OBJDIR) mkdir $(OBJDIR) + @if not exist $(BUILDDIR) mkdir $(BUILDDIR) + +# clean removes targets and objects +clean: + @if exist $(OBJDIR) $(RMDIR) $(OBJDIR) + @if exist $(BUILDDIR) $(RMDIR) $(BUILDDIR) + +############################################################################# + +# The following targets build Java wrappers for the dns-sd.h API. + +Java: setup $(BUILDDIR)\dns_sd.jar $(BUILDDIR)\jdns_sd.dll + @echo "Java wrappers done" + +JAVASRC = $(SHAREDDIR)\Java +JARCONTENTS = $(OBJDIR)\com\apple\dnssd\DNSSDService.class \ + $(OBJDIR)\com\apple\dnssd\DNSRecord.class \ + $(OBJDIR)\com\apple\dnssd\DNSSDException.class \ + $(OBJDIR)\com\apple\dnssd\TXTRecord.class \ + $(OBJDIR)\com\apple\dnssd\DNSSDRegistration.class \ + $(OBJDIR)\com\apple\dnssd\BaseListener.class \ + $(OBJDIR)\com\apple\dnssd\BrowseListener.class \ + $(OBJDIR)\com\apple\dnssd\ResolveListener.class \ + $(OBJDIR)\com\apple\dnssd\RegisterListener.class \ + $(OBJDIR)\com\apple\dnssd\QueryListener.class \ + $(OBJDIR)\com\apple\dnssd\DomainListener.class \ + $(OBJDIR)\com\apple\dnssd\DNSSD.class + +$(BUILDDIR)\dns_sd.jar: $(JARCONTENTS) + $(JAR) -cf $@ -C $(OBJDIR) com + +$(BUILDDIR)\jdns_sd.dll: $(JAVASRC)\JNISupport.c $(OBJDIR)\DNSSD.java.h + $(CC) -Fe$@ $(JAVASRC)\JNISupport.c $(CFLAGS) -I$(OBJDIR) \ + $(LIBDIR)\DNSSD.lib $(JDK)\lib\jvm.lib + +.SUFFIXES : .java +{$(JAVASRC)}.java{$(OBJDIR)\com\apple\dnssd}.class: + $(JAVAC) -d $(OBJDIR) -classpath $(OBJDIR) $< + +$(OBJDIR)\DNSSD.java.h: $(OBJDIR)\com\apple\dnssd\DNSSD.class + $(JAVAH) -classpath $(OBJDIR) -o $@ \ + com.apple.dnssd.AppleBrowser \ + com.apple.dnssd.AppleResolver \ + com.apple.dnssd.AppleRegistration \ + com.apple.dnssd.AppleQuery \ + com.apple.dnssd.AppleService + diff --git a/mDNSMacOSX/Applications/HAAutomounter/main.m b/mDNSWindows/Applications/NSPTool/Prefix.h similarity index 67% rename from mDNSMacOSX/Applications/HAAutomounter/main.m rename to mDNSWindows/Applications/NSPTool/Prefix.h index 6ad4471..c7f01c5 100644 --- a/mDNSMacOSX/Applications/HAAutomounter/main.m +++ b/mDNSWindows/Applications/NSPTool/Prefix.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -22,23 +24,20 @@ Change History (most recent first): -$Log: main.m,v $ -Revision 1.4 2003/08/12 19:55:08 cheshire -Update to APSL 2.0 - - */ - -#import - -#import "HAAutomounter.h" - -int main (int argc, const char * argv[]) { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +$Log: Prefix.h,v $ +Revision 1.1 2004/01/30 03:02:58 bradley +NameSpace Provider Tool for installing, removing, list, etc. NameSpace Providers. + +*/ - [[HAAutomounter alloc] init]; +#ifndef __PREFIX__ +#define __PREFIX__ - [[NSRunLoop currentRunLoop] run]; +#if( defined( _DEBUG ) ) + #define DEBUG 1 + #define MDNS_DEBUGMSGS 1 +#else + #define DEBUG 0 +#endif - [pool release]; - return 0; -} +#endif // __PREFIX__ diff --git a/mDNSWindows/Applications/NSPTool/Tool.c b/mDNSWindows/Applications/NSPTool/Tool.c new file mode 100644 index 0000000..bee72f8 --- /dev/null +++ b/mDNSWindows/Applications/NSPTool/Tool.c @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Tool.c,v $ +Revision 1.1 2004/01/30 03:02:58 bradley +NameSpace Provider Tool for installing, removing, list, etc. NameSpace Providers. + +*/ + +#include +#include + +#include "CommonServices.h" +#include "DebugServices.h" + +#include +#include + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +int main( int argc, char *argv[] ); +DEBUG_LOCAL void Usage( void ); +DEBUG_LOCAL int ProcessArgs( int argc, char *argv[] ); +DEBUG_LOCAL OSStatus InstallNSP( const char *inName, const char *inGUID, const char *inPath ); +DEBUG_LOCAL OSStatus RemoveNSP( const char *inGUID ); +DEBUG_LOCAL OSStatus EnableNSP( const char *inGUID, BOOL inEnable ); +DEBUG_LOCAL OSStatus ListNameSpaces( void ); +DEBUG_LOCAL OSStatus ReorderNameSpaces( void ); + +DEBUG_LOCAL WCHAR * CharToWCharString( const char *inCharString, WCHAR *outWCharString ); +DEBUG_LOCAL char * GUIDtoString( const GUID *inGUID, char *outString ); +DEBUG_LOCAL OSStatus StringToGUID( const char *inCharString, GUID *outGUID ); + +//=========================================================================================================================== +// main +//=========================================================================================================================== + +int main( int argc, char *argv[] ) +{ + OSStatus err; + + debug_initialize( kDebugOutputTypeMetaConsole ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose ); + + err = ProcessArgs( argc, argv ); + return( (int) err ); +} + +//=========================================================================================================================== +// Usage +//=========================================================================================================================== + +DEBUG_LOCAL void Usage( void ) +{ + fprintf( stderr, "\n" ); + fprintf( stderr, "NSP Tool 1.0d1\n" ); + fprintf( stderr, " Name Space Provider Tool\n" ); + fprintf( stderr, "\n" ); + + fprintf( stderr, " -install - Installs a Name Space Provider\n" ); + fprintf( stderr, "\n" ); + fprintf( stderr, " Name of the NSP\n" ); + fprintf( stderr, " GUID of the NSP\n" ); + fprintf( stderr, " Path to the NSP file\n" ); + fprintf( stderr, "\n" ); + + fprintf( stderr, " -remove - Removes a Name Space Provider\n" ); + fprintf( stderr, "\n" ); + fprintf( stderr, " GUID of the NSP\n" ); + fprintf( stderr, "\n" ); + + fprintf( stderr, " -enable/-disable - Enables or Disables a Name Space Provider\n" ); + fprintf( stderr, "\n" ); + fprintf( stderr, " GUID of the NSP\n" ); + fprintf( stderr, "\n" ); + + fprintf( stderr, " -list - Lists Name Space Providers\n" ); + fprintf( stderr, " -reorder - Reorders Name Space Providers\n" ); + fprintf( stderr, " -h[elp] - Help\n" ); + fprintf( stderr, "\n" ); +} + +//=========================================================================================================================== +// ProcessArgs +//=========================================================================================================================== + +DEBUG_LOCAL int ProcessArgs( int argc, char* argv[] ) +{ + OSStatus err; + int i; + const char * name; + const char * guid; + const char * path; + + if( argc <= 1 ) + { + Usage(); + err = 0; + goto exit; + } + for( i = 1; i < argc; ++i ) + { + if( strcmp( argv[ i ], "-install" ) == 0 ) + { + // Install + + if( argc <= ( i + 3 ) ) + { + fprintf( stderr, "\n### ERROR: missing arguments for %s\n\n", argv[ i ] ); + Usage(); + err = kParamErr; + goto exit; + } + name = argv[ ++i ]; + guid = argv[ ++i ]; + path = argv[ ++i ]; + + if( *name == '\0' ) + { + name = "DotLocalNSP"; + } + if( *guid == '\0' ) + { + guid = "B600E6E9-553B-4a19-8696-335E5C896153"; + } + + err = InstallNSP( name, guid, path ); + require_noerr( err, exit ); + } + else if( strcmp( argv[ i ], "-remove" ) == 0 ) + { + // Remove + + if( argc <= ( i + 1 ) ) + { + fprintf( stderr, "\n### ERROR: missing arguments for %s\n\n", argv[ i ] ); + Usage(); + err = kParamErr; + goto exit; + } + guid = argv[ ++i ]; + if( *guid == '\0' ) + { + guid = "B600E6E9-553B-4a19-8696-335E5C896153"; + } + + err = RemoveNSP( guid ); + require_noerr( err, exit ); + } + else if( ( strcmp( argv[ i ], "-enable" ) == 0 ) || + ( strcmp( argv[ i ], "-disable" ) == 0 ) ) + { + BOOL enable; + + // Enable/Disable + + enable = ( strcmp( argv[ i ], "-enable" ) == 0 ); + if( argc <= ( i + 1 ) ) + { + fprintf( stderr, "\n### ERROR: missing arguments for %s\n\n", argv[ i ] ); + Usage(); + err = kParamErr; + goto exit; + } + guid = argv[ ++i ]; + + err = EnableNSP( guid, enable ); + require_noerr( err, exit ); + } + else if( strcmp( argv[ i ], "-list" ) == 0 ) + { + // List + + err = ListNameSpaces(); + require_noerr( err, exit ); + } + else if( strcmp( argv[ i ], "-reorder" ) == 0 ) + { + // Reorder + + err = ReorderNameSpaces(); + require_noerr( err, exit ); + } + else if( ( strcmp( argv[ i ], "-help" ) == 0 ) || + ( strcmp( argv[ i ], "-h" ) == 0 ) ) + { + // Help + + Usage(); + err = 0; + goto exit; + } + else + { + fprintf( stderr, "\n### ERROR: unknown argment: \"%s\"\n\n", argv[ i ] ); + Usage(); + err = kParamErr; + goto exit; + } + } + err = kNoErr; + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// InstallNSP +//=========================================================================================================================== + +OSStatus InstallNSP( const char *inName, const char *inGUID, const char *inPath ) +{ + OSStatus err; + size_t size; + WSADATA wsd; + WCHAR name[ 256 ]; + GUID guid; + WCHAR path[ MAX_PATH ]; + + require_action( inName && ( *inName != '\0' ), exit, err = kParamErr ); + require_action( inGUID && ( *inGUID != '\0' ), exit, err = kParamErr ); + require_action( inPath && ( *inPath != '\0' ), exit, err = kParamErr ); + + size = strlen( inName ); + require_action( size < sizeof_array( name ), exit, err = kSizeErr ); + CharToWCharString( inName, name ); + + err = StringToGUID( inGUID, &guid ); + require_noerr( err, exit ); + + size = strlen( inPath ); + require_action( size < sizeof_array( path ), exit, err = kSizeErr ); + CharToWCharString( inPath, path ); + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsd ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + + err = WSCInstallNameSpace( name, path, NS_DNS, 1, &guid ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + WSACleanup(); + require_noerr( err, exit ); + + fprintf( stderr, "Installed NSP \"%s\" (%s) at %s\n", inName, inGUID, inPath ); + +exit: + if( err != kNoErr ) + { + fprintf( stderr, "### FAILED (%d) to install \"%s\" (%s) Name Space Provider at %s\n", err, inName, inGUID, inPath ); + } + return( err ); +} + +//=========================================================================================================================== +// RemoveNSP +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus RemoveNSP( const char *inGUID ) +{ + OSStatus err; + WSADATA wsd; + GUID guid; + + require_action( inGUID && ( *inGUID != '\0' ), exit, err = kParamErr ); + + err = StringToGUID( inGUID, &guid ); + require_noerr( err, exit ); + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsd ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + + err = WSCUnInstallNameSpace( &guid ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + WSACleanup(); + require_noerr( err, exit ); + + fprintf( stderr, "Removed NSP %s\n", inGUID ); + +exit: + if( err != kNoErr ) + { + fprintf( stderr, "### FAILED (%d) to remove %s Name Space Provider\n", err, inGUID ); + } + return( err ); +} + +//=========================================================================================================================== +// EnableNSP +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus EnableNSP( const char *inGUID, BOOL inEnable ) +{ + OSStatus err; + WSADATA wsd; + GUID guid; + + require_action( inGUID && ( *inGUID != '\0' ), exit, err = kParamErr ); + + err = StringToGUID( inGUID, &guid ); + require_noerr( err, exit ); + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsd ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + + err = WSCEnableNSProvider( &guid, inEnable ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + WSACleanup(); + require_noerr( err, exit ); + + fprintf( stderr, "Removed NSP %s\n", inGUID ); + +exit: + if( err != kNoErr ) + { + fprintf( stderr, "### FAILED (%d) to remove %s Name Space Provider\n", err, inGUID ); + } + return( err ); +} + +//=========================================================================================================================== +// ListNameSpaces +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus ListNameSpaces( void ) +{ + OSStatus err; + WSADATA wsd; + bool started; + int n; + int i; + DWORD size; + WSANAMESPACE_INFO * array; + char s[ 256 ]; + + array = NULL; + started = false; + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsd ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + started = true; + + // Build an array of all the NSPs. Call it first with NULL to get the size, allocate a buffer, then get them into it. + + size = 0; + n = WSAEnumNameSpaceProviders( &size, NULL ); + err = translate_errno( n != SOCKET_ERROR, (OSStatus) GetLastError(), kUnknownErr ); + require_action( err == WSAEFAULT, exit, err = kUnknownErr ); + + array = (WSANAMESPACE_INFO *) malloc( size ); + require_action( array, exit, err = kNoMemoryErr ); + + n = WSAEnumNameSpaceProviders( &size, array ); + err = translate_errno( n != SOCKET_ERROR, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + fprintf( stdout, "\n" ); + for( i = 0; i < n; ++i ) + { + fprintf( stdout, "Name Space %d\n", i + 1 ); + fprintf( stdout, " NSProviderId: %s\n", GUIDtoString( &array[ i ].NSProviderId, s ) ); + fprintf( stdout, " dwNameSpace: %d\n", array[ i ].dwNameSpace ); + fprintf( stdout, " fActive: %s\n", array[ i ].fActive ? "YES" : "NO" ); + fprintf( stdout, " dwVersion: %d\n", array[ i ].dwVersion ); + fprintf( stdout, " lpszIdentifier: \"%s\"\n", array[ i ].lpszIdentifier ); + fprintf( stdout, "\n" ); + } + err = kNoErr; + +exit: + if( array ) + { + free( array ); + } + if( started ) + { + WSACleanup(); + } + if( err != kNoErr ) + { + fprintf( stderr, "### FAILED (%d) to list Name Space Providers\n", err ); + } + return( err ); +} + +//=========================================================================================================================== +// ReorderNameSpaces +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus ReorderNameSpaces( void ) +{ + OSStatus err; + WSADATA wsd; + bool started; + int n; + int i; + DWORD size; + WSANAMESPACE_INFO * array; + WCHAR name[ 256 ]; + WCHAR path[ MAX_PATH ]; + + array = NULL; + started = false; + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsd ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + started = true; + + // Build an array of all the NSPs. Call it first with NULL to get the size, allocate a buffer, then get them into it. + + size = 0; + n = WSAEnumNameSpaceProviders( &size, NULL ); + err = translate_errno( n != SOCKET_ERROR, (OSStatus) GetLastError(), kUnknownErr ); + require_action( err == WSAEFAULT, exit, err = kUnknownErr ); + + array = (WSANAMESPACE_INFO *) malloc( size ); + require_action( array, exit, err = kNoMemoryErr ); + + n = WSAEnumNameSpaceProviders( &size, array ); + err = translate_errno( n != SOCKET_ERROR, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + // Find the "Tcpip" NSP. + + for( i = 0; i < n; ++i ) + { + if( strcmp( array[ i ].lpszIdentifier, "Tcpip" ) == 0 ) + { + break; + } + } + require_action( i < n, exit, err = kNotFoundErr ); + + // Uninstall it then re-install it to move it to the end. + + size = strlen( array[ i ].lpszIdentifier ); + require_action( size < sizeof_array( name ), exit, err = kSizeErr ); + CharToWCharString( array[ i ].lpszIdentifier, name ); + + size = strlen( "%SystemRoot%\\System32\\mswsock.dll" ); + require_action( size < sizeof_array( path ), exit, err = kSizeErr ); + CharToWCharString( "%SystemRoot%\\System32\\mswsock.dll", path ); + + err = WSCUnInstallNameSpace( &array[ i ].NSProviderId ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + + err = WSCInstallNameSpace( name, path, NS_DNS, array[ i ].dwVersion, &array[ i ].NSProviderId ); + err = translate_errno( err == 0, errno_compat(), WSAEINVAL ); + require_noerr( err, exit ); + + // Success! + + fprintf( stderr, "Reordered \"Tcpip\" NSP to to the bottom of the NSP chain\n" ); + err = kNoErr; + +exit: + if( array ) + { + free( array ); + } + if( started ) + { + WSACleanup(); + } + if( err != kNoErr ) + { + fprintf( stderr, "### FAILED (%d) to reorder Name Space Providers\n", err ); + } + return( err ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// CharToWCharString +//=========================================================================================================================== + +DEBUG_LOCAL WCHAR * CharToWCharString( const char *inCharString, WCHAR *outWCharString ) +{ + const char * src; + WCHAR * dst; + char c; + + check( inCharString ); + check( outWCharString ); + + src = inCharString; + dst = outWCharString; + do + { + c = *src++; + *dst++ = (WCHAR) c; + + } while( c != '\0' ); + + return( outWCharString ); +} + +//=========================================================================================================================== +// GUIDtoString +//=========================================================================================================================== + +DEBUG_LOCAL char * GUIDtoString( const GUID *inGUID, char *outString ) +{ + check( inGUID ); + check( outString ); + + sprintf( outString, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + inGUID->Data1, inGUID->Data2, inGUID->Data3, + inGUID->Data4[ 0 ], inGUID->Data4[ 1 ], inGUID->Data4[ 2 ], inGUID->Data4[ 3 ], + inGUID->Data4[ 4 ], inGUID->Data4[ 5 ], inGUID->Data4[ 6 ], inGUID->Data4[ 7 ] ); + return( outString ); +} + +//=========================================================================================================================== +// StringToGUID +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus StringToGUID( const char *inCharString, GUID *outGUID ) +{ + OSStatus err; + int n; + unsigned int v[ 8 ]; + + check( inCharString ); + check( outGUID ); + + n = sscanf( inCharString, "%lX-%hX-%hX-%02X%02X-%02X%02X%02X%02X%02X%02X", + &outGUID->Data1, &outGUID->Data2, &outGUID->Data3, + &v[ 0 ], &v[ 1 ], &v[ 2 ], &v[ 3 ], &v[ 4 ], &v[ 5 ], &v[ 6 ], &v[ 7 ] ); + require_action( n == 11, exit, err = kFormatErr ); + + outGUID->Data4[ 0 ] = (unsigned char) v[ 0 ]; + outGUID->Data4[ 1 ] = (unsigned char) v[ 1 ]; + outGUID->Data4[ 2 ] = (unsigned char) v[ 2 ]; + outGUID->Data4[ 3 ] = (unsigned char) v[ 3 ]; + outGUID->Data4[ 4 ] = (unsigned char) v[ 4 ]; + outGUID->Data4[ 5 ] = (unsigned char) v[ 5 ]; + outGUID->Data4[ 6 ] = (unsigned char) v[ 6 ]; + outGUID->Data4[ 7 ] = (unsigned char) v[ 7 ]; + err = kNoErr; + +exit: + return( err ); +} diff --git a/mDNSWindows/Applications/NSPTool/Tool.mcp b/mDNSWindows/Applications/NSPTool/Tool.mcp new file mode 100644 index 0000000000000000000000000000000000000000..a6dcb6f918cb8f4777fa05e4ded5c27a2b0e51a0 GIT binary patch literal 306146 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 A+yDRo literal 0 HcmV?d00001 diff --git a/mDNSWindows/Applications/SystemService/EventLogMessages.bin b/mDNSWindows/Applications/SystemService/EventLogMessages.bin new file mode 100644 index 0000000000000000000000000000000000000000..e74c67ecef348431f7ae0e388a110a48400daf15 GIT binary patch literal 28 ccmZQ%U|?WmU{C;I0U(V!Z literal 0 HcmV?d00001 diff --git a/mDNSMacOS9/mDNSPrefixClassic.h b/mDNSWindows/Applications/SystemService/Prefix.h similarity index 60% rename from mDNSMacOS9/mDNSPrefixClassic.h rename to mDNSWindows/Applications/SystemService/Prefix.h index d275361..844b96e 100644 --- a/mDNSMacOS9/mDNSPrefixClassic.h +++ b/mDNSWindows/Applications/SystemService/Prefix.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -21,20 +23,26 @@ * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): + +$Log: Prefix.h,v $ +Revision 1.2 2004/04/30 02:40:23 bradley +Define DNS_SD_CLIENT_ENABLED=0 so DNSSD.c can be included without linking the client IPC code. + +Revision 1.1 2004/01/30 02:58:39 bradley +mDNSResponder Windows Service. Provides global Rendezvous support with an IPC interface. -$Log: mDNSPrefixClassic.h,v $ -Revision 1.5 2003/08/12 19:56:24 cheshire -Update to APSL 2.0 +*/ - */ +#ifndef __PREFIX__ +#define __PREFIX__ -// 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 +#if( defined( _DEBUG ) ) + #define DEBUG 1 + #define MDNS_DEBUGMSGS 1 +#else + #define DEBUG 0 +#endif -// 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 DNS_SD_CLIENT_ENABLED 0 -#define __ONLYSYSTEMTASK__ 1 -#define MDNS_DEBUGMSGS 0 +#endif // __PREFIX__ diff --git a/mDNSMacOSX/Applications/DNSServiceRegistration/main.m b/mDNSWindows/Applications/SystemService/Resource.h similarity index 74% rename from mDNSMacOSX/Applications/DNSServiceRegistration/main.m rename to mDNSWindows/Applications/SystemService/Resource.h index c340509..50064bd 100644 --- a/mDNSMacOSX/Applications/DNSServiceRegistration/main.m +++ b/mDNSWindows/Applications/SystemService/Resource.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -21,16 +23,11 @@ * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): + +$Log: Resource.h,v $ +Revision 1.1 2004/01/30 02:58:39 bradley +mDNSResponder Windows Service. Provides global Rendezvous support with an IPC interface. -$Log: main.m,v $ -Revision 1.4 2003/08/12 19:55:07 cheshire -Update to APSL 2.0 - - */ - -#import +*/ -int main(int argc, const char *argv[]) -{ - return NSApplicationMain(argc, argv); -} +#define IDS_SERVICE_DESCRIPTION 101 diff --git a/mDNSWindows/Applications/SystemService/Service.c b/mDNSWindows/Applications/SystemService/Service.c new file mode 100644 index 0000000..d65fa58 --- /dev/null +++ b/mDNSWindows/Applications/SystemService/Service.c @@ -0,0 +1,829 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Service.c,v $ +Revision 1.1 2004/01/30 02:58:39 bradley +mDNSResponder Windows Service. Provides global Rendezvous support with an IPC interface. + +*/ + +#include +#include + +#define _WIN32_WINNT 0x0400 + +#include "CommonServices.h" +#include "DebugServices.h" + +#include "DNSSDDirect.h" +#include "RMxServer.h" + +#include "Resource.h" + +#include "mDNSClientAPI.h" + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[Server] " +#define kServiceName "Rendezvous" +#define kServiceDependencies "Tcpip\0winmgmt\0\0" + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +int main( int argc, char *argv[] ); +static void Usage( void ); +static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ); +static OSStatus InstallService( const char *inName, const char *inDisplayName, const char *inDescription, const char *inPath ); +static OSStatus RemoveService( const char *inName ); +static OSStatus SetServiceDescription( SC_HANDLE inSCM, const char *inServiceName, const char *inDescription ); +static void ReportStatus( int inType, const char *inFormat, ... ); +static OSStatus RunDirect( int argc, char *argv[] ); + +static void WINAPI ServiceMain( DWORD argc, LPSTR argv[] ); +static OSStatus ServiceSetupEventLogging( void ); +static void WINAPI ServiceControlHandler( DWORD inControl ); + +static OSStatus ServiceRun( int argc, char *argv[] ); +static void ServiceStop( void ); + +static OSStatus ServiceSpecificInitialize( int argc, char *argv[] ); +static OSStatus ServiceSpecificRun( int argc, char *argv[] ); +static OSStatus ServiceSpecificStop( void ); +static void ServiceSpecificFinalize( int argc, char *argv[] ); + +#include "mDNSClientAPI.h" + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +DEBUG_LOCAL BOOL gServiceQuietMode = FALSE; +DEBUG_LOCAL SERVICE_TABLE_ENTRY gServiceDispatchTable[] = +{ + { kServiceName, ServiceMain }, + { NULL, NULL } +}; +DEBUG_LOCAL SERVICE_STATUS gServiceStatus; +DEBUG_LOCAL SERVICE_STATUS_HANDLE gServiceStatusHandle = NULL; +DEBUG_LOCAL HANDLE gServiceEventSource = NULL; +DEBUG_LOCAL bool gServiceAllowRemote = false; +DEBUG_LOCAL int gServiceCacheEntryCount = 0; // 0 means to use the DNS-SD default. + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// main +//=========================================================================================================================== + +int main( int argc, char *argv[] ) +{ + OSStatus err; + BOOL ok; + BOOL start; + int i; + + debug_initialize( kDebugOutputTypeMetaConsole ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose ); + + // Install a Console Control Handler to handle things like control-c signals. + + ok = SetConsoleCtrlHandler( ConsoleControlHandler, TRUE ); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + // Default to automatically starting the service dispatcher if no extra arguments are specified. + + start = ( argc <= 1 ); + + // Parse arguments. + + for( i = 1; i < argc; ++i ) + { + if( strcmp( argv[ i ], "-install" ) == 0 ) // Install + { + char desc[ 256 ]; + + desc[ 0 ] = 0; + LoadStringA( GetModuleHandle( NULL ), IDS_SERVICE_DESCRIPTION, desc, sizeof( desc ) ); + err = InstallService( kServiceName, kServiceName, desc, argv[ 0 ] ); + if( err ) + { + ReportStatus( EVENTLOG_ERROR_TYPE, "install service failed (%d)\n", err ); + goto exit; + } + } + else if( strcmp( argv[ i ], "-remove" ) == 0 ) // Remove + { + err = RemoveService( kServiceName ); + if( err ) + { + ReportStatus( EVENTLOG_ERROR_TYPE, "remove service failed (%d)\n", err ); + goto exit; + } + } + else if( strcmp( argv[ i ], "-start" ) == 0 ) // Start + { + start = TRUE; + } + else if( strcmp( argv[ i ], "-server" ) == 0 ) // Server + { + err = RunDirect( argc, argv ); + if( err ) + { + ReportStatus( EVENTLOG_ERROR_TYPE, "run service directly failed (%d)\n", err ); + } + goto exit; + } + else if( strcmp( argv[ i ], "-q" ) == 0 ) // Quiet Mode (toggle) + { + gServiceQuietMode = !gServiceQuietMode; + } + else if( strcmp( argv[ i ], "-remote" ) == 0 ) // Allow Remote Connections + { + gServiceAllowRemote = true; + } + else if( strcmp( argv[ i ], "-cache" ) == 0 ) // Number of mDNS cache entries + { + if( i <= argc ) + { + ReportStatus( EVENTLOG_ERROR_TYPE, "-cache used, but number of cache entries not specified\n" ); + err = kParamErr; + goto exit; + } + gServiceCacheEntryCount = atoi( argv[ ++i ] ); + } + else if( ( strcmp( argv[ i ], "-help" ) == 0 ) || // Help + ( strcmp( argv[ i ], "-h" ) == 0 ) ) + { + Usage(); + err = 0; + break; + } + else + { + Usage(); + err = kParamErr; + break; + } + } + + // Start the service dispatcher if requested. This does not return until all services have terminated. If any + // global initialization is needed, it should be done before starting the service dispatcher, but only if it + // will take less than 30 seconds. Otherwise, use a separate thread for it and start the dispatcher immediately. + + if( start ) + { + ok = StartServiceCtrlDispatcher( gServiceDispatchTable ); + err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr ); + if( err != kNoErr ) + { + ReportStatus( EVENTLOG_ERROR_TYPE, "start service dispatcher failed (%d)\n", err ); + goto exit; + } + } + err = 0; + +exit: + dlog( kDebugLevelTrace, DEBUG_NAME "exited (%d %m)\n", err, err ); + return( (int) err ); +} + +//=========================================================================================================================== +// Usage +//=========================================================================================================================== + +static void Usage( void ) +{ + fprintf( stderr, "\n" ); + fprintf( stderr, "mDNSResponder 1.0d1\n" ); + fprintf( stderr, "\n" ); + fprintf( stderr, " Runs the service normally\n" ); + fprintf( stderr, " -install Creates the service and starts it\n" ); + fprintf( stderr, " -remove Stops the service and deletes it\n" ); + fprintf( stderr, " -start Starts the service dispatcher after processing all other arguments\n" ); + fprintf( stderr, " -server Runs the service directly as a server (for debugging)\n" ); + fprintf( stderr, " -q Toggles Quiet Mode (no events or output)\n" ); + fprintf( stderr, " -remote Allow remote connections\n" ); + fprintf( stderr, " -cache n Number of mDNS cache entries (defaults to %d)\n", kDNSServiceCacheEntryCountDefault ); + fprintf( stderr, " -h[elp] Display Help/Usage\n" ); + fprintf( stderr, "\n" ); +} + +//=========================================================================================================================== +// ConsoleControlHandler +//=========================================================================================================================== + +static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ) +{ + BOOL handled; + OSStatus err; + + handled = FALSE; + switch( inControlEvent ) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + err = ServiceSpecificStop(); + require_noerr( err, exit ); + + handled = TRUE; + break; + + default: + break; + } + +exit: + return( handled ); +} + +//=========================================================================================================================== +// InstallService +//=========================================================================================================================== + +static OSStatus InstallService( const char *inName, const char *inDisplayName, const char *inDescription, const char *inPath ) +{ + OSStatus err; + SC_HANDLE scm; + SC_HANDLE service; + BOOL ok; + TCHAR fullPath[ MAX_PATH ]; + TCHAR * namePtr; + DWORD size; + + scm = NULL; + service = NULL; + + // Get a full path to the executable since a relative path may have been specified. + + size = GetFullPathName( inPath, sizeof( fullPath ), fullPath, &namePtr ); + err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr ); + require_noerr( err, exit ); + + // Create the service and start it. + + scm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); + err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr ); + require_noerr( err, exit ); + + service = CreateService( scm, inName, inDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, inPath, NULL, NULL, kServiceDependencies, + NULL, NULL ); + err = translate_errno( service, (OSStatus) GetLastError(), kDuplicateErr ); + require_noerr( err, exit ); + + if( inDescription ) + { + err = SetServiceDescription( scm, inName, inDescription ); + check_noerr( err ); + } + + ok = StartService( service, 0, NULL ); + err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr ); + require_noerr( err, exit ); + + ReportStatus( EVENTLOG_SUCCESS, "installed service \"%s\"/\"%s\" at \"%s\"\n", inName, inDisplayName, inPath ); + err = kNoErr; + +exit: + if( service ) + { + CloseServiceHandle( service ); + } + if( scm ) + { + CloseServiceHandle( scm ); + } + return( err ); +} + +//=========================================================================================================================== +// RemoveService +//=========================================================================================================================== + +static OSStatus RemoveService( const char *inName ) +{ + OSStatus err; + SC_HANDLE scm; + SC_HANDLE service; + BOOL ok; + SERVICE_STATUS status; + + scm = NULL; + service = NULL; + + // Open a connection to the service. + + scm = OpenSCManager( 0, 0, SC_MANAGER_ALL_ACCESS ); + err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr ); + require_noerr( err, exit ); + + service = OpenService( scm, inName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE ); + err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr ); + require_noerr( err, exit ); + + // Stop the service, if it is not already stopped, then delete it. + + ok = QueryServiceStatus( service, &status ); + err = translate_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr ); + require_noerr( err, exit ); + + if( status.dwCurrentState != SERVICE_STOPPED ) + { + ok = ControlService( service, SERVICE_CONTROL_STOP, &status ); + check_translated_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr ); + } + + ok = DeleteService( service ); + err = translate_errno( ok, (OSStatus) GetLastError(), kDeletedErr ); + require_noerr( err, exit ); + + ReportStatus( EVENTLOG_SUCCESS, "Removed service \"%s\"\n", inName ); + err = ERROR_SUCCESS; + +exit: + if( service ) + { + CloseServiceHandle( service ); + } + if( scm ) + { + CloseServiceHandle( scm ); + } + return( err ); +} + +//=========================================================================================================================== +// SetServiceDescription +//=========================================================================================================================== + +static OSStatus SetServiceDescription( SC_HANDLE inSCM, const char *inServiceName, const char *inDescription ) +{ + OSStatus err; + SC_LOCK lock; + SC_HANDLE service; + SERVICE_DESCRIPTION description; + BOOL ok; + + check( inServiceName ); + check( inDescription ); + + lock = NULL; + service = NULL; + + // Open the database (if not provided) and lock it to prevent other access while re-configuring. + + if( !inSCM ) + { + inSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); + err = translate_errno( inSCM, (OSStatus) GetLastError(), kOpenErr ); + require_noerr( err, exit ); + } + + lock = LockServiceDatabase( inSCM ); + err = translate_errno( lock, (OSStatus) GetLastError(), kInUseErr ); + require_noerr( err, exit ); + + // Open a handle to the service. + + service = OpenService( inSCM, inServiceName, SERVICE_CHANGE_CONFIG ); + err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr ); + require_noerr( err, exit ); + + // Change the description. + + description.lpDescription = (char *) inDescription; + ok = ChangeServiceConfig2( service, SERVICE_CONFIG_DESCRIPTION, &description ); + err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr ); + require_noerr( err, exit ); + + err = ERROR_SUCCESS; + +exit: + // Close the service and release the lock. + + if( service ) + { + CloseServiceHandle( service ); + } + if( lock ) + { + UnlockServiceDatabase( lock ); + } + return( err ); +} + +//=========================================================================================================================== +// ReportStatus +//=========================================================================================================================== + +static void ReportStatus( int inType, const char *inFormat, ... ) +{ + if( !gServiceQuietMode ) + { + va_list args; + + va_start( args, inFormat ); + if( gServiceEventSource ) + { + char s[ 1024 ]; + BOOL ok; + const char * array[ 1 ]; + + vsprintf( s, inFormat, args ); + array[ 0 ] = s; + ok = ReportEvent( gServiceEventSource, (WORD) inType, 0, 0x20000001L, NULL, 1, 0, array, NULL ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + } + else + { + int n; + + n = vfprintf( stderr, inFormat, args ); + check( n >= 0 ); + } + va_end( args ); + } +} + +//=========================================================================================================================== +// RunDirect +//=========================================================================================================================== + +static OSStatus RunDirect( int argc, char *argv[] ) +{ + OSStatus err; + BOOL initialized; + + initialized = FALSE; + + err = ServiceSpecificInitialize( argc, argv ); + require_noerr( err, exit ); + initialized = TRUE; + + // Run the service. This does not return until the service quits or is stopped. + + ReportStatus( EVENTLOG_SUCCESS, "Running \"%s\" service directly\n", kServiceName ); + + err = ServiceSpecificRun( argc, argv ); + require_noerr( err, exit ); + + // Clean up. + +exit: + if( initialized ) + { + ServiceSpecificFinalize( argc, argv ); + } + return( err ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// ServiceMain +//=========================================================================================================================== + +static void WINAPI ServiceMain( DWORD argc, LPSTR argv[] ) +{ + OSStatus err; + BOOL ok; + char desc[ 256 ]; + + err = ServiceSetupEventLogging(); + require_noerr( err, exit ); + + // Initialize the service status and register the service control handler with the name of the service. + + gServiceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; + gServiceStatus.dwCurrentState = 0; + gServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + gServiceStatus.dwWin32ExitCode = NO_ERROR; + gServiceStatus.dwServiceSpecificExitCode = NO_ERROR; + gServiceStatus.dwCheckPoint = 0; + gServiceStatus.dwWaitHint = 0; + + gServiceStatusHandle = RegisterServiceCtrlHandler( argv[ 0 ], ServiceControlHandler ); + err = translate_errno( gServiceStatusHandle, (OSStatus) GetLastError(), kInUseErr ); + require_noerr( err, exit ); + + // Setup the description. This should be done by the installer, but it doesn't support that yet. + + desc[ 0 ] = '\0'; + LoadStringA( GetModuleHandle( NULL ), IDS_SERVICE_DESCRIPTION, desc, sizeof( desc ) ); + err = SetServiceDescription( NULL, kServiceName, desc ); + check_noerr( err ); + + // Mark the service as starting. + + gServiceStatus.dwCurrentState = SERVICE_START_PENDING; + gServiceStatus.dwCheckPoint = 0; + gServiceStatus.dwWaitHint = 5000; // 5 seconds + ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + check_translated_errno( ok, GetLastError(), kParamErr ); + + // Run the service. This does not return until the service quits or is stopped. + + err = ServiceRun( (int) argc, argv ); + if( err != kNoErr ) + { + gServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + gServiceStatus.dwServiceSpecificExitCode = (DWORD) err; + } + + // Service-specific work is done so mark the service as stopped. + + gServiceStatus.dwCurrentState = SERVICE_STOPPED; + ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + check_translated_errno( ok, GetLastError(), kParamErr ); + + // Note: The service status handle should not be closed according to Microsoft documentation. + +exit: + if( gServiceEventSource ) + { + ok = DeregisterEventSource( gServiceEventSource ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + gServiceEventSource = NULL; + } +} + +//=========================================================================================================================== +// ServiceSetupEventLogging +//=========================================================================================================================== + +static OSStatus ServiceSetupEventLogging( void ) +{ + OSStatus err; + HKEY key; + const char * s; + DWORD typesSupported; + char path[ MAX_PATH ]; + DWORD n; + + key = NULL; + + // Add/Open source name as a sub-key under the Application key in the EventLog registry key. + + s = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\" kServiceName; + err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key ); + require_noerr( err, exit ); + + // Add the name to the EventMessageFile subkey. + + path[ 0 ] = '\0'; + GetModuleFileName( NULL, path, MAX_PATH ); + n = (DWORD)( strlen( path ) + 1 ); + err = RegSetValueEx( key, "EventMessageFile", 0, REG_EXPAND_SZ, (const LPBYTE) path, n ); + require_noerr( err, exit ); + + // Set the supported event types in the TypesSupported subkey. + + typesSupported = 0 + | EVENTLOG_SUCCESS + | EVENTLOG_ERROR_TYPE + | EVENTLOG_WARNING_TYPE + | EVENTLOG_INFORMATION_TYPE + | EVENTLOG_AUDIT_SUCCESS + | EVENTLOG_AUDIT_FAILURE; + err = RegSetValueEx( key, "TypesSupported", 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) ); + require_noerr( err, exit ); + + // Set up the event source. + + gServiceEventSource = RegisterEventSource( NULL, kServiceName ); + err = translate_errno( gServiceEventSource, (OSStatus) GetLastError(), kParamErr ); + require_noerr( err, exit ); + +exit: + if( key ) + { + RegCloseKey( key ); + } + return( err ); +} + +//=========================================================================================================================== +// ServiceControlHandler +//=========================================================================================================================== + +static void WINAPI ServiceControlHandler( DWORD inControl ) +{ + BOOL setStatus; + BOOL ok; + + setStatus = TRUE; + switch( inControl ) + { + case SERVICE_CONTROL_STOP: + dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: SERVICE_CONTROL_STOP\n" ); + + ServiceStop(); + setStatus = FALSE; + break; + + default: + dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: event (0x%08X)\n", inControl ); + break; + } + + if( setStatus && gServiceStatusHandle ) + { + ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + } +} + +//=========================================================================================================================== +// ServiceRun +//=========================================================================================================================== + +static OSStatus ServiceRun( int argc, char *argv[] ) +{ + OSStatus err; + BOOL initialized; + BOOL ok; + + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + initialized = FALSE; + + // Initialize the service-specific stuff and mark the service as running. + + err = ServiceSpecificInitialize( argc, argv ); + require_noerr( err, exit ); + initialized = TRUE; + + gServiceStatus.dwCurrentState = SERVICE_RUNNING; + ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + check_translated_errno( ok, GetLastError(), kParamErr ); + + // Run the service-specific stuff. This does not return until the service quits or is stopped. + + ReportStatus( EVENTLOG_INFORMATION_TYPE, "mDNSResponder started\n" ); + err = ServiceSpecificRun( argc, argv ); + ReportStatus( EVENTLOG_INFORMATION_TYPE, "mDNSResponder stopped (%d)\n", err ); + require_noerr( err, exit ); + + // Service stopped. Clean up and we're done. + +exit: + if( initialized ) + { + ServiceSpecificFinalize( argc, argv ); + } + return( err ); +} + +//=========================================================================================================================== +// ServiceStop +//=========================================================================================================================== + +static void ServiceStop( void ) +{ + BOOL ok; + OSStatus err; + + // Signal the event to cause the service to exit. + + if( gServiceStatusHandle ) + { + gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); + check_translated_errno( ok, GetLastError(), kParamErr ); + } + + err = ServiceSpecificStop(); + check_noerr( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Service Specific == +#endif + +//=========================================================================================================================== +// ServiceSpecificInitialize +//=========================================================================================================================== + +static OSStatus ServiceSpecificInitialize( int argc, char *argv[] ) +{ + OSStatus err; + DNSServiceInitializeFlags initFlags; + RMxServerFlags flags; + + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + initFlags = kDNSServiceInitializeFlagsAdvertise | kDNSServiceInitializeFlagsNoServerCheck; + err = DNSServiceInitialize_direct( initFlags, gServiceCacheEntryCount ); + require_noerr( err, exit ); + + flags = 0; + if( gServiceAllowRemote ) + { + flags |= kRMxServerFlagsAllowRemote; + } + err = RMxServerInitialize( flags ); + require_noerr( err, exit ); + +exit: + if( err != kNoErr ) + { + ServiceSpecificFinalize( argc, argv ); + } + return( err ); +} + +//=========================================================================================================================== +// ServiceSpecificRun +//=========================================================================================================================== + +static OSStatus ServiceSpecificRun( int argc, char *argv[] ) +{ + OSStatus err; + + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + err = RMxServerRun(); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// ServiceSpecificStop +//=========================================================================================================================== + +static OSStatus ServiceSpecificStop( void ) +{ + OSStatus err; + + err = RMxServerStop( kRMxServerStopFlagsNoWait ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// ServiceSpecificFinalize +//=========================================================================================================================== + +static void ServiceSpecificFinalize( int argc, char *argv[] ) +{ + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + RMxServerFinalize(); + DNSServiceFinalize_direct(); +} diff --git a/mDNSWindows/Applications/SystemService/Service.mcp b/mDNSWindows/Applications/SystemService/Service.mcp new file mode 100644 index 0000000000000000000000000000000000000000..0347957cafe95e43f4aebadde6e2a867f60be7dc GIT binary patch literal 299890 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

wQnxDKE>_jGEM8pVh_o*h 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/main.m b/mDNSWindows/Applications/SystemServiceTest/Prefix.h similarity index 66% rename from mDNSMacOSX/Applications/DNSServiceBrowser/main.m rename to mDNSWindows/Applications/SystemServiceTest/Prefix.h index c340509..68e705b 100644 --- a/mDNSMacOSX/Applications/DNSServiceBrowser/main.m +++ b/mDNSWindows/Applications/SystemServiceTest/Prefix.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -22,15 +24,23 @@ Change History (most recent first): -$Log: main.m,v $ -Revision 1.4 2003/08/12 19:55:07 cheshire -Update to APSL 2.0 +$Log: Prefix.h,v $ +Revision 1.1 2004/01/30 02:58:57 bradley +Test tool for the mDNSResponder Windows service. + +*/ + +#ifndef __PREFIX__ +#define __PREFIX__ - */ +#if( defined( _DEBUG ) ) + #define DEBUG 1 + #define MDNS_DEBUGMSGS 1 +#else + #define DEBUG 0 +#endif -#import +#define DNS_SD_DIRECT_ENABLED 0 +#define DNS_SD_CLIENT_ENABLED 1 -int main(int argc, const char *argv[]) -{ - return NSApplicationMain(argc, argv); -} +#endif // __PREFIX__ diff --git a/mDNSWindows/Applications/SystemServiceTest/Tool.c b/mDNSWindows/Applications/SystemServiceTest/Tool.c new file mode 100644 index 0000000..5ab16d9 --- /dev/null +++ b/mDNSWindows/Applications/SystemServiceTest/Tool.c @@ -0,0 +1,847 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: Tool.c,v $ +Revision 1.3 2004/04/09 21:03:15 bradley +Changed port numbers to use network byte order for consistency with other platforms. + +Revision 1.2 2004/04/08 09:43:43 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.1 2004/01/30 02:58:57 bradley +Test tool for the mDNSResponder Windows service. + +*/ + +#include +#include + +#include "CommonServices.h" +#include "DebugServices.h" +#include "DNSSD.h" + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +#define MAX_DOMAIN_LABEL 63 +#define MAX_DOMAIN_NAME 255 + +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; + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +int main( int argc, char* argv[] ); +static void Usage( void ); +static int ProcessArgs( int argc, char* argv[] ); + +#if( defined( WINVER ) ) + static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ); +#endif + +static void CALLBACK_COMPAT + EnumerateDomainsCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inDomain, + void * inContext ); + +static void CALLBACK_COMPAT + BrowseCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + +static void CALLBACK_COMPAT + ResolveCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ); + +static void CALLBACK_COMPAT + RegisterCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + +static void CALLBACK_COMPAT + RecordCallBack( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + void * inContext ); + +static void CALLBACK_COMPAT + QueryCallBack( + const DNSServiceRef inRef, + const DNSServiceFlags inFlags, + const uint32_t inInterfaceIndex, + const DNSServiceErrorType inErrorCode, + const char * inName, + const uint16_t inRRType, + const uint16_t inRRClass, + const uint16_t inRDataSize, + const void * inRData, + const uint32_t inTTL, + void * inContext ); + +static void PrintRData( uint16_t inRRType, size_t inRDataSize, const uint8_t *inRData ); + +static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc); +static char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc); + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +#if( defined( WINVER ) ) + static volatile int gQuit = 0; +#endif + +//=========================================================================================================================== +// main +//=========================================================================================================================== + +int main( int argc, char *argv[] ) +{ + OSStatus err; + + debug_initialize( kDebugOutputTypeMetaConsole ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelTrace ); + + SetConsoleCtrlHandler( ConsoleControlHandler, TRUE ); + err = ProcessArgs( argc, argv ); + return( (int) err ); +} + +//=========================================================================================================================== +// Usage +//=========================================================================================================================== + +static void Usage( void ) +{ + fprintf( stderr, "\n" ); + fprintf( stderr, "Rendezvous Service Test 1.0d1\n" ); + fprintf( stderr, "\n" ); + fprintf( stderr, " -server Set Remote Server\n" ); + fprintf( stderr, " -cv Check Version\n" ); + fprintf( stderr, " -bd Browse for Browse Domains\n" ); + fprintf( stderr, " -bs Browse for Services\n" ); + fprintf( stderr, " -rsi Resolve Service Instance\n" ); + fprintf( stderr, " -rs Register Service\n" ); + fprintf( stderr, " -rr Register Records\n" ); + fprintf( stderr, " -qr Query Record\n" ); + fprintf( stderr, " -cr Reconfirm Record\n" ); + fprintf( stderr, " -cp Copy Property\n" ); + fprintf( stderr, " -h[elp] Help\n" ); + fprintf( stderr, "\n" ); +} + +DEBUG_LOCAL DNSServiceRef gRef = NULL; +DEBUG_LOCAL DNSRecordRef gRecordRef = NULL; +DEBUG_LOCAL const char * gServer = NULL; + +//=========================================================================================================================== +// ProcessArgs +//=========================================================================================================================== + +static int ProcessArgs( int argc, char* argv[] ) +{ + OSStatus err; + int i; + const char * name; + const char * type; + const char * domain; + uint16_t port; + const char * host; + const char * txt; + uint16_t txtSize; + uint8_t txtStorage[ 256 ]; + uint32_t ipv4; + char s[ 256 ]; + DNSRecordRef records[ 10 ]; + char fullName[ kDNSServiceMaxDomainName ]; + uint16_t rrType; + + err = DNSServiceInitialize( kDNSServiceInitializeFlagsNoServerCheck, 0 ); + require_noerr( err, exit ); + + // Parse the command line arguments (ignore first argument since it's just the program name). + + if( argc <= 1 ) + { + Usage(); + err = 0; + goto exit; + } + for( i = 1; i < argc; ++i ) + { + if( strcmp( argv[ i ], "-server" ) == 0 ) + { + require_action( argc > ( i + 1 ), exit, err = kParamErr ); + gServer = argv[ ++i ]; + + printf( "Server set to \"%s\"\n", gServer ); + } + else if( strcmp( argv[ i ], "-cv" ) == 0 ) + { + // Check Version + + err = DNSServiceCheckVersion(); + printf( "CheckVersion: %ld\n", err ); + err = kNoErr; + goto exit; + } + else if( strcmp( argv[ i ], "-bd" ) == 0 ) + { + err = DNSServiceEnumerateDomains( &gRef, kDNSServiceFlagsBrowseDomains, 0, + EnumerateDomainsCallBack, NULL ); + require_noerr( err, exit ); + } + else if( strcmp( argv[ i ], "-bs" ) == 0 ) + { + // Browse service + + if( argc > ( i + 2 ) ) + { + type = argv[ ++i ]; + domain = argv[ ++i ]; + } + else + { + type = "_http._tcp"; + domain = ""; + } + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + + err = DNSServiceBrowse( &gRef, 0, 0, type, domain, BrowseCallBack, NULL ); + require_noerr( err, exit ); + } + else if( strcmp( argv[ i ], "-rsi" ) == 0 ) + { + // Resolve Service Instance + + if( argc > ( i + 3 ) ) + { + name = argv[ ++i ]; + type = argv[ ++i ]; + domain = argv[ ++i ]; + } + else + { + name = "test service"; + type = "_http._tcp"; + domain = ""; + } + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + + err = DNSServiceResolve( &gRef, 0, 0, name, type, domain, ResolveCallBack, NULL ); + require_noerr( err, exit ); + } + else if( strcmp( argv[ i ], "-rs" ) == 0 ) + { + // Register Service + + if( argc > ( i + 6 ) ) + { + name = argv[ ++i ]; + type = argv[ ++i ]; + domain = argv[ ++i ]; + host = argv[ ++i ]; + port = (uint16_t) atoi( argv[ ++i ] ); + txt = argv[ ++i ]; + } + else + { + name = "test service"; + type = "_http._tcp"; + domain = ""; + host = ""; + port = 80; + txt = "My TXT Record"; + } + if( *txt != '\0' ) + { + txtStorage[ 0 ] = (uint8_t) strlen( txt ); + memcpy( &txtStorage[ 1 ], txt, txtStorage[ 0 ] ); + txtSize = (uint16_t)( 1 + txtStorage[ 0 ] ); + txt = (const char *) txtStorage; + } + else + { + txt = NULL; + txtSize = 0; + } + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + + err = DNSServiceRegister( &gRef, 0, 0, name, type, domain, host, htons( port ), txtSize, txt, + RegisterCallBack, NULL ); + require_noerr( err, exit ); + + #if( TEST_SERVICE_RECORDS ) + ipv4 = 0x11223344; + err = DNSServiceAddRecord( gRef, &gRecordRef, 0, kDNSServiceDNSType_A, kDNSServiceDNSClass_IN, &ipv4, 60 ); + require_noerr( err, exit ); + + Sleep( 10000 ); + + ipv4 = 0x22334455; + err = DNSServiceUpdateRecord( gRef, gRecordRef, 0, 4, &ipv4, 60 ); + require_noerr( err, exit ); + + Sleep( 10000 ); + + err = DNSServiceRemoveRecord( gRef, gRecordRef, 0 ); + require_noerr( err, exit ); + gRecordRef = NULL; + + Sleep( 10000 ); + #endif + } + else if( strcmp( argv[ i ], "-rr" ) == 0 ) + { + // Register Records + + err = DNSServiceCreateConnection( &gRef ); + require_noerr( err, exit ); + + printf( "registering 10 address records...\n" ); + ipv4 = 0x11223310; + for( i = 0; i < 10; ++i ) + { + sprintf( s, "testhost-%d.local.", i ); + ++ipv4; + err = DNSServiceRegisterRecord( gRef, &records[ i ], kDNSServiceFlagsUnique, 0, s, + kDNSServiceDNSType_A, kDNSServiceDNSClass_IN, 4, &ipv4, 60, RecordCallBack, NULL ); + check_noerr( err ); + } + Sleep( 10000 ); + + printf( "deregistering half of the records\n" ); + for( i = 0; i < 10; ++i ) + { + if( i % 2 ) + { + err = DNSServiceRemoveRecord( gRef, records[ i ], 0 ); + check_noerr( err ); + records[ i ] = NULL; + } + } + Sleep( 10000 ); + + printf( "updating the remaining records\n" ); + for( i = 0; i < 10; ++i ) + { + if( records[ i ] ) + { + ++ipv4; + err = DNSServiceUpdateRecord( gRef, records[ i ], 0, 4, &ipv4, 60 ); + check_noerr( err ); + } + } + Sleep( 10000 ); + + printf( "deregistering all remaining records\n" ); + DNSServiceRefDeallocate( gRef ); + + Sleep( 5000 ); + } + else if( strcmp( argv[ i ], "-qr" ) == 0 ) + { + // Query Record + + if( argc > ( i + 4 ) ) + { + name = argv[ ++i ]; + type = argv[ ++i ]; + domain = argv[ ++i ]; + rrType = (uint16_t) atoi( argv[ ++i ] ); + } + else + { + name = "test"; + type = ""; + domain = ""; + rrType = 1; // Address + } + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + err = DNSServiceConstructFullName( fullName, name, type, domain ); + require_noerr( err, exit ); + + printf( "resolving fullname %s type %d\n", fullName, rrType ); + err = DNSServiceQueryRecord( &gRef, 0, 0, fullName, rrType, kDNSServiceDNSClass_IN, QueryCallBack, NULL ); + require_noerr( err, exit ); + } + else if( strcmp( argv[ i ], "-cr" ) == 0 ) + { + // Reconfirm Record + + if( argc > ( i + 4 ) ) + { + name = argv[ ++i ]; + type = argv[ ++i ]; + domain = argv[ ++i ]; + rrType = (uint16_t) atoi( argv[ ++i ] ); + } + else + { + name = "test"; + type = ""; + domain = ""; + rrType = 1; // Address + } + if( ( domain[ 0 ] == '\0' ) || ( ( domain[ 0 ] == '.' ) && ( domain[ 1 ] == '\0' ) ) ) + { + domain = "local."; + } + err = DNSServiceConstructFullName( fullName, name, type, domain ); + require_noerr( err, exit ); + + printf( "reconfirming record fullname %s type %d\n", fullName, rrType ); + ipv4 = 0x11223310; + DNSServiceReconfirmRecord( 0, 0, fullName, rrType, kDNSServiceDNSClass_IN, 4, &ipv4 ); + } + else if( strcmp( argv[ i ], "-cp" ) == 0 ) + { + DNSPropertyCode code; + DNSPropertyData data; + + // Copy Property + + if( argc > ( i + 1 ) ) + { + name = argv[ ++i ]; + require_action( strlen( name ) == 4, exit, err = kParamErr ); + + code = (DNSPropertyCode)( name[ 0 ] << 24 ); + code |= (DNSPropertyCode)( name[ 1 ] << 16 ); + code |= (DNSPropertyCode)( name[ 2 ] << 8 ); + code |= (DNSPropertyCode) name[ 3 ]; + } + else + { + code = kDNSPropertyCodeVersion; + name = "vers"; + } + + err = DNSServiceCopyProperty( code, &data ); + require_noerr( err, exit ); + + printf( "'%s' property:\n", name ); + if( code == kDNSPropertyCodeVersion ) + { + printf( " clientCurrentVersion: 0x%08X\n", data.u.version.clientCurrentVersion ); + printf( " clientOldestServerVersion: 0x%08X\n", data.u.version.clientOldestServerVersion ); + printf( " serverCurrentVersion: 0x%08X\n", data.u.version.serverCurrentVersion ); + printf( " serverOldestClientVersion: 0x%08X\n", data.u.version.serverOldestClientVersion ); + } + } + else if( ( strcmp( argv[ i ], "-help" ) == 0 ) || ( strcmp( argv[ i ], "-h" ) == 0 ) ) + { + // Help + + Usage(); + err = 0; + goto exit; + } + else + { + // Unknown parameter. + + dlog( kDebugLevelError, "unknown parameter (%s)\n", argv[ i ] ); + err = kParamErr; + goto exit; + } + } + + // Run until control-C'd. + + while( !gQuit ) + { + Sleep( 100 ); + } + err = kNoErr; + +exit: + DNSServiceFinalize(); + if( err ) + { + Usage(); + } + return( err ); +} + +//=========================================================================================================================== +// 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 ); +} + +//=========================================================================================================================== +// EnumerateDomainsCallBack +//=========================================================================================================================== + +static void CALLBACK_COMPAT + EnumerateDomainsCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inDomain, + void * inContext ) +{ + printf( "inRef: 0x%08X\n", (uintptr_t) inRef ); + printf( "inFlags: 0x%08X\n", (int) inFlags ); + printf( "inInterfaceIndex: 0x%08X\n", (int) inInterfaceIndex ); + printf( "inErrorCode: %ld\n", inErrorCode ); + printf( "inDomain: \"%s\"\n", inDomain ? inDomain : "" ); + printf( "inContext: 0x%08X\n", (uintptr_t) inContext ); + printf( "\n" ); +} + +//=========================================================================================================================== +// BrowseCallBack +//=========================================================================================================================== + +static void CALLBACK_COMPAT + BrowseCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + printf( "inRef: 0x%08X\n", (uintptr_t) inRef ); + printf( "inFlags: 0x%08X\n", (int) inFlags ); + printf( "inInterfaceIndex: 0x%08X\n", (int) inInterfaceIndex ); + printf( "inErrorCode: %ld\n", inErrorCode ); + printf( "inName: \"%s\"\n", inName ? inName : "" ); + printf( "inType: \"%s\"\n", inType ? inType : "" ); + printf( "inDomain: \"%s\"\n", inDomain ? inDomain : "" ); + printf( "inContext: 0x%08X\n", (uintptr_t) inContext ); + printf( "\n" ); +} + +//=========================================================================================================================== +// ResolveCallBack +//=========================================================================================================================== + +static void CALLBACK_COMPAT + ResolveCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ) +{ + printf( "inRef: 0x%08X\n", (uintptr_t) inRef ); + printf( "inFlags: 0x%08X\n", (int) inFlags ); + printf( "inInterfaceIndex: 0x%08X\n", (int) inInterfaceIndex ); + printf( "inErrorCode: %ld\n", inErrorCode ); + printf( "inFullName: \"%s\"\n", inFullName ? inFullName : "" ); + printf( "inHostName: \"%s\"\n", inHostName ? inHostName : "" ); + printf( "inPort: %d\n", ntohs( inPort ) ); + printf( "inTXTSize: %ld\n", inTXTSize ); + printf( "inTXT: 0x%08X\n", (uintptr_t) inTXT ); + printf( "inContext: 0x%08X\n", (uintptr_t) inContext ); + printf( "\n" ); +} + +//=========================================================================================================================== +// RegisterCallBack +//=========================================================================================================================== + +static void CALLBACK_COMPAT + RegisterCallBack( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + printf( "inRef: 0x%08X\n", (uintptr_t) inRef ); + printf( "inFlags: 0x%08X\n", (int) inFlags ); + printf( "inErrorCode: %ld\n", inErrorCode ); + printf( "inName: \"%s\"\n", inName ? inName : "" ); + printf( "inType: \"%s\"\n", inType ? inType : "" ); + printf( "inDomain: \"%s\"\n", inDomain ? inDomain : "" ); + printf( "inContext: 0x%08X\n", (uintptr_t) inContext ); + printf( "\n" ); +} + +//=========================================================================================================================== +// RecordCallBack +//=========================================================================================================================== + +static void CALLBACK_COMPAT + RecordCallBack( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + void * inContext ) +{ + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( inRecordRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inContext ); + + if( inErrorCode == kDNSServiceErr_NoError ) + { + printf( "RecordCallBack: no errors\n" ); + } + else + { + printf( "RecordCallBack: %ld error\n", inErrorCode ); + } +} + +//=========================================================================================================================== +// QueryCallBack +//=========================================================================================================================== + +static void CALLBACK_COMPAT + QueryCallBack( + const DNSServiceRef inRef, + const DNSServiceFlags inFlags, + const uint32_t inInterfaceIndex, + const DNSServiceErrorType inErrorCode, + const char * inName, + const uint16_t inRRType, + const uint16_t inRRClass, + const uint16_t inRDataSize, + const void * inRData, + const uint32_t inTTL, + void * inContext ) +{ + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( inRRClass ); + DEBUG_UNUSED( inTTL ); + DEBUG_UNUSED( inContext ); + + if( inErrorCode == kDNSServiceErr_NoError ) + { + if( inFlags & kDNSServiceFlagsAdd ) + { + printf( "Add" ); + } + else + { + printf( "Rmv" ); + } + if( inFlags & kDNSServiceFlagsMoreComing ) + { + printf( "+" ); + } + else + { + printf( " " ); + } + printf(" 0x%04X %d %s rdata ", inFlags, inInterfaceIndex, inName ); + PrintRData( inRRType, (size_t) inRDataSize, (const uint8_t *) inRData ); + } + else + { + printf( "QueryCallback: %ld error\n", inErrorCode ); + } +} + +//=========================================================================================================================== +// PrintRData +//=========================================================================================================================== + +static void PrintRData( uint16_t inRRType, size_t inRDataSize, const uint8_t *inRData ) +{ + size_t i; + srv_rdata * srv; + char s[ 1005 ]; + struct in_addr in; + + switch( inRRType ) + { + case kDNSServiceDNSType_TXT: + + // Print all the alphanumeric and punctuation characters + + for( i = 0; i < inRDataSize; ++i ) + { + if( ( inRData[ i ] >= 32 ) && ( inRData[ i ] <= 127 ) ) + { + printf( "%c", inRData[ i ] ); + } + } + printf( "\n" ); + break; + + case kDNSServiceDNSType_SRV: + srv = (srv_rdata *)inRData; + ConvertDomainNameToCString_withescape(&srv->target, s, 0); + printf("pri=%d, w=%d, port=%d, target=%s\n", srv->priority, srv->weight, srv->port, s); + break; + + case kDNSServiceDNSType_A: + check( inRDataSize == 4 ); + memcpy( &in, inRData, sizeof( in ) ); + printf( "%s\n", inet_ntoa( in ) ); + break; + + case kDNSServiceDNSType_PTR: + ConvertDomainNameToCString_withescape( (domainname *) inRData, s, 0 ); + break; + + case kDNSServiceDNSType_AAAA: + check( inRDataSize == 16 ); + printf( "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X\n", + inRData[0], inRData[1], inRData[2], inRData[3], inRData[4], inRData[5], inRData[6], inRData[7], inRData[8], + inRData[9], inRData[10], inRData[11], inRData[12], inRData[13], inRData[14], inRData[15] ); + break; + + default: + printf( "ERROR: I dont know how to print inRData of type %d\n", inRRType ); + return; + } +} + +static char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) + { + const unsigned char * src = label->c; // Domain label we're reading + const unsigned char len = *src++; // Read length of this (non-null) label + const unsigned char *const end = src + len; // Work out where the label ends + if (len > 63) return(NULL); // If illegal label, abort + while (src < end) // While we have characters in the label + { + unsigned 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 = (unsigned 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 unsigned char *src = name->c; // Domain name we're reading + const unsigned 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 + } diff --git a/mDNSWindows/Applications/SystemServiceTest/Tool.mcp b/mDNSWindows/Applications/SystemServiceTest/Tool.mcp new file mode 100644 index 0000000000000000000000000000000000000000..59200136c9ec33e43aa7d23997e28360bd5da598 GIT binary patch literal 398728 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/Applications/mdnsNSP/NSP.c b/mDNSWindows/Applications/mdnsNSP/NSP.c new file mode 100644 index 0000000..faf459d --- /dev/null +++ b/mDNSWindows/Applications/mdnsNSP/NSP.c @@ -0,0 +1,1367 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: NSP.c,v $ +Revision 1.2 2004/04/08 09:43:43 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.1 2004/01/30 03:00:33 bradley +Rendezvous NameSpace Provider (NSP). Hooks into the Windows name resolution system to resolve +Rendezvous name lookups using Multicast DNS so .local names work in all Windows apps. + +*/ + +#include +#include +#include + +#include "CommonServices.h" +#include "DebugServices.h" + +#include +#include + +#include "DNSSD.h" + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +typedef struct Query * QueryRef; +typedef struct Query Query; +struct Query +{ + QueryRef next; + int refCount; + DWORD querySetFlags; + WSAQUERYSETW * querySet; + size_t querySetSize; + HANDLE dataEvent; + HANDLE cancelEvent; + HANDLE waitHandles[ 2 ]; + DWORD waitCount; + DNSServiceRef resolver; + char name[ kDNSServiceMaxDomainName ]; + size_t nameSize; + uint32_t addr; + bool addrValid; +}; + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// DLL Exports + +BOOL WINAPI DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ); + +// NSP SPIs + +int WSPAPI NSPCleanup( LPGUID inProviderID ); + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceBegin( + LPGUID inProviderID, + LPWSAQUERYSETW inQuerySet, + LPWSASERVICECLASSINFOW inServiceClassInfo, + DWORD inFlags, + LPHANDLE outLookup ); + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceNext( + HANDLE inLookup, + DWORD inFlags, + LPDWORD ioBufferLength, + LPWSAQUERYSETW outResults ); + +DEBUG_LOCAL int WSPAPI NSPLookupServiceEnd( HANDLE inLookup ); + +DEBUG_LOCAL int WSPAPI + NSPSetService( + LPGUID inProviderID, + LPWSASERVICECLASSINFOW inServiceClassInfo, + LPWSAQUERYSETW inRegInfo, + WSAESETSERVICEOP inOperation, + DWORD inFlags ); + +DEBUG_LOCAL int WSPAPI NSPInstallServiceClass( LPGUID inProviderID, LPWSASERVICECLASSINFOW inServiceClassInfo ); +DEBUG_LOCAL int WSPAPI NSPRemoveServiceClass( LPGUID inProviderID, LPGUID inServiceClassID ); +DEBUG_LOCAL int WSPAPI NSPGetServiceClassInfo( LPGUID inProviderID, LPDWORD ioBufSize, LPWSASERVICECLASSINFOW ioServiceClassInfo ); + +// Private + +#define NSPLock() EnterCriticalSection( &gLock ); +#define NSPUnlock() LeaveCriticalSection( &gLock ); + +DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags, QueryRef *outRef ); +DEBUG_LOCAL OSStatus QueryRetain( QueryRef inRef ); +DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ); + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ); + +DEBUG_LOCAL OSStatus + QueryCopyQuerySet( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW ** outQuerySet, + size_t * outSize ); + +DEBUG_LOCAL void + QueryCopyQuerySetTo( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW * outQuerySet ); + +DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags ); + +#if( DEBUG ) + void DebugDumpQuerySet( DebugLevel inLevel, const WSAQUERYSETW *inQuerySet ); + + #define dlog_query_set( LEVEL, SET ) DebugDumpQuerySet( LEVEL, SET ) +#else + #define dlog_query_set( LEVEL, SET ) +#endif + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +// {B600E6E9-553B-4a19-8696-335E5C896153} +// GUID kRendezvousNSPGUID = { 0xb600e6e9, 0x553b, 0x4a19, { 0x86, 0x96, 0x33, 0x5e, 0x5c, 0x89, 0x61, 0x53 } }; + +DEBUG_LOCAL LONG gRefCount = 0; +DEBUG_LOCAL CRITICAL_SECTION gLock; +DEBUG_LOCAL bool gLockInitialized = false; +DEBUG_LOCAL bool gDNSSDInitialized = false; +DEBUG_LOCAL QueryRef gQueryList = NULL; + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// DllMain +//=========================================================================================================================== + +BOOL APIENTRY DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) +{ + DEBUG_USE_ONLY( inInstance ); + DEBUG_UNUSED( inReserved ); + + switch( inReason ) + { + case DLL_PROCESS_ATTACH: + debug_initialize( kDebugOutputTypeWindowsEventLog, "Rendezvous NSP", inInstance ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelInfo ); + dlog( kDebugLevelTrace, "\n" ); + dlog( kDebugLevelVerbose, "%s: process attach\n", __ROUTINE__ ); + break; + + case DLL_PROCESS_DETACH: + dlog( kDebugLevelVerbose, "%s: process detach\n", __ROUTINE__ ); + break; + + case DLL_THREAD_ATTACH: + dlog( kDebugLevelVerbose, "%s: thread attach\n", __ROUTINE__ ); + break; + + case DLL_THREAD_DETACH: + dlog( kDebugLevelVerbose, "%s: thread detach\n", __ROUTINE__ ); + break; + + default: + dlog( kDebugLevelNotice, "%s: unknown reason code (%d)\n", __ROUTINE__, inReason ); + break; + } + return( TRUE ); +} + +//=========================================================================================================================== +// NSPStartup +// +// This function is called when our namespace DLL is loaded. It sets up the NSP functions we implement and initializes us. +//=========================================================================================================================== + +int WSPAPI NSPStartup( LPGUID inProviderID, LPNSP_ROUTINE outRoutines ) +{ + OSStatus err; + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s (GUID=%U, refCount=%ld)\n", __ROUTINE__, inProviderID, gRefCount ); + + // Only initialize if this is the first time NSPStartup is called. + + if( InterlockedIncrement( &gRefCount ) != 1 ) + { + err = NO_ERROR; + goto exit; + } + + // Initialize our internal state. + + InitializeCriticalSection( &gLock ); + gLockInitialized = true; + + // Set the size to exclude NSPIoctl because we don't implement it. + + outRoutines->cbSize = FIELD_OFFSET( NSP_ROUTINE, NSPIoctl ); + outRoutines->dwMajorVersion = 4; + outRoutines->dwMinorVersion = 4; + outRoutines->NSPCleanup = NSPCleanup; + outRoutines->NSPLookupServiceBegin = NSPLookupServiceBegin; + outRoutines->NSPLookupServiceNext = NSPLookupServiceNext; + outRoutines->NSPLookupServiceEnd = NSPLookupServiceEnd; + outRoutines->NSPSetService = NSPSetService; + outRoutines->NSPInstallServiceClass = NSPInstallServiceClass; + outRoutines->NSPRemoveServiceClass = NSPRemoveServiceClass; + outRoutines->NSPGetServiceClassInfo = NSPGetServiceClassInfo; + + err = NO_ERROR; + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + NSPCleanup( inProviderID ); + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPCleanup +// +// This function is called when our namespace DLL is unloaded. It cleans up anything we set up in NSPStartup. +//=========================================================================================================================== + +int WSPAPI NSPCleanup( LPGUID inProviderID ) +{ + DEBUG_USE_ONLY( inProviderID ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s (GUID=%U, refCount=%ld)\n", __ROUTINE__, inProviderID, gRefCount ); + + // Only initialize if this is the first time NSPStartup is called. + + if( InterlockedDecrement( &gRefCount ) != 0 ) + { + goto exit; + } + + // Stop any outstanding queries. + + if( gLockInitialized ) + { + NSPLock(); + } + while( gQueryList ) + { + check_string( gQueryList->refCount == 1, "NSPCleanup with outstanding queries!" ); + QueryRelease( gQueryList ); + } + if( gLockInitialized ) + { + NSPUnlock(); + } + + // Shut down DNS-SD and release our resources. + + if( gDNSSDInitialized ) + { + gDNSSDInitialized = false; + DNSServiceFinalize(); + } + if( gLockInitialized ) + { + gLockInitialized = false; + DeleteCriticalSection( &gLock ); + } + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPLookupServiceBegin +// +// This function maps to the WinSock WSALookupServiceBegin function. It starts the lookup process and returns a HANDLE +// that can be used in subsequent operations. Subsequent calls only need to refer to this query by the handle as +// opposed to specifying the query parameters each time. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceBegin( + LPGUID inProviderID, + LPWSAQUERYSETW inQuerySet, + LPWSASERVICECLASSINFOW inServiceClassInfo, + DWORD inFlags, + LPHANDLE outLookup ) +{ + OSStatus err; + QueryRef obj; + LPCWSTR name; + size_t size; + LPCWSTR p; + DWORD type; + DWORD n; + DWORD i; + INT family; + INT protocol; + + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassInfo ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + + obj = NULL; + require_action( inQuerySet, exit, err = WSAEINVAL ); + name = inQuerySet->lpszServiceInstanceName; + require_action_quiet( name, exit, err = WSAEINVAL ); + require_action( outLookup, exit, err = WSAEINVAL ); + + dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=\"%S\")\n", __ROUTINE__, inFlags, name ); + dlog_query_set( kDebugLevelVerbose, inQuerySet ); + + // Check if we can handle this type of request and if we support any of the protocols being requested. + // We only support the DNS namespace, TCP and UDP protocols, and IPv4. Only blob results are supported. + + require_action_quiet( inFlags & LUP_RETURN_BLOB, exit, err = WSASERVICE_NOT_FOUND ); + + type = inQuerySet->dwNameSpace; + require_action_quiet( ( type == NS_DNS ) || ( type == NS_ALL ), exit, err = WSASERVICE_NOT_FOUND ); + + n = inQuerySet->dwNumberOfProtocols; + if( n > 0 ) + { + require_action( inQuerySet->lpafpProtocols, exit, err = WSAEINVAL ); + for( i = 0; i < n; ++i ) + { + family = inQuerySet->lpafpProtocols[ i ].iAddressFamily; + protocol = inQuerySet->lpafpProtocols[ i ].iProtocol; + if( ( family == AF_INET ) && ( ( protocol == IPPROTO_UDP ) || ( protocol == IPPROTO_TCP ) ) ) + { + break; + } + } + require_action_quiet( i < n, exit, err = WSASERVICE_NOT_FOUND ); + } + + // Check if the name ends in ".local" and if not, exit with an error since we only resolve .local names. + // The name may or may not end with a "." (fully qualified) so handle both cases. DNS is also case + // insensitive the check for .local has to be case insensitive (.LoCaL is equivalent to .local). This + // manually does the wchar_t strlen and stricmp to avoid needing any special wchar_t versions of the + // libraries. It is probably faster to do the inline compare than invoke functions to do it anyway. + + for( p = name; *p; ++p ) {} // Find end of string + size = (size_t)( p - name ); + require_action_quiet( size > sizeof_string( ".local" ), exit, err = WSASERVICE_NOT_FOUND ); + + p = name + ( size - 1 ); + p = ( *p == '.' ) ? ( p - sizeof_string( ".local" ) ) : ( ( p - sizeof_string( ".local" ) ) + 1 ); + require_action_quiet( ( ( p[ 0 ] == '.' ) && + ( ( p[ 1 ] == 'L' ) || ( p[ 1 ] == 'l' ) ) && + ( ( p[ 2 ] == 'O' ) || ( p[ 2 ] == 'o' ) ) && + ( ( p[ 3 ] == 'C' ) || ( p[ 3 ] == 'c' ) ) && + ( ( p[ 4 ] == 'A' ) || ( p[ 4 ] == 'a' ) ) && + ( ( p[ 5 ] == 'L' ) || ( p[ 5 ] == 'l' ) ) ), + exit, err = WSASERVICE_NOT_FOUND ); + + // The name ends in .local so start the resolve operation. Lazy initialize DNS-SD if needed. + + NSPLock(); + if( !gDNSSDInitialized ) + { + err = DNSServiceInitialize( kDNSServiceInitializeFlagsNoServerCheck, 0 ); + require_noerr( err, exit ); + gDNSSDInitialized = true; + } + + err = QueryCreate( inQuerySet, inFlags, &obj ); + NSPUnlock(); + require_noerr( err, exit ); + + *outLookup = (HANDLE) obj; + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPLookupServiceNext +// +// This function maps to the Winsock call WSALookupServiceNext. This routine takes a handle to a previously defined +// query and attempts to locate a service matching the criteria defined by the query. If so, that instance is returned +// in the lpqsResults parameter. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceNext( + HANDLE inLookup, + DWORD inFlags, + LPDWORD ioSize, + LPWSAQUERYSETW outResults ) +{ + OSStatus err; + QueryRef obj; + DWORD waitResult; + size_t size; + + DEBUG_USE_ONLY( inFlags ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + + obj = NULL; + NSPLock(); + err = QueryRetain( (QueryRef) inLookup ); + require_noerr( err, exit ); + obj = (QueryRef) inLookup; + require_action( ioSize, exit, err = WSAEINVAL ); + require_action( outResults, exit, err = WSAEINVAL ); + + dlog( kDebugLevelTrace, "%s (lookup=%#p, flags=0x%08X, *ioSize=%d)\n", __ROUTINE__, inLookup, inFlags, *ioSize ); + + // Wait for data or a cancel. Release the lock while waiting. This is safe because we've retained the query. + + NSPUnlock(); + waitResult = WaitForMultipleObjects( obj->waitCount, obj->waitHandles, FALSE, 5 * 1000 ); + NSPLock(); + require_action_quiet( waitResult != ( WAIT_OBJECT_0 + 1 ), exit, err = WSA_E_CANCELLED ); + err = translate_errno( waitResult == WAIT_OBJECT_0, (OSStatus) GetLastError(), WSASERVICE_NOT_FOUND ); + require_noerr_quiet( err, exit ); + require_action_quiet( obj->addrValid, exit, err = WSA_E_NO_MORE ); + + // Copy the externalized query results to the callers buffer (if it fits). + + size = QueryCopyQuerySetSize( obj, obj->querySet, obj->querySetFlags ); + require_action( size <= (size_t) *ioSize, exit, err = WSAEFAULT ); + + QueryCopyQuerySetTo( obj, obj->querySet, obj->querySetFlags, outResults ); + outResults->dwOutputFlags = RESULT_IS_ADDED; + obj->addrValid = false; + +exit: + if( obj ) + { + QueryRelease( obj ); + } + NSPUnlock(); + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPLookupServiceEnd +// +// This function maps to the Winsock call WSALookupServiceEnd. Once the user process has finished is query (usually +// indicated when WSALookupServiceNext returns the error WSA_E_NO_MORE) a call to this function is made to release any +// allocated resources associated with the query. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPLookupServiceEnd( HANDLE inLookup ) +{ + OSStatus err; + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + + dlog( kDebugLevelTrace, "%s (lookup=%#p)\n", __ROUTINE__, inLookup ); + + NSPLock(); + err = QueryRelease( (QueryRef) inLookup ); + NSPUnlock(); + require_noerr( err, exit ); + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPSetService +// +// This function maps to the Winsock call WSASetService. This routine is called when the user wants to register or +// deregister an instance of a server with our service. For registration, the user needs to associate the server with a +// service class. For deregistration the service class is required along with the servicename. The inRegInfo parameter +// contains a WSAQUERYSET structure defining the server (such as protocol and address where it is). +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI + NSPSetService( + LPGUID inProviderID, + LPWSASERVICECLASSINFOW inServiceClassInfo, + LPWSAQUERYSETW inRegInfo, + WSAESETSERVICEOP inOperation, + DWORD inFlags ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassInfo ); + DEBUG_UNUSED( inRegInfo ); + DEBUG_UNUSED( inOperation ); + DEBUG_UNUSED( inFlags ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow services to be registered so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSAEINVAL ); +} + +//=========================================================================================================================== +// NSPInstallServiceClass +// +// This function maps to the Winsock call WSAInstallServiceClass. This routine is used to install a service class which +// is used to define certain characteristics for a group of services. After a service class is registered, an actual +// instance of a server may be registered. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPInstallServiceClass( LPGUID inProviderID, LPWSASERVICECLASSINFOW inServiceClassInfo ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassInfo ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow service classes to be installed so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSA_INVALID_PARAMETER ); +} + +//=========================================================================================================================== +// NSPRemoveServiceClass +// +// This function maps to the Winsock call WSARemoveServiceClass. This routine removes a previously registered service +// class. This is accomplished by connecting to the namespace service and writing the GUID which defines the given +// service class. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPRemoveServiceClass( LPGUID inProviderID, LPGUID inServiceClassID ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassID ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow service classes to be installed so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSATYPE_NOT_FOUND ); +} + +//=========================================================================================================================== +// NSPGetServiceClassInfo +// +// This function maps to the Winsock call WSAGetServiceClassInfo. This routine returns the information associated with +// a given service class. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPGetServiceClassInfo( LPGUID inProviderID, LPDWORD ioSize, LPWSASERVICECLASSINFOW ioServiceClassInfo ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( ioSize ); + DEBUG_UNUSED( ioServiceClassInfo ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow service classes to be installed so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSATYPE_NOT_FOUND ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// QueryCreate +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags, QueryRef *outRef ) +{ + OSStatus err; + QueryRef obj; + char name[ kDNSServiceMaxDomainName ]; + int n; + QueryRef * p; + + obj = NULL; + check( inQuerySet ); + check( inQuerySet->lpszServiceInstanceName ); + check( outRef ); + + // Convert the wchar_t name to UTF-8. + + n = WideCharToMultiByte( CP_UTF8, 0, inQuerySet->lpszServiceInstanceName, -1, name, sizeof( name ), NULL, NULL ); + err = translate_errno( n > 0, (OSStatus) GetLastError(), WSAEINVAL ); + require_noerr( err, exit ); + + // Allocate the object and append it to the list. Append immediately so releases of partial objects work. + + obj = (QueryRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + obj->refCount = 1; + + for( p = &gQueryList; *p; p = &( *p )->next ) {} // Find the end of the list. + *p = obj; + + // Set up events to signal when data is ready and when cancelling. + + obj->dataEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( obj->dataEvent, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + obj->cancelEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( obj->cancelEvent, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + obj->waitCount = 0; + obj->waitHandles[ obj->waitCount++ ] = obj->dataEvent; + obj->waitHandles[ obj->waitCount++ ] = obj->cancelEvent; + check( obj->waitCount == sizeof_array( obj->waitHandles ) ); + + // Copy the QuerySet so it can be returned later. + + obj->querySetFlags = inQuerySetFlags; + inQuerySetFlags = ( inQuerySetFlags & ~( LUP_RETURN_ADDR | LUP_RETURN_BLOB ) ) | LUP_RETURN_NAME; + err = QueryCopyQuerySet( obj, inQuerySet, inQuerySetFlags, &obj->querySet, &obj->querySetSize ); + require_noerr( err, exit ); + + // Start the query. + + err = DNSServiceQueryRecord( &obj->resolver, 0, 0, name, kDNSServiceDNSType_A, kDNSServiceDNSClass_IN, + QueryRecordCallback, obj ); + require_noerr( err, exit ); + + // Success! + + *outRef = obj; + obj = NULL; + err = NO_ERROR; + +exit: + if( obj ) + { + QueryRelease( obj ); + } + return( err ); +} + +//=========================================================================================================================== +// QueryRetain +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus QueryRetain( QueryRef inRef ) +{ + OSStatus err; + QueryRef obj; + + for( obj = gQueryList; obj; obj = obj->next ) + { + if( obj == inRef ) + { + break; + } + } + require_action( obj, exit, err = WSA_INVALID_HANDLE ); + + ++inRef->refCount; + err = NO_ERROR; + +exit: + return( err ); +} + +//=========================================================================================================================== +// QueryRelease +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ) +{ + OSStatus err; + QueryRef * p; + BOOL ok; + + // Find the item in the list. + + for( p = &gQueryList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + require_action( *p, exit, err = WSA_INVALID_HANDLE ); + + // Signal a cancel to unblock any threads waiting for results. + + if( inRef->cancelEvent ) + { + ok = SetEvent( inRef->cancelEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + + // Stop the query. + + if( inRef->resolver ) + { + DNSServiceRefDeallocate( inRef->resolver ); + inRef->resolver = NULL; + } + + // Decrement the refCount. Fully release if it drops to 0. If still referenced, just exit. + + if( --inRef->refCount != 0 ) + { + err = NO_ERROR; + goto exit; + } + *p = inRef->next; + + // Release resources. + + if( inRef->cancelEvent ) + { + ok = CloseHandle( inRef->cancelEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + if( inRef->dataEvent ) + { + ok = CloseHandle( inRef->dataEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + if( inRef->querySet ) + { + free( inRef->querySet ); + } + free( inRef ); + err = NO_ERROR; + +exit: + return( err ); +} + +//=========================================================================================================================== +// QueryRecordCallback +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ) +{ + QueryRef obj; + const char * src; + char * dst; + BOOL ok; + + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inTTL ); + + NSPLock(); + obj = (QueryRef) inContext; + check( obj ); + require_noerr( inErrorCode, exit ); + require_quiet( inFlags & kDNSServiceFlagsAdd, exit ); + require( inRRClass == kDNSServiceDNSClass_IN, exit ); + require( inRRType == kDNSServiceDNSType_A, exit ); + require( inRDataSize == 4, exit ); + + dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=%s, rrType=%d, rDataSize=%d)\n", + __ROUTINE__, inFlags, inName, inRRType, inRDataSize ); + + // Copy the name if needed. + + if( obj->name[ 0 ] == '\0' ) + { + src = inName; + dst = obj->name; + while( *src != '\0' ) + { + *dst++ = *src++; + } + *dst = '\0'; + obj->nameSize = (size_t)( dst - obj->name ); + check( obj->nameSize < sizeof( obj->name ) ); + } + + // Copy the data. + + memcpy( &obj->addr, inRData, inRDataSize ); + obj->addrValid = true; + + // Signal that a result is ready. + + check( obj->dataEvent ); + ok = SetEvent( obj->dataEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + + // Stop the resolver after the first response. + + DNSServiceRefDeallocate( inRef ); + obj->resolver = NULL; + +exit: + NSPUnlock(); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// QueryCopyQuerySet +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus + QueryCopyQuerySet( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW ** outQuerySet, + size_t * outSize ) +{ + OSStatus err; + size_t size; + WSAQUERYSETW * qs; + + check( inQuerySet ); + check( outQuerySet ); + + size = QueryCopyQuerySetSize( inRef, inQuerySet, inQuerySetFlags ); + qs = (WSAQUERYSETW *) calloc( 1, size ); + require_action( qs, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + QueryCopyQuerySetTo( inRef, inQuerySet, inQuerySetFlags, qs ); + + *outQuerySet = qs; + if( outSize ) + { + *outSize = size; + } + qs = NULL; + err = NO_ERROR; + +exit: + if( qs ) + { + free( qs ); + } + return( err ); +} + +//=========================================================================================================================== +// QueryCopyQuerySetTo +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL void + QueryCopyQuerySetTo( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW * outQuerySet ) +{ + uint8_t * dst; + LPCWSTR s; + LPWSTR q; + DWORD n; + DWORD i; + +#if( DEBUG ) + size_t debugSize; + + debugSize = QueryCopyQuerySetSize( inRef, inQuerySet, inQuerySetFlags ); +#endif + + check( inQuerySet ); + check( outQuerySet ); + + dst = (uint8_t *) outQuerySet; + + // Copy the static portion of the results. + + *outQuerySet = *inQuerySet; + dst += sizeof( *inQuerySet ); + + if( inQuerySetFlags & LUP_RETURN_NAME ) + { + s = inQuerySet->lpszServiceInstanceName; + if( s ) + { + outQuerySet->lpszServiceInstanceName = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + } + else + { + outQuerySet->lpszServiceInstanceName = NULL; + } + + if( inQuerySet->lpServiceClassId ) + { + outQuerySet->lpServiceClassId = (LPGUID) dst; + *outQuerySet->lpServiceClassId = *inQuerySet->lpServiceClassId; + dst += sizeof( *inQuerySet->lpServiceClassId ); + } + + if( inQuerySet->lpVersion ) + { + outQuerySet->lpVersion = (LPWSAVERSION) dst; + *outQuerySet->lpVersion = *inQuerySet->lpVersion; + dst += sizeof( *inQuerySet->lpVersion ); + } + + s = inQuerySet->lpszComment; + if( s ) + { + outQuerySet->lpszComment = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + + if( inQuerySet->lpNSProviderId ) + { + outQuerySet->lpNSProviderId = (LPGUID) dst; + *outQuerySet->lpNSProviderId = *inQuerySet->lpNSProviderId; + dst += sizeof( *inQuerySet->lpNSProviderId ); + } + + s = inQuerySet->lpszContext; + if( s ) + { + outQuerySet->lpszContext = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + + n = inQuerySet->dwNumberOfProtocols; + if( n > 0 ) + { + check( inQuerySet->lpafpProtocols ); + + outQuerySet->lpafpProtocols = (LPAFPROTOCOLS) dst; + for( i = 0; i < n; ++i ) + { + outQuerySet->lpafpProtocols[ i ] = inQuerySet->lpafpProtocols[ i ]; + dst += sizeof( *inQuerySet->lpafpProtocols ); + } + } + + s = inQuerySet->lpszQueryString; + if( s ) + { + outQuerySet->lpszQueryString = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + + // Copy the address(es). + + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addrValid ) + { + struct sockaddr_in * addr; + + outQuerySet->dwNumberOfCsAddrs = 1; + outQuerySet->lpcsaBuffer = (LPCSADDR_INFO) dst; + dst += sizeof( *outQuerySet->lpcsaBuffer ); + + outQuerySet->lpcsaBuffer[ 0 ].LocalAddr.lpSockaddr = NULL; + outQuerySet->lpcsaBuffer[ 0 ].LocalAddr.iSockaddrLength = 0; + + outQuerySet->lpcsaBuffer[ 0 ].RemoteAddr.lpSockaddr = (LPSOCKADDR) dst; + outQuerySet->lpcsaBuffer[ 0 ].RemoteAddr.iSockaddrLength = sizeof( struct sockaddr_in ); + + addr = (struct sockaddr_in *) dst; + memset( addr, 0, sizeof( *addr ) ); + addr->sin_family = AF_INET; + memcpy( &addr->sin_addr, &inRef->addr, 4 ); + dst += sizeof( *addr ); + + outQuerySet->lpcsaBuffer[ 0 ].iSocketType = AF_INET; // Emulate Tcpip NSP + outQuerySet->lpcsaBuffer[ 0 ].iProtocol = IPPROTO_UDP; // Emulate Tcpip NSP + } + else + { + outQuerySet->dwNumberOfCsAddrs = 0; + outQuerySet->lpcsaBuffer = NULL; + } + + // Copy the hostent blob. + + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addrValid ) + { + uint8_t * base; + struct hostent * he; + uintptr_t * p; + + outQuerySet->lpBlob = (LPBLOB) dst; + dst += sizeof( *outQuerySet->lpBlob ); + + base = dst; + he = (struct hostent *) dst; + dst += sizeof( *he ); + + he->h_name = (char *)( dst - base ); + memcpy( dst, inRef->name, inRef->nameSize + 1 ); + dst += ( inRef->nameSize + 1 ); + + he->h_aliases = (char **)( dst - base ); + p = (uintptr_t *) dst; + *p++ = 0; + dst = (uint8_t *) p; + + he->h_addrtype = AF_INET; + he->h_length = 4; + + he->h_addr_list = (char **)( dst - base ); + p = (uintptr_t *) dst; + dst += ( 2 * sizeof( *p ) ); + *p++ = (uintptr_t)( dst - base ); + *p++ = 0; + p = (uintptr_t *) dst; + *p++ = (uintptr_t) inRef->addr; + dst = (uint8_t *) p; + + outQuerySet->lpBlob->cbSize = (ULONG)( dst - base ); + outQuerySet->lpBlob->pBlobData = (BYTE *) base; + } + dlog_query_set( kDebugLevelVerbose, outQuerySet ); + + check( (size_t)( dst - ( (uint8_t *) outQuerySet ) ) == debugSize ); +} + +//=========================================================================================================================== +// QueryCopyQuerySetSize +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags ) +{ + size_t size; + LPCWSTR s; + LPCWSTR p; + + check( inRef ); + check( inQuerySet ); + + // Calculate the size of the static portion of the results. + + size = sizeof( *inQuerySet ); + + if( inQuerySetFlags & LUP_RETURN_NAME ) + { + s = inQuerySet->lpszServiceInstanceName; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + } + + if( inQuerySet->lpServiceClassId ) + { + size += sizeof( *inQuerySet->lpServiceClassId ); + } + + if( inQuerySet->lpVersion ) + { + size += sizeof( *inQuerySet->lpVersion ); + } + + s = inQuerySet->lpszComment; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + + if( inQuerySet->lpNSProviderId ) + { + size += sizeof( *inQuerySet->lpNSProviderId ); + } + + s = inQuerySet->lpszContext; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + + size += ( inQuerySet->dwNumberOfProtocols * sizeof( *inQuerySet->lpafpProtocols ) ); + + s = inQuerySet->lpszQueryString; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + + // Calculate the size of the address(es). + + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addrValid ) + { + size += sizeof( *inQuerySet->lpcsaBuffer ); + size += sizeof( struct sockaddr_in ); + } + + // Calculate the size of the hostent blob. + + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addrValid ) + { + size += sizeof( *inQuerySet->lpBlob ); // Blob ptr/size structure + size += sizeof( struct hostent ); // Old-style hostent structure + size += ( inRef->nameSize + 1 ); // Name and null terminator + size += 4; // Alias list terminator (0 offset) + size += 4; // Offset to address. + size += 4; // Address list terminator (0 offset) + size += 4; // IPv4 address + } + return( size ); +} + +#if 0 +#pragma mark - +#endif + +#if( DEBUG ) +//=========================================================================================================================== +// DebugDumpQuerySet +//=========================================================================================================================== + +#define DebugSocketFamilyToString( FAM ) ( ( FAM ) == AF_INET ) ? "AF_INET" : \ + ( ( FAM ) == AF_INET6 ) ? "AF_INET6" : "" + +#define DebugSocketProtocolToString( PROTO ) ( ( PROTO ) == IPPROTO_UDP ) ? "IPPROTO_UDP" : \ + ( ( PROTO ) == IPPROTO_TCP ) ? "IPPROTO_TCP" : "" + +#define DebugNameSpaceToString( NS ) ( ( NS ) == NS_DNS ) ? "NS_DNS" : ( ( NS ) == NS_ALL ) ? "NS_ALL" : "" + +void DebugDumpQuerySet( DebugLevel inLevel, const WSAQUERYSETW *inQuerySet ) +{ + DWORD i; + + check( inQuerySet ); + + // Fixed portion of the QuerySet. + + dlog( inLevel, "QuerySet:\n" ); + dlog( inLevel, " dwSize: %d (expected %d)\n", inQuerySet->dwSize, sizeof( *inQuerySet ) ); + if( inQuerySet->lpszServiceInstanceName ) + { + dlog( inLevel, " lpszServiceInstanceName: %S\n", inQuerySet->lpszServiceInstanceName ); + } + else + { + dlog( inLevel, " lpszServiceInstanceName: \n" ); + } + if( inQuerySet->lpServiceClassId ) + { + dlog( inLevel, " lpServiceClassId: %U\n", inQuerySet->lpServiceClassId ); + } + else + { + dlog( inLevel, " lpServiceClassId: \n" ); + } + if( inQuerySet->lpVersion ) + { + dlog( inLevel, " lpVersion:\n" ); + dlog( inLevel, " dwVersion: %d\n", inQuerySet->lpVersion->dwVersion ); + dlog( inLevel, " dwVersion: %d\n", inQuerySet->lpVersion->ecHow ); + } + else + { + dlog( inLevel, " lpVersion: \n" ); + } + if( inQuerySet->lpszComment ) + { + dlog( inLevel, " lpszComment: %S\n", inQuerySet->lpszComment ); + } + else + { + dlog( inLevel, " lpszComment: \n" ); + } + dlog( inLevel, " dwNameSpace: %d %s\n", inQuerySet->dwNameSpace, + DebugNameSpaceToString( inQuerySet->dwNameSpace ) ); + if( inQuerySet->lpNSProviderId ) + { + dlog( inLevel, " lpNSProviderId: %U\n", inQuerySet->lpNSProviderId ); + } + else + { + dlog( inLevel, " lpNSProviderId: \n" ); + } + if( inQuerySet->lpszContext ) + { + dlog( inLevel, " lpszContext: %S\n", inQuerySet->lpszContext ); + } + else + { + dlog( inLevel, " lpszContext: \n" ); + } + dlog( inLevel, " dwNumberOfProtocols: %d\n", inQuerySet->dwNumberOfProtocols ); + dlog( inLevel, " lpafpProtocols: %s\n", inQuerySet->lpafpProtocols ? "" : "" ); + for( i = 0; i < inQuerySet->dwNumberOfProtocols; ++i ) + { + if( i != 0 ) + { + dlog( inLevel, "\n" ); + } + dlog( inLevel, " iAddressFamily: %d %s\n", inQuerySet->lpafpProtocols[ i ].iAddressFamily, + DebugSocketFamilyToString( inQuerySet->lpafpProtocols[ i ].iAddressFamily ) ); + dlog( inLevel, " iProtocol: %d %s\n", inQuerySet->lpafpProtocols[ i ].iProtocol, + DebugSocketProtocolToString( inQuerySet->lpafpProtocols[ i ].iProtocol ) ); + } + if( inQuerySet->lpszQueryString ) + { + dlog( inLevel, " lpszQueryString: %S\n", inQuerySet->lpszQueryString ); + } + else + { + dlog( inLevel, " lpszQueryString: \n" ); + } + dlog( inLevel, " dwNumberOfCsAddrs: %d\n", inQuerySet->dwNumberOfCsAddrs ); + dlog( inLevel, " lpcsaBuffer: %s\n", inQuerySet->lpcsaBuffer ? "" : "" ); + for( i = 0; i < inQuerySet->dwNumberOfCsAddrs; ++i ) + { + if( i != 0 ) + { + dlog( inLevel, "\n" ); + } + if( inQuerySet->lpcsaBuffer[ i ].LocalAddr.lpSockaddr && + ( inQuerySet->lpcsaBuffer[ i ].LocalAddr.iSockaddrLength > 0 ) ) + { + dlog( inLevel, " LocalAddr: %##a\n", + inQuerySet->lpcsaBuffer[ i ].LocalAddr.lpSockaddr ); + } + else + { + dlog( inLevel, " LocalAddr: \n" ); + } + if( inQuerySet->lpcsaBuffer[ i ].RemoteAddr.lpSockaddr && + ( inQuerySet->lpcsaBuffer[ i ].RemoteAddr.iSockaddrLength > 0 ) ) + { + dlog( inLevel, " RemoteAddr: %##a\n", + inQuerySet->lpcsaBuffer[ i ].RemoteAddr.lpSockaddr ); + } + else + { + dlog( inLevel, " RemoteAddr: \n" ); + } + dlog( inLevel, " iSocketType: %d\n", inQuerySet->lpcsaBuffer[ i ].iSocketType ); + dlog( inLevel, " iProtocol: %d\n", inQuerySet->lpcsaBuffer[ i ].iProtocol ); + } + dlog( inLevel, " dwOutputFlags: %d\n", inQuerySet->dwOutputFlags ); + + // Blob portion of the QuerySet. + + if( inQuerySet->lpBlob ) + { + dlog( inLevel, " lpBlob:\n" ); + dlog( inLevel, " cbSize: %ld\n", inQuerySet->lpBlob->cbSize ); + dlog( inLevel, " pBlobData: %#p\n", inQuerySet->lpBlob->pBlobData ); + dloghex( inLevel, 12, NULL, 0, 0, NULL, 0, + inQuerySet->lpBlob->pBlobData, inQuerySet->lpBlob->pBlobData, inQuerySet->lpBlob->cbSize, + kDebugFlagsNone, NULL, 0 ); + } + else + { + dlog( inLevel, " lpBlob: \n" ); + } +} +#endif diff --git a/mDNSWindows/Applications/mdnsNSP/NSP.def b/mDNSWindows/Applications/mdnsNSP/NSP.def new file mode 100644 index 0000000..3428a50 --- /dev/null +++ b/mDNSWindows/Applications/mdnsNSP/NSP.def @@ -0,0 +1,36 @@ +; +; Copyright (c) 2003-2004 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: NSP.def,v $ +; Revision 1.1 2004/01/30 03:00:33 bradley +; Rendezvous NameSpace Provider (NSP). Hooks into the Windows name resolution system to resolve +; Rendezvous name lookups using Multicast DNS so .local names work in all Windows apps. +; +; + +LIBRARY RendezvousNSP + +EXPORTS + NSPStartup + NSPCleanup diff --git a/mDNSWindows/Applications/mdnsNSP/NSP.mcp b/mDNSWindows/Applications/mdnsNSP/NSP.mcp new file mode 100644 index 0000000000000000000000000000000000000000..96007ece3f0de0b3628107a5859a41336156938b GIT binary patch literal 198118 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" is the actual parent path of the DLL): + +NSPTool -install "RendezvousNSP" "B600E6E9-553B-4a19-8696-335E5C896153" "" + +You can remove remove the Rendezvous NSP with the following line: + +NSPTool -remove "B600E6E9-553B-4a19-8696-335E5C896153" + +For more information, check out the following URL: + + diff --git a/mDNSWindows/Applications/mdnsNSP/mdnsNSP.c b/mDNSWindows/Applications/mdnsNSP/mdnsNSP.c new file mode 100644 index 0000000..71fa70e --- /dev/null +++ b/mDNSWindows/Applications/mdnsNSP/mdnsNSP.c @@ -0,0 +1,1367 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: mdnsNSP.c,v $ +Revision 1.2 2004/04/08 09:43:43 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.1 2004/01/30 03:00:33 bradley +Rendezvous NameSpace Provider (NSP). Hooks into the Windows name resolution system to resolve +Rendezvous name lookups using Multicast DNS so .local names work in all Windows apps. + +*/ + +#include +#include +#include + +#include "CommonServices.h" +#include "DebugServices.h" + +#include +#include + +#include "DNSSD.h" + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +typedef struct Query * QueryRef; +typedef struct Query Query; +struct Query +{ + QueryRef next; + int refCount; + DWORD querySetFlags; + WSAQUERYSETW * querySet; + size_t querySetSize; + HANDLE dataEvent; + HANDLE cancelEvent; + HANDLE waitHandles[ 2 ]; + DWORD waitCount; + DNSServiceRef resolver; + char name[ kDNSServiceMaxDomainName ]; + size_t nameSize; + uint32_t addr; + bool addrValid; +}; + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// DLL Exports + +BOOL WINAPI DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ); + +// NSP SPIs + +int WSPAPI NSPCleanup( LPGUID inProviderID ); + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceBegin( + LPGUID inProviderID, + LPWSAQUERYSETW inQuerySet, + LPWSASERVICECLASSINFOW inServiceClassInfo, + DWORD inFlags, + LPHANDLE outLookup ); + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceNext( + HANDLE inLookup, + DWORD inFlags, + LPDWORD ioBufferLength, + LPWSAQUERYSETW outResults ); + +DEBUG_LOCAL int WSPAPI NSPLookupServiceEnd( HANDLE inLookup ); + +DEBUG_LOCAL int WSPAPI + NSPSetService( + LPGUID inProviderID, + LPWSASERVICECLASSINFOW inServiceClassInfo, + LPWSAQUERYSETW inRegInfo, + WSAESETSERVICEOP inOperation, + DWORD inFlags ); + +DEBUG_LOCAL int WSPAPI NSPInstallServiceClass( LPGUID inProviderID, LPWSASERVICECLASSINFOW inServiceClassInfo ); +DEBUG_LOCAL int WSPAPI NSPRemoveServiceClass( LPGUID inProviderID, LPGUID inServiceClassID ); +DEBUG_LOCAL int WSPAPI NSPGetServiceClassInfo( LPGUID inProviderID, LPDWORD ioBufSize, LPWSASERVICECLASSINFOW ioServiceClassInfo ); + +// Private + +#define NSPLock() EnterCriticalSection( &gLock ); +#define NSPUnlock() LeaveCriticalSection( &gLock ); + +DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags, QueryRef *outRef ); +DEBUG_LOCAL OSStatus QueryRetain( QueryRef inRef ); +DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ); + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ); + +DEBUG_LOCAL OSStatus + QueryCopyQuerySet( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW ** outQuerySet, + size_t * outSize ); + +DEBUG_LOCAL void + QueryCopyQuerySetTo( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW * outQuerySet ); + +DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags ); + +#if( DEBUG ) + void DebugDumpQuerySet( DebugLevel inLevel, const WSAQUERYSETW *inQuerySet ); + + #define dlog_query_set( LEVEL, SET ) DebugDumpQuerySet( LEVEL, SET ) +#else + #define dlog_query_set( LEVEL, SET ) +#endif + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +// {B600E6E9-553B-4a19-8696-335E5C896153} +// GUID kRendezvousNSPGUID = { 0xb600e6e9, 0x553b, 0x4a19, { 0x86, 0x96, 0x33, 0x5e, 0x5c, 0x89, 0x61, 0x53 } }; + +DEBUG_LOCAL LONG gRefCount = 0; +DEBUG_LOCAL CRITICAL_SECTION gLock; +DEBUG_LOCAL bool gLockInitialized = false; +DEBUG_LOCAL bool gDNSSDInitialized = false; +DEBUG_LOCAL QueryRef gQueryList = NULL; + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// DllMain +//=========================================================================================================================== + +BOOL APIENTRY DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) +{ + DEBUG_USE_ONLY( inInstance ); + DEBUG_UNUSED( inReserved ); + + switch( inReason ) + { + case DLL_PROCESS_ATTACH: + debug_initialize( kDebugOutputTypeWindowsEventLog, "Rendezvous NSP", inInstance ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelInfo ); + dlog( kDebugLevelTrace, "\n" ); + dlog( kDebugLevelVerbose, "%s: process attach\n", __ROUTINE__ ); + break; + + case DLL_PROCESS_DETACH: + dlog( kDebugLevelVerbose, "%s: process detach\n", __ROUTINE__ ); + break; + + case DLL_THREAD_ATTACH: + dlog( kDebugLevelVerbose, "%s: thread attach\n", __ROUTINE__ ); + break; + + case DLL_THREAD_DETACH: + dlog( kDebugLevelVerbose, "%s: thread detach\n", __ROUTINE__ ); + break; + + default: + dlog( kDebugLevelNotice, "%s: unknown reason code (%d)\n", __ROUTINE__, inReason ); + break; + } + return( TRUE ); +} + +//=========================================================================================================================== +// NSPStartup +// +// This function is called when our namespace DLL is loaded. It sets up the NSP functions we implement and initializes us. +//=========================================================================================================================== + +int WSPAPI NSPStartup( LPGUID inProviderID, LPNSP_ROUTINE outRoutines ) +{ + OSStatus err; + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s (GUID=%U, refCount=%ld)\n", __ROUTINE__, inProviderID, gRefCount ); + + // Only initialize if this is the first time NSPStartup is called. + + if( InterlockedIncrement( &gRefCount ) != 1 ) + { + err = NO_ERROR; + goto exit; + } + + // Initialize our internal state. + + InitializeCriticalSection( &gLock ); + gLockInitialized = true; + + // Set the size to exclude NSPIoctl because we don't implement it. + + outRoutines->cbSize = FIELD_OFFSET( NSP_ROUTINE, NSPIoctl ); + outRoutines->dwMajorVersion = 4; + outRoutines->dwMinorVersion = 4; + outRoutines->NSPCleanup = NSPCleanup; + outRoutines->NSPLookupServiceBegin = NSPLookupServiceBegin; + outRoutines->NSPLookupServiceNext = NSPLookupServiceNext; + outRoutines->NSPLookupServiceEnd = NSPLookupServiceEnd; + outRoutines->NSPSetService = NSPSetService; + outRoutines->NSPInstallServiceClass = NSPInstallServiceClass; + outRoutines->NSPRemoveServiceClass = NSPRemoveServiceClass; + outRoutines->NSPGetServiceClassInfo = NSPGetServiceClassInfo; + + err = NO_ERROR; + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + NSPCleanup( inProviderID ); + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPCleanup +// +// This function is called when our namespace DLL is unloaded. It cleans up anything we set up in NSPStartup. +//=========================================================================================================================== + +int WSPAPI NSPCleanup( LPGUID inProviderID ) +{ + DEBUG_USE_ONLY( inProviderID ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s (GUID=%U, refCount=%ld)\n", __ROUTINE__, inProviderID, gRefCount ); + + // Only initialize if this is the first time NSPStartup is called. + + if( InterlockedDecrement( &gRefCount ) != 0 ) + { + goto exit; + } + + // Stop any outstanding queries. + + if( gLockInitialized ) + { + NSPLock(); + } + while( gQueryList ) + { + check_string( gQueryList->refCount == 1, "NSPCleanup with outstanding queries!" ); + QueryRelease( gQueryList ); + } + if( gLockInitialized ) + { + NSPUnlock(); + } + + // Shut down DNS-SD and release our resources. + + if( gDNSSDInitialized ) + { + gDNSSDInitialized = false; + DNSServiceFinalize(); + } + if( gLockInitialized ) + { + gLockInitialized = false; + DeleteCriticalSection( &gLock ); + } + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPLookupServiceBegin +// +// This function maps to the WinSock WSALookupServiceBegin function. It starts the lookup process and returns a HANDLE +// that can be used in subsequent operations. Subsequent calls only need to refer to this query by the handle as +// opposed to specifying the query parameters each time. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceBegin( + LPGUID inProviderID, + LPWSAQUERYSETW inQuerySet, + LPWSASERVICECLASSINFOW inServiceClassInfo, + DWORD inFlags, + LPHANDLE outLookup ) +{ + OSStatus err; + QueryRef obj; + LPCWSTR name; + size_t size; + LPCWSTR p; + DWORD type; + DWORD n; + DWORD i; + INT family; + INT protocol; + + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassInfo ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + + obj = NULL; + require_action( inQuerySet, exit, err = WSAEINVAL ); + name = inQuerySet->lpszServiceInstanceName; + require_action_quiet( name, exit, err = WSAEINVAL ); + require_action( outLookup, exit, err = WSAEINVAL ); + + dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=\"%S\")\n", __ROUTINE__, inFlags, name ); + dlog_query_set( kDebugLevelVerbose, inQuerySet ); + + // Check if we can handle this type of request and if we support any of the protocols being requested. + // We only support the DNS namespace, TCP and UDP protocols, and IPv4. Only blob results are supported. + + require_action_quiet( inFlags & LUP_RETURN_BLOB, exit, err = WSASERVICE_NOT_FOUND ); + + type = inQuerySet->dwNameSpace; + require_action_quiet( ( type == NS_DNS ) || ( type == NS_ALL ), exit, err = WSASERVICE_NOT_FOUND ); + + n = inQuerySet->dwNumberOfProtocols; + if( n > 0 ) + { + require_action( inQuerySet->lpafpProtocols, exit, err = WSAEINVAL ); + for( i = 0; i < n; ++i ) + { + family = inQuerySet->lpafpProtocols[ i ].iAddressFamily; + protocol = inQuerySet->lpafpProtocols[ i ].iProtocol; + if( ( family == AF_INET ) && ( ( protocol == IPPROTO_UDP ) || ( protocol == IPPROTO_TCP ) ) ) + { + break; + } + } + require_action_quiet( i < n, exit, err = WSASERVICE_NOT_FOUND ); + } + + // Check if the name ends in ".local" and if not, exit with an error since we only resolve .local names. + // The name may or may not end with a "." (fully qualified) so handle both cases. DNS is also case + // insensitive the check for .local has to be case insensitive (.LoCaL is equivalent to .local). This + // manually does the wchar_t strlen and stricmp to avoid needing any special wchar_t versions of the + // libraries. It is probably faster to do the inline compare than invoke functions to do it anyway. + + for( p = name; *p; ++p ) {} // Find end of string + size = (size_t)( p - name ); + require_action_quiet( size > sizeof_string( ".local" ), exit, err = WSASERVICE_NOT_FOUND ); + + p = name + ( size - 1 ); + p = ( *p == '.' ) ? ( p - sizeof_string( ".local" ) ) : ( ( p - sizeof_string( ".local" ) ) + 1 ); + require_action_quiet( ( ( p[ 0 ] == '.' ) && + ( ( p[ 1 ] == 'L' ) || ( p[ 1 ] == 'l' ) ) && + ( ( p[ 2 ] == 'O' ) || ( p[ 2 ] == 'o' ) ) && + ( ( p[ 3 ] == 'C' ) || ( p[ 3 ] == 'c' ) ) && + ( ( p[ 4 ] == 'A' ) || ( p[ 4 ] == 'a' ) ) && + ( ( p[ 5 ] == 'L' ) || ( p[ 5 ] == 'l' ) ) ), + exit, err = WSASERVICE_NOT_FOUND ); + + // The name ends in .local so start the resolve operation. Lazy initialize DNS-SD if needed. + + NSPLock(); + if( !gDNSSDInitialized ) + { + err = DNSServiceInitialize( kDNSServiceInitializeFlagsNoServerCheck, 0 ); + require_noerr( err, exit ); + gDNSSDInitialized = true; + } + + err = QueryCreate( inQuerySet, inFlags, &obj ); + NSPUnlock(); + require_noerr( err, exit ); + + *outLookup = (HANDLE) obj; + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPLookupServiceNext +// +// This function maps to the Winsock call WSALookupServiceNext. This routine takes a handle to a previously defined +// query and attempts to locate a service matching the criteria defined by the query. If so, that instance is returned +// in the lpqsResults parameter. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI + NSPLookupServiceNext( + HANDLE inLookup, + DWORD inFlags, + LPDWORD ioSize, + LPWSAQUERYSETW outResults ) +{ + OSStatus err; + QueryRef obj; + DWORD waitResult; + size_t size; + + DEBUG_USE_ONLY( inFlags ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + + obj = NULL; + NSPLock(); + err = QueryRetain( (QueryRef) inLookup ); + require_noerr( err, exit ); + obj = (QueryRef) inLookup; + require_action( ioSize, exit, err = WSAEINVAL ); + require_action( outResults, exit, err = WSAEINVAL ); + + dlog( kDebugLevelTrace, "%s (lookup=%#p, flags=0x%08X, *ioSize=%d)\n", __ROUTINE__, inLookup, inFlags, *ioSize ); + + // Wait for data or a cancel. Release the lock while waiting. This is safe because we've retained the query. + + NSPUnlock(); + waitResult = WaitForMultipleObjects( obj->waitCount, obj->waitHandles, FALSE, 5 * 1000 ); + NSPLock(); + require_action_quiet( waitResult != ( WAIT_OBJECT_0 + 1 ), exit, err = WSA_E_CANCELLED ); + err = translate_errno( waitResult == WAIT_OBJECT_0, (OSStatus) GetLastError(), WSASERVICE_NOT_FOUND ); + require_noerr_quiet( err, exit ); + require_action_quiet( obj->addrValid, exit, err = WSA_E_NO_MORE ); + + // Copy the externalized query results to the callers buffer (if it fits). + + size = QueryCopyQuerySetSize( obj, obj->querySet, obj->querySetFlags ); + require_action( size <= (size_t) *ioSize, exit, err = WSAEFAULT ); + + QueryCopyQuerySetTo( obj, obj->querySet, obj->querySetFlags, outResults ); + outResults->dwOutputFlags = RESULT_IS_ADDED; + obj->addrValid = false; + +exit: + if( obj ) + { + QueryRelease( obj ); + } + NSPUnlock(); + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPLookupServiceEnd +// +// This function maps to the Winsock call WSALookupServiceEnd. Once the user process has finished is query (usually +// indicated when WSALookupServiceNext returns the error WSA_E_NO_MORE) a call to this function is made to release any +// allocated resources associated with the query. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPLookupServiceEnd( HANDLE inLookup ) +{ + OSStatus err; + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + + dlog( kDebugLevelTrace, "%s (lookup=%#p)\n", __ROUTINE__, inLookup ); + + NSPLock(); + err = QueryRelease( (QueryRef) inLookup ); + NSPUnlock(); + require_noerr( err, exit ); + +exit: + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + if( err != NO_ERROR ) + { + SetLastError( (DWORD) err ); + return( SOCKET_ERROR ); + } + return( NO_ERROR ); +} + +//=========================================================================================================================== +// NSPSetService +// +// This function maps to the Winsock call WSASetService. This routine is called when the user wants to register or +// deregister an instance of a server with our service. For registration, the user needs to associate the server with a +// service class. For deregistration the service class is required along with the servicename. The inRegInfo parameter +// contains a WSAQUERYSET structure defining the server (such as protocol and address where it is). +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI + NSPSetService( + LPGUID inProviderID, + LPWSASERVICECLASSINFOW inServiceClassInfo, + LPWSAQUERYSETW inRegInfo, + WSAESETSERVICEOP inOperation, + DWORD inFlags ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassInfo ); + DEBUG_UNUSED( inRegInfo ); + DEBUG_UNUSED( inOperation ); + DEBUG_UNUSED( inFlags ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow services to be registered so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSAEINVAL ); +} + +//=========================================================================================================================== +// NSPInstallServiceClass +// +// This function maps to the Winsock call WSAInstallServiceClass. This routine is used to install a service class which +// is used to define certain characteristics for a group of services. After a service class is registered, an actual +// instance of a server may be registered. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPInstallServiceClass( LPGUID inProviderID, LPWSASERVICECLASSINFOW inServiceClassInfo ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassInfo ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow service classes to be installed so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSA_INVALID_PARAMETER ); +} + +//=========================================================================================================================== +// NSPRemoveServiceClass +// +// This function maps to the Winsock call WSARemoveServiceClass. This routine removes a previously registered service +// class. This is accomplished by connecting to the namespace service and writing the GUID which defines the given +// service class. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPRemoveServiceClass( LPGUID inProviderID, LPGUID inServiceClassID ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( inServiceClassID ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow service classes to be installed so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSATYPE_NOT_FOUND ); +} + +//=========================================================================================================================== +// NSPGetServiceClassInfo +// +// This function maps to the Winsock call WSAGetServiceClassInfo. This routine returns the information associated with +// a given service class. +//=========================================================================================================================== + +DEBUG_LOCAL int WSPAPI NSPGetServiceClassInfo( LPGUID inProviderID, LPDWORD ioSize, LPWSASERVICECLASSINFOW ioServiceClassInfo ) +{ + DEBUG_UNUSED( inProviderID ); + DEBUG_UNUSED( ioSize ); + DEBUG_UNUSED( ioServiceClassInfo ); + + dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ ); + + // We don't allow service classes to be installed so always return an error. + + dlog( kDebugLevelTrace, "%s end (ticks=%d)\n", __ROUTINE__, GetTickCount() ); + return( WSATYPE_NOT_FOUND ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// QueryCreate +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags, QueryRef *outRef ) +{ + OSStatus err; + QueryRef obj; + char name[ kDNSServiceMaxDomainName ]; + int n; + QueryRef * p; + + obj = NULL; + check( inQuerySet ); + check( inQuerySet->lpszServiceInstanceName ); + check( outRef ); + + // Convert the wchar_t name to UTF-8. + + n = WideCharToMultiByte( CP_UTF8, 0, inQuerySet->lpszServiceInstanceName, -1, name, sizeof( name ), NULL, NULL ); + err = translate_errno( n > 0, (OSStatus) GetLastError(), WSAEINVAL ); + require_noerr( err, exit ); + + // Allocate the object and append it to the list. Append immediately so releases of partial objects work. + + obj = (QueryRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + obj->refCount = 1; + + for( p = &gQueryList; *p; p = &( *p )->next ) {} // Find the end of the list. + *p = obj; + + // Set up events to signal when data is ready and when cancelling. + + obj->dataEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( obj->dataEvent, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + obj->cancelEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + require_action( obj->cancelEvent, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + obj->waitCount = 0; + obj->waitHandles[ obj->waitCount++ ] = obj->dataEvent; + obj->waitHandles[ obj->waitCount++ ] = obj->cancelEvent; + check( obj->waitCount == sizeof_array( obj->waitHandles ) ); + + // Copy the QuerySet so it can be returned later. + + obj->querySetFlags = inQuerySetFlags; + inQuerySetFlags = ( inQuerySetFlags & ~( LUP_RETURN_ADDR | LUP_RETURN_BLOB ) ) | LUP_RETURN_NAME; + err = QueryCopyQuerySet( obj, inQuerySet, inQuerySetFlags, &obj->querySet, &obj->querySetSize ); + require_noerr( err, exit ); + + // Start the query. + + err = DNSServiceQueryRecord( &obj->resolver, 0, 0, name, kDNSServiceDNSType_A, kDNSServiceDNSClass_IN, + QueryRecordCallback, obj ); + require_noerr( err, exit ); + + // Success! + + *outRef = obj; + obj = NULL; + err = NO_ERROR; + +exit: + if( obj ) + { + QueryRelease( obj ); + } + return( err ); +} + +//=========================================================================================================================== +// QueryRetain +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus QueryRetain( QueryRef inRef ) +{ + OSStatus err; + QueryRef obj; + + for( obj = gQueryList; obj; obj = obj->next ) + { + if( obj == inRef ) + { + break; + } + } + require_action( obj, exit, err = WSA_INVALID_HANDLE ); + + ++inRef->refCount; + err = NO_ERROR; + +exit: + return( err ); +} + +//=========================================================================================================================== +// QueryRelease +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus QueryRelease( QueryRef inRef ) +{ + OSStatus err; + QueryRef * p; + BOOL ok; + + // Find the item in the list. + + for( p = &gQueryList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + require_action( *p, exit, err = WSA_INVALID_HANDLE ); + + // Signal a cancel to unblock any threads waiting for results. + + if( inRef->cancelEvent ) + { + ok = SetEvent( inRef->cancelEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + + // Stop the query. + + if( inRef->resolver ) + { + DNSServiceRefDeallocate( inRef->resolver ); + inRef->resolver = NULL; + } + + // Decrement the refCount. Fully release if it drops to 0. If still referenced, just exit. + + if( --inRef->refCount != 0 ) + { + err = NO_ERROR; + goto exit; + } + *p = inRef->next; + + // Release resources. + + if( inRef->cancelEvent ) + { + ok = CloseHandle( inRef->cancelEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + if( inRef->dataEvent ) + { + ok = CloseHandle( inRef->dataEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + } + if( inRef->querySet ) + { + free( inRef->querySet ); + } + free( inRef ); + err = NO_ERROR; + +exit: + return( err ); +} + +//=========================================================================================================================== +// QueryRecordCallback +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + QueryRecordCallback( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ) +{ + QueryRef obj; + const char * src; + char * dst; + BOOL ok; + + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inTTL ); + + NSPLock(); + obj = (QueryRef) inContext; + check( obj ); + require_noerr( inErrorCode, exit ); + require_quiet( inFlags & kDNSServiceFlagsAdd, exit ); + require( inRRClass == kDNSServiceDNSClass_IN, exit ); + require( inRRType == kDNSServiceDNSType_A, exit ); + require( inRDataSize == 4, exit ); + + dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=%s, rrType=%d, rDataSize=%d)\n", + __ROUTINE__, inFlags, inName, inRRType, inRDataSize ); + + // Copy the name if needed. + + if( obj->name[ 0 ] == '\0' ) + { + src = inName; + dst = obj->name; + while( *src != '\0' ) + { + *dst++ = *src++; + } + *dst = '\0'; + obj->nameSize = (size_t)( dst - obj->name ); + check( obj->nameSize < sizeof( obj->name ) ); + } + + // Copy the data. + + memcpy( &obj->addr, inRData, inRDataSize ); + obj->addrValid = true; + + // Signal that a result is ready. + + check( obj->dataEvent ); + ok = SetEvent( obj->dataEvent ); + check_translated_errno( ok, GetLastError(), WSAEINVAL ); + + // Stop the resolver after the first response. + + DNSServiceRefDeallocate( inRef ); + obj->resolver = NULL; + +exit: + NSPUnlock(); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// QueryCopyQuerySet +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus + QueryCopyQuerySet( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW ** outQuerySet, + size_t * outSize ) +{ + OSStatus err; + size_t size; + WSAQUERYSETW * qs; + + check( inQuerySet ); + check( outQuerySet ); + + size = QueryCopyQuerySetSize( inRef, inQuerySet, inQuerySetFlags ); + qs = (WSAQUERYSETW *) calloc( 1, size ); + require_action( qs, exit, err = WSA_NOT_ENOUGH_MEMORY ); + + QueryCopyQuerySetTo( inRef, inQuerySet, inQuerySetFlags, qs ); + + *outQuerySet = qs; + if( outSize ) + { + *outSize = size; + } + qs = NULL; + err = NO_ERROR; + +exit: + if( qs ) + { + free( qs ); + } + return( err ); +} + +//=========================================================================================================================== +// QueryCopyQuerySetTo +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL void + QueryCopyQuerySetTo( + QueryRef inRef, + const WSAQUERYSETW * inQuerySet, + DWORD inQuerySetFlags, + WSAQUERYSETW * outQuerySet ) +{ + uint8_t * dst; + LPCWSTR s; + LPWSTR q; + DWORD n; + DWORD i; + +#if( DEBUG ) + size_t debugSize; + + debugSize = QueryCopyQuerySetSize( inRef, inQuerySet, inQuerySetFlags ); +#endif + + check( inQuerySet ); + check( outQuerySet ); + + dst = (uint8_t *) outQuerySet; + + // Copy the static portion of the results. + + *outQuerySet = *inQuerySet; + dst += sizeof( *inQuerySet ); + + if( inQuerySetFlags & LUP_RETURN_NAME ) + { + s = inQuerySet->lpszServiceInstanceName; + if( s ) + { + outQuerySet->lpszServiceInstanceName = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + } + else + { + outQuerySet->lpszServiceInstanceName = NULL; + } + + if( inQuerySet->lpServiceClassId ) + { + outQuerySet->lpServiceClassId = (LPGUID) dst; + *outQuerySet->lpServiceClassId = *inQuerySet->lpServiceClassId; + dst += sizeof( *inQuerySet->lpServiceClassId ); + } + + if( inQuerySet->lpVersion ) + { + outQuerySet->lpVersion = (LPWSAVERSION) dst; + *outQuerySet->lpVersion = *inQuerySet->lpVersion; + dst += sizeof( *inQuerySet->lpVersion ); + } + + s = inQuerySet->lpszComment; + if( s ) + { + outQuerySet->lpszComment = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + + if( inQuerySet->lpNSProviderId ) + { + outQuerySet->lpNSProviderId = (LPGUID) dst; + *outQuerySet->lpNSProviderId = *inQuerySet->lpNSProviderId; + dst += sizeof( *inQuerySet->lpNSProviderId ); + } + + s = inQuerySet->lpszContext; + if( s ) + { + outQuerySet->lpszContext = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + + n = inQuerySet->dwNumberOfProtocols; + if( n > 0 ) + { + check( inQuerySet->lpafpProtocols ); + + outQuerySet->lpafpProtocols = (LPAFPROTOCOLS) dst; + for( i = 0; i < n; ++i ) + { + outQuerySet->lpafpProtocols[ i ] = inQuerySet->lpafpProtocols[ i ]; + dst += sizeof( *inQuerySet->lpafpProtocols ); + } + } + + s = inQuerySet->lpszQueryString; + if( s ) + { + outQuerySet->lpszQueryString = (LPWSTR) dst; + q = (LPWSTR) dst; + while( ( *q++ = *s++ ) != 0 ) {} + dst = (uint8_t *) q; + } + + // Copy the address(es). + + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addrValid ) + { + struct sockaddr_in * addr; + + outQuerySet->dwNumberOfCsAddrs = 1; + outQuerySet->lpcsaBuffer = (LPCSADDR_INFO) dst; + dst += sizeof( *outQuerySet->lpcsaBuffer ); + + outQuerySet->lpcsaBuffer[ 0 ].LocalAddr.lpSockaddr = NULL; + outQuerySet->lpcsaBuffer[ 0 ].LocalAddr.iSockaddrLength = 0; + + outQuerySet->lpcsaBuffer[ 0 ].RemoteAddr.lpSockaddr = (LPSOCKADDR) dst; + outQuerySet->lpcsaBuffer[ 0 ].RemoteAddr.iSockaddrLength = sizeof( struct sockaddr_in ); + + addr = (struct sockaddr_in *) dst; + memset( addr, 0, sizeof( *addr ) ); + addr->sin_family = AF_INET; + memcpy( &addr->sin_addr, &inRef->addr, 4 ); + dst += sizeof( *addr ); + + outQuerySet->lpcsaBuffer[ 0 ].iSocketType = AF_INET; // Emulate Tcpip NSP + outQuerySet->lpcsaBuffer[ 0 ].iProtocol = IPPROTO_UDP; // Emulate Tcpip NSP + } + else + { + outQuerySet->dwNumberOfCsAddrs = 0; + outQuerySet->lpcsaBuffer = NULL; + } + + // Copy the hostent blob. + + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addrValid ) + { + uint8_t * base; + struct hostent * he; + uintptr_t * p; + + outQuerySet->lpBlob = (LPBLOB) dst; + dst += sizeof( *outQuerySet->lpBlob ); + + base = dst; + he = (struct hostent *) dst; + dst += sizeof( *he ); + + he->h_name = (char *)( dst - base ); + memcpy( dst, inRef->name, inRef->nameSize + 1 ); + dst += ( inRef->nameSize + 1 ); + + he->h_aliases = (char **)( dst - base ); + p = (uintptr_t *) dst; + *p++ = 0; + dst = (uint8_t *) p; + + he->h_addrtype = AF_INET; + he->h_length = 4; + + he->h_addr_list = (char **)( dst - base ); + p = (uintptr_t *) dst; + dst += ( 2 * sizeof( *p ) ); + *p++ = (uintptr_t)( dst - base ); + *p++ = 0; + p = (uintptr_t *) dst; + *p++ = (uintptr_t) inRef->addr; + dst = (uint8_t *) p; + + outQuerySet->lpBlob->cbSize = (ULONG)( dst - base ); + outQuerySet->lpBlob->pBlobData = (BYTE *) base; + } + dlog_query_set( kDebugLevelVerbose, outQuerySet ); + + check( (size_t)( dst - ( (uint8_t *) outQuerySet ) ) == debugSize ); +} + +//=========================================================================================================================== +// QueryCopyQuerySetSize +// +// Warning: Assumes the NSP lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags ) +{ + size_t size; + LPCWSTR s; + LPCWSTR p; + + check( inRef ); + check( inQuerySet ); + + // Calculate the size of the static portion of the results. + + size = sizeof( *inQuerySet ); + + if( inQuerySetFlags & LUP_RETURN_NAME ) + { + s = inQuerySet->lpszServiceInstanceName; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + } + + if( inQuerySet->lpServiceClassId ) + { + size += sizeof( *inQuerySet->lpServiceClassId ); + } + + if( inQuerySet->lpVersion ) + { + size += sizeof( *inQuerySet->lpVersion ); + } + + s = inQuerySet->lpszComment; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + + if( inQuerySet->lpNSProviderId ) + { + size += sizeof( *inQuerySet->lpNSProviderId ); + } + + s = inQuerySet->lpszContext; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + + size += ( inQuerySet->dwNumberOfProtocols * sizeof( *inQuerySet->lpafpProtocols ) ); + + s = inQuerySet->lpszQueryString; + if( s ) + { + for( p = s; *p; ++p ) {} + size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) ); + } + + // Calculate the size of the address(es). + + if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addrValid ) + { + size += sizeof( *inQuerySet->lpcsaBuffer ); + size += sizeof( struct sockaddr_in ); + } + + // Calculate the size of the hostent blob. + + if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addrValid ) + { + size += sizeof( *inQuerySet->lpBlob ); // Blob ptr/size structure + size += sizeof( struct hostent ); // Old-style hostent structure + size += ( inRef->nameSize + 1 ); // Name and null terminator + size += 4; // Alias list terminator (0 offset) + size += 4; // Offset to address. + size += 4; // Address list terminator (0 offset) + size += 4; // IPv4 address + } + return( size ); +} + +#if 0 +#pragma mark - +#endif + +#if( DEBUG ) +//=========================================================================================================================== +// DebugDumpQuerySet +//=========================================================================================================================== + +#define DebugSocketFamilyToString( FAM ) ( ( FAM ) == AF_INET ) ? "AF_INET" : \ + ( ( FAM ) == AF_INET6 ) ? "AF_INET6" : "" + +#define DebugSocketProtocolToString( PROTO ) ( ( PROTO ) == IPPROTO_UDP ) ? "IPPROTO_UDP" : \ + ( ( PROTO ) == IPPROTO_TCP ) ? "IPPROTO_TCP" : "" + +#define DebugNameSpaceToString( NS ) ( ( NS ) == NS_DNS ) ? "NS_DNS" : ( ( NS ) == NS_ALL ) ? "NS_ALL" : "" + +void DebugDumpQuerySet( DebugLevel inLevel, const WSAQUERYSETW *inQuerySet ) +{ + DWORD i; + + check( inQuerySet ); + + // Fixed portion of the QuerySet. + + dlog( inLevel, "QuerySet:\n" ); + dlog( inLevel, " dwSize: %d (expected %d)\n", inQuerySet->dwSize, sizeof( *inQuerySet ) ); + if( inQuerySet->lpszServiceInstanceName ) + { + dlog( inLevel, " lpszServiceInstanceName: %S\n", inQuerySet->lpszServiceInstanceName ); + } + else + { + dlog( inLevel, " lpszServiceInstanceName: \n" ); + } + if( inQuerySet->lpServiceClassId ) + { + dlog( inLevel, " lpServiceClassId: %U\n", inQuerySet->lpServiceClassId ); + } + else + { + dlog( inLevel, " lpServiceClassId: \n" ); + } + if( inQuerySet->lpVersion ) + { + dlog( inLevel, " lpVersion:\n" ); + dlog( inLevel, " dwVersion: %d\n", inQuerySet->lpVersion->dwVersion ); + dlog( inLevel, " dwVersion: %d\n", inQuerySet->lpVersion->ecHow ); + } + else + { + dlog( inLevel, " lpVersion: \n" ); + } + if( inQuerySet->lpszComment ) + { + dlog( inLevel, " lpszComment: %S\n", inQuerySet->lpszComment ); + } + else + { + dlog( inLevel, " lpszComment: \n" ); + } + dlog( inLevel, " dwNameSpace: %d %s\n", inQuerySet->dwNameSpace, + DebugNameSpaceToString( inQuerySet->dwNameSpace ) ); + if( inQuerySet->lpNSProviderId ) + { + dlog( inLevel, " lpNSProviderId: %U\n", inQuerySet->lpNSProviderId ); + } + else + { + dlog( inLevel, " lpNSProviderId: \n" ); + } + if( inQuerySet->lpszContext ) + { + dlog( inLevel, " lpszContext: %S\n", inQuerySet->lpszContext ); + } + else + { + dlog( inLevel, " lpszContext: \n" ); + } + dlog( inLevel, " dwNumberOfProtocols: %d\n", inQuerySet->dwNumberOfProtocols ); + dlog( inLevel, " lpafpProtocols: %s\n", inQuerySet->lpafpProtocols ? "" : "" ); + for( i = 0; i < inQuerySet->dwNumberOfProtocols; ++i ) + { + if( i != 0 ) + { + dlog( inLevel, "\n" ); + } + dlog( inLevel, " iAddressFamily: %d %s\n", inQuerySet->lpafpProtocols[ i ].iAddressFamily, + DebugSocketFamilyToString( inQuerySet->lpafpProtocols[ i ].iAddressFamily ) ); + dlog( inLevel, " iProtocol: %d %s\n", inQuerySet->lpafpProtocols[ i ].iProtocol, + DebugSocketProtocolToString( inQuerySet->lpafpProtocols[ i ].iProtocol ) ); + } + if( inQuerySet->lpszQueryString ) + { + dlog( inLevel, " lpszQueryString: %S\n", inQuerySet->lpszQueryString ); + } + else + { + dlog( inLevel, " lpszQueryString: \n" ); + } + dlog( inLevel, " dwNumberOfCsAddrs: %d\n", inQuerySet->dwNumberOfCsAddrs ); + dlog( inLevel, " lpcsaBuffer: %s\n", inQuerySet->lpcsaBuffer ? "" : "" ); + for( i = 0; i < inQuerySet->dwNumberOfCsAddrs; ++i ) + { + if( i != 0 ) + { + dlog( inLevel, "\n" ); + } + if( inQuerySet->lpcsaBuffer[ i ].LocalAddr.lpSockaddr && + ( inQuerySet->lpcsaBuffer[ i ].LocalAddr.iSockaddrLength > 0 ) ) + { + dlog( inLevel, " LocalAddr: %##a\n", + inQuerySet->lpcsaBuffer[ i ].LocalAddr.lpSockaddr ); + } + else + { + dlog( inLevel, " LocalAddr: \n" ); + } + if( inQuerySet->lpcsaBuffer[ i ].RemoteAddr.lpSockaddr && + ( inQuerySet->lpcsaBuffer[ i ].RemoteAddr.iSockaddrLength > 0 ) ) + { + dlog( inLevel, " RemoteAddr: %##a\n", + inQuerySet->lpcsaBuffer[ i ].RemoteAddr.lpSockaddr ); + } + else + { + dlog( inLevel, " RemoteAddr: \n" ); + } + dlog( inLevel, " iSocketType: %d\n", inQuerySet->lpcsaBuffer[ i ].iSocketType ); + dlog( inLevel, " iProtocol: %d\n", inQuerySet->lpcsaBuffer[ i ].iProtocol ); + } + dlog( inLevel, " dwOutputFlags: %d\n", inQuerySet->dwOutputFlags ); + + // Blob portion of the QuerySet. + + if( inQuerySet->lpBlob ) + { + dlog( inLevel, " lpBlob:\n" ); + dlog( inLevel, " cbSize: %ld\n", inQuerySet->lpBlob->cbSize ); + dlog( inLevel, " pBlobData: %#p\n", inQuerySet->lpBlob->pBlobData ); + dloghex( inLevel, 12, NULL, 0, 0, NULL, 0, + inQuerySet->lpBlob->pBlobData, inQuerySet->lpBlob->pBlobData, inQuerySet->lpBlob->cbSize, + kDebugFlagsNone, NULL, 0 ); + } + else + { + dlog( inLevel, " lpBlob: \n" ); + } +} +#endif diff --git a/mDNSWindows/Applications/mdnsNSP/mdnsNSP.def b/mDNSWindows/Applications/mdnsNSP/mdnsNSP.def new file mode 100644 index 0000000..948f863 --- /dev/null +++ b/mDNSWindows/Applications/mdnsNSP/mdnsNSP.def @@ -0,0 +1,36 @@ +; +; Copyright (c) 2003-2004 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: mdnsNSP.def,v $ +; Revision 1.1 2004/01/30 03:00:33 bradley +; Rendezvous NameSpace Provider (NSP). Hooks into the Windows name resolution system to resolve +; Rendezvous name lookups using Multicast DNS so .local names work in all Windows apps. +; +; + +LIBRARY RendezvousNSP + +EXPORTS + NSPStartup + NSPCleanup diff --git a/mDNSWindows/Applications/mdnsNSP/mdnsNSP.mcp b/mDNSWindows/Applications/mdnsNSP/mdnsNSP.mcp new file mode 100644 index 0000000000000000000000000000000000000000..96007ece3f0de0b3628107a5859a41336156938b GIT binary patch literal 198118 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 +#endif + +#if( ( macintosh || __MACH__ ) && !KERNEL ) + + #if( defined( __MWERKS__ ) ) + #if( __option( c9x ) ) + #include + #endif + #else + #include + #endif + + #include + + #if( __MACH__ ) + + // Mac OS X + + #include + #include + #include + #include + #include + #include + #include + #include + + #include + + #else + + // Classic Mac OS + + #include + #include + + #endif + +#elif( KERNEL ) + + // Mac OS X Kernel + + #include + + #include + #include + +#elif( TARGET_OS_LINUX ) + + // Linux (no special includes yet). + +#elif( TARGET_OS_PALM ) + + // Palm (no special includes yet). + +#elif( TARGET_OS_VXWORKS ) + + // VxWorks + + #include "vxWorks.h" + +#elif( TARGET_OS_WIN32 ) + + // Windows + + #if( !defined( WIN32_WINDOWS ) ) + #define WIN32_WINDOWS 0x0401 + #endif + + #if( !defined( _WIN32_WINDOWS ) ) + #define _WIN32_WINDOWS 0x0401 + #endif + + #if( !defined( WIN32_LEAN_AND_MEAN ) ) + #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces. + #endif + + #if( defined( __MWERKS__ ) ) + + #if( __option( c9x ) ) + #include + #endif + + #include + + #elif( defined( _MSC_VER ) ) + + #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. + #pragma warning( disable:4706 ) // Disable "assignment within conditional expression" for Microsoft headers. + + #endif + + #include + #include + #include + + #if( defined( _MSC_VER ) ) + #pragma warning( default:4706 ) + #endif + +#else + #error unknown OS - update this file to support your OS +#endif + +#if( !defined( TARGET_BUILD_MAIN ) ) + #if( !TARGET_OS_VXWORKS ) + #define TARGET_BUILD_MAIN 1 + #endif +#endif + +#if( __GNUC__ || !TARGET_OS_VXWORKS ) + #define TARGET_LANGUAGE_C_LIKE 1 +#else + #define TARGET_LANGUAGE_C_LIKE 0 +#endif + +#if 0 +#pragma mark == CPU == +#endif + +//=========================================================================================================================== +// CPU +//=========================================================================================================================== + +// PowerPC + +#if( !defined( TARGET_CPU_PPC ) ) + #if( defined( __ppc__ ) || defined( __PPC__ ) || defined( powerpc ) || defined( ppc ) || defined( _M_MPPC ) ) + #define TARGET_CPU_PPC 1 + #else + #define TARGET_CPU_PPC 0 + #endif +#endif + +// x86 + +#if( !defined( TARGET_CPU_X86 ) ) + #if( __INTEL__ || defined( __i386__ ) || defined( i386 ) || defined( intel ) || defined( _M_IX86 ) ) + #define TARGET_CPU_X86 1 + #else + #define TARGET_CPU_X86 0 + #endif +#endif + +// MIPS + +#if( !defined( TARGET_CPU_MIPS ) ) + #if( __MIPS__ || defined( MIPS32 ) || defined( R3000 ) || defined( R4000 ) || defined( R4650 ) || defined( _M_MRX000 ) ) + #define TARGET_CPU_MIPS 1 + #else + #define TARGET_CPU_MIPS 0 + #endif +#endif + +#if( !defined( TARGET_CPU_PPC ) && !defined( TARGET_CPU_X86 ) && !defined( TARGET_CPU_MIPS ) ) + #error unknown CPU - update this file to support your CPU +#endif + +#if 0 +#pragma mark == Byte Order == +#endif + +//=========================================================================================================================== +// Byte Order +//=========================================================================================================================== + +// TARGET_RT_LITTLE_ENDIAN + +#if( !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #if( MIPSEL || IL_LITTLE_ENDIAN || defined( __LITTLE_ENDIAN__ ) || \ + ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) ) || \ + ( defined( _BYTE_ORDER ) && defined( _LITTLE_ENDIAN ) && ( _BYTE_ORDER == _LITTLE_ENDIAN ) ) || \ + ( defined( __BYTE_ORDER ) && defined( __LITTLE_ENDIAN ) && ( __BYTE_ORDER == __LITTLE_ENDIAN ) ) || \ + TARGET_CPU_X86 || ( defined( TARGET_RT_BIG_ENDIAN ) && !TARGET_RT_BIG_ENDIAN ) ) + #define TARGET_RT_LITTLE_ENDIAN 1 + #else + #define TARGET_RT_LITTLE_ENDIAN 0 + #endif +#endif + +// TARGET_RT_BIG_ENDIAN + +#if( !defined( TARGET_RT_BIG_ENDIAN ) ) + #if( MIPSEB || IL_BIG_ENDIAN || defined( __BIG_ENDIAN__ ) || \ + ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) ) || \ + ( defined( _BYTE_ORDER ) && defined( _BIG_ENDIAN ) && ( _BYTE_ORDER == _BIG_ENDIAN ) ) || \ + ( defined( __BYTE_ORDER ) && defined( __BIG_ENDIAN ) && ( __BYTE_ORDER == __BIG_ENDIAN ) ) || \ + ( defined( TARGET_RT_LITTLE_ENDIAN ) && !TARGET_RT_LITTLE_ENDIAN ) ) + #define TARGET_RT_BIG_ENDIAN 1 + #else + #define TARGET_RT_BIG_ENDIAN 0 + #endif +#endif + +#if( defined( TARGET_RT_LITTLE_ENDIAN ) && !defined( TARGET_RT_BIG_ENDIAN ) ) + #if( TARGET_RT_LITTLE_ENDIAN ) + #define TARGET_RT_BIG_ENDIAN 0 + #else + #define TARGET_RT_BIG_ENDIAN 1 + #endif +#endif + +#if( defined( TARGET_RT_BIG_ENDIAN ) && !defined( TARGET_RT_LITTLE_ENDIAN ) ) + #if( TARGET_RT_BIG_ENDIAN ) + #define TARGET_RT_LITTLE_ENDIAN 0 + #else + #define TARGET_RT_LITTLE_ENDIAN 1 + #endif +#endif + +#if( !defined( TARGET_RT_LITTLE_ENDIAN ) || !defined( TARGET_RT_BIG_ENDIAN ) ) + #error unknown byte order - update this file to support your byte order +#endif + +// TARGET_RT_BYTE_ORDER + +#if( !defined( TARGET_RT_BYTE_ORDER_BIG_ENDIAN ) ) + #define TARGET_RT_BYTE_ORDER_BIG_ENDIAN 1234 +#endif + +#if( !defined( TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN ) ) + #define TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN 4321 +#endif + +#if( !defined( TARGET_RT_BYTE_ORDER ) ) + #if( TARGET_RT_LITTLE_ENDIAN ) + #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN + #else + #define TARGET_RT_BYTE_ORDER TARGET_RT_BYTE_ORDER_BIG_ENDIAN + #endif +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#if( !TARGET_OS_MAC ) + #define CR '\r' +#endif + +#define LF '\n' +#define CRSTR "\r" +#define LFSTR "\n" +#define CRLF "\r\n" +#define CRCR "\r\r" + +#if 0 +#pragma mark == Compatibility == +#endif + +//=========================================================================================================================== +// Compatibility +//=========================================================================================================================== + +// Macros to allow the same code to work on Windows and other sockets API-compatible platforms. + +#if( TARGET_OS_WIN32 ) + #define close_compat( X ) closesocket( X ) + #define errno_compat() (int) GetLastError() + #define set_errno_compat( X ) SetLastError( X ) + #define EWOULDBLOCK_compat WSAEWOULDBLOCK + #define ETIMEDOUT_compat WSAETIMEDOUT + #define ENOTCONN_compat WSAENOTCONN + #define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET ) + #define kInvalidSocketRef INVALID_SOCKET + #if( TARGET_LANGUAGE_C_LIKE ) + typedef SOCKET SocketRef; + #endif +#else + #define close_compat( X ) close( X ) + #define errno_compat() errno + #define set_errno_compat( X ) do { errno = ( X ); } while( 0 ) + #define EWOULDBLOCK_compat EWOULDBLOCK + #define ETIMEDOUT_compat ETIMEDOUT + #define ENOTCONN_compat ENOTCONN + #define IsValidSocket( X ) ( ( X ) >= 0 ) + #define kInvalidSocketRef -1 + #if( TARGET_LANGUAGE_C_LIKE ) + typedef int SocketRef; + #endif +#endif + +// socklen_t is not defined on the following platforms so emulate it if not defined: +// +// - Pre-Panther Mac OS X. Panther defines SO_NOADDRERR so trigger off that. +// - Windows SDK prior to 2003. 2003+ SDK's define EAI_AGAIN so trigger off that. +// - VxWorks + +#if( TARGET_LANGUAGE_C_LIKE ) + #if( ( TARGET_OS_MAC && !defined( SO_NOADDRERR ) ) || ( TARGET_OS_WIN32 && !defined( EAI_AGAIN ) ) || TARGET_OS_VXWORKS ) + typedef int socklen_t; + #endif +#endif + +// ssize_t is not defined on the following platforms so emulate it if not defined: +// +// - Mac OS X when not building with BSD headers +// - Windows + +#if( TARGET_LANGUAGE_C_LIKE ) + #if( ( TARGET_OS_WIN32 || !defined( _BSD_SSIZE_T_DEFINED_ ) ) && !TARGET_OS_VXWORKS ) + typedef int ssize_t; + #endif +#endif + +// sockaddr_storage is not supported on non-IPv6 machines so alias it to an IPv4-compatible structure. + +#if( TARGET_LANGUAGE_C_LIKE ) + #if( !defined( AF_INET6 ) ) + #define sockaddr_storage sockaddr_in + #define ss_family sin_family + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined SOCKADDR_IS_IP_LOOPBACK + + @abstract Determines if a sockaddr is an IPv4 or IPv6 loopback address (if IPv6 is supported). +*/ + +#if( defined( AF_INET6 ) ) + #define SOCKADDR_IS_IP_LOOPBACK( SA ) \ + ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \ + : ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET6 ) \ + ? IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) \ + : 0 +#else + #define SOCKADDR_IS_IP_LOOPBACK( SA ) \ + ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) \ + : 0 +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined SOCKADDR_IS_IP_LINK_LOCAL + + @abstract Determines if a sockaddr is an IPv4 or IPv6 link-local address (if IPv6 is supported). +*/ + +#if( defined( AF_INET6 ) ) + #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \ + ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \ + ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \ + : IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) ) +#else + #define SOCKADDR_IS_IP_LINK_LOCAL( SA ) \ + ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET ) \ + ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && \ + ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) ) \ + : 0 ) +#endif + +// _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( TARGET_OS_WINDOWS_CE ) + #define _beginthreadex_compat( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR ) \ + (uintptr_t) CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, \ + (LPDWORD) THREAD_ID_PTR ) + + #define _endthreadex_compat( RESULT ) ExitThread( (DWORD) RESULT ) +#elif( TARGET_OS_WIN32 ) + #define _beginthreadex_compat _beginthreadex + #define _endthreadex_compat _endthreadex +#endif + +// The C99 "inline" keyword is not supported by Microsoft compilers, but they do support __inline so map it when needed. + +#if( defined( _MSC_VER ) ) + #define inline_compat __inline +#else + #define inline_compat inline +#endif + +// Calling conventions + +#if( !defined( CALLBACK_COMPAT ) ) + #if( TARGET_OS_WIN32 || TARGET_OS_WINDOWS_CE ) + #define CALLBACK_COMPAT CALLBACK + #else + #define CALLBACK_COMPAT + #endif +#endif + +#if 0 +#pragma mark == Macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kSizeCString + + @abstract A meta-value to pass to supported routines to indicate the size should be calculated with strlen. +*/ + +#define kSizeCString ( (size_t) -1 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined sizeof_array + + @abstract Determines the number of elements in an array. +*/ + +#define sizeof_array( X ) ( sizeof( X ) / sizeof( X[ 0 ] ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined sizeof_element + + @abstract Determines the size of an array element. +*/ + +#define sizeof_element( X ) sizeof( X[ 0 ] ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined sizeof_string + + @abstract Determines the size of a constant C string, excluding the null terminator. +*/ + +#define sizeof_string( X ) ( sizeof( ( X ) ) - 1 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined sizeof_field + + @abstract Determines the size of a field of a type. +*/ + +#define sizeof_field( TYPE, FIELD ) sizeof( ( ( (TYPE *) 0 )->FIELD ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RoundUp + + @abstract Rounds X up to a multiple of Y. +*/ + +#define RoundUp( X, Y ) ( ( X ) + ( ( Y ) - ( ( X ) % ( Y ) ) ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function IsAligned + + @abstract Returns non-zero if X is aligned to a Y byte boundary and 0 if not. Y must be a power of 2. +*/ + +#define IsAligned( X, Y ) ( ( ( X ) & ( ( Y ) - 1 ) ) == 0 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function IsFieldAligned + + @abstract Returns non-zero if FIELD of type TYPE is aligned to a Y byte boundary and 0 if not. Y must be a power of 2. +*/ + +#define IsFieldAligned( X, TYPE, FIELD, Y ) IsAligned( ( (uintptr_t)( X ) ) + offsetof( TYPE, FIELD ), ( Y ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function AlignDown + + @abstract Aligns X down to a Y byte boundary. Y must be a power of 2. +*/ + +#define AlignDown( X, Y ) ( ( X ) & ~( ( Y ) - 1 ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function AlignUp + + @abstract Aligns X up to a Y byte boundary. Y must be a power of 2. +*/ + +#define AlignUp( X, Y ) ( ( ( X ) + ( ( Y ) - 1 ) ) & ~( ( Y ) - 1 ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function Min + + @abstract Returns the lesser of X and Y. +*/ + +#if( !defined( Min ) ) + #define Min( X, Y ) ( ( ( X ) < ( Y ) ) ? ( X ) : ( Y ) ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function Max + + @abstract Returns the greater of X and Y. +*/ + +#if( !defined( Max ) ) + #define Max( X, Y ) ( ( ( X ) > ( Y ) ) ? ( X ) : ( Y ) ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function InsertBits + + @abstract Inserts BITS (both 0 and 1 bits) into X, controlled by MASK and SHIFT, and returns the result. + + @discussion + + MASK is the bitmask of the bits in the final position. + SHIFT is the number of bits to shift left for 1 to reach the first bit position of MASK. + + For example, if you wanted to insert 0x3 into the leftmost 4 bits of a 32-bit value: + + InsertBits( 0, 0x3, 0xF0000000U, 28 ) == 0x30000000 +*/ + +#define InsertBits( X, BITS, MASK, SHIFT ) ( ( ( X ) & ~( MASK ) ) | ( ( ( BITS ) << ( SHIFT ) ) & ( MASK ) ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function ExtractBits + + @abstract Extracts bits from X, controlled by MASK and SHIFT, and returns the result. + + @discussion + + MASK is the bitmask of the bits in the final position. + SHIFT is the number of bits to shift right to right justify MASK. + + For example, if you had a 32-bit value (e.g. 0x30000000) wanted the left-most 4 bits (e.g. 3 in this example): + + ExtractBits( 0x30000000U, 0xF0000000U, 28 ) == 0x3 +*/ + +#define ExtractBits( X, MASK, SHIFT ) ( ( ( X ) >> ( SHIFT ) ) & ( ( MASK ) >> ( SHIFT ) ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function Stringify + + @abstract Stringify's an expression. + + @discussion + + Stringify macros to process raw text passed via -D options to C string constants. The double-wrapping is necessary + because the C preprocessor doesn't perform its normal argument expansion pre-scan with stringified macros so the + -D macro needs to be expanded once via the wrapper macro then stringified so the raw text is stringified. Otherwise, + the replacement value would be used instead of the symbolic name (only for preprocessor symbols like #defines). + + For example: + + #define kMyConstant 1 + + printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant" + printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "1" + + Non-preprocessor symbols do not have this issue. For example: + + enum + { + kMyConstant = 1 + }; + + printf( "%s", Stringify( kMyConstant ) ); // Prints "kMyConstant" + printf( "%s", StringifyExpansion( kMyConstant ) ); // Prints "kMyConstant" + + See for more info on C preprocessor pre-scanning. +*/ + +#define Stringify( X ) # X +#define StringifyExpansion( X ) Stringify( X ) + +#if 0 +#pragma mark == Types == +#endif + +#if( TARGET_LANGUAGE_C_LIKE ) +//=========================================================================================================================== +// Standard Types +//=========================================================================================================================== + +#if( !defined( INT8_MIN ) ) + + #define INT8_MIN SCHAR_MIN + + #if( defined( _MSC_VER ) ) + + // C99 stdint.h not supported in VC++/VS.NET yet. + + typedef INT8 int8_t; + typedef UINT8 uint8_t; + typedef INT16 int16_t; + typedef UINT16 uint16_t; + typedef INT32 int32_t; + typedef UINT32 uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + + #elif( TARGET_OS_VXWORKS && ( TORNADO_VERSION < 220 ) ) + typedef long long int64_t; + typedef unsigned long long uint64_t; + #endif + + typedef int8_t int_least8_t; + typedef int16_t int_least16_t; + typedef int32_t int_least32_t; + typedef int64_t int_least64_t; + + typedef uint8_t uint_least8_t; + typedef uint16_t uint_least16_t; + typedef uint32_t uint_least32_t; + typedef uint64_t uint_least64_t; + + typedef int8_t int_fast8_t; + typedef int16_t int_fast16_t; + typedef int32_t int_fast32_t; + typedef int64_t int_fast64_t; + + typedef uint8_t uint_fast8_t; + typedef uint16_t uint_fast16_t; + typedef uint32_t uint_fast32_t; + typedef uint64_t uint_fast64_t; + + #if( !defined( _MSC_VER ) || TARGET_OS_WINDOWS_CE ) + typedef long int intptr_t; + typedef unsigned long int uintptr_t; + #endif + +#endif + +// Macros for minimum-width integer constants + +#if( !defined( INT8_C ) ) + #define INT8_C( value ) value +#endif + +#if( !defined( INT16_C ) ) + #define INT16_C( value ) value +#endif + +#if( !defined( INT32_C ) ) + #define INT32_C( value ) value ## L +#endif + +#if( !defined( INT64_C ) ) + #if( defined( _MSC_VER ) ) + #define INT64_C( value ) value ## i64 + #else + #define INT64_C( value ) value ## LL + #endif +#endif + +#if( !defined( UINT8_C ) ) + #define UINT8_C( value ) value ## U +#endif + +#if( !defined( UINT16_C ) ) + #define UINT16_C( value ) value ## U +#endif + +#if( !defined( UINT32_C ) ) + #define UINT32_C( value ) value ## UL +#endif + +#if( !defined( UINT64_C ) ) + #if( defined( _MSC_VER ) ) + #define UINT64_C( value ) value ## UI64 + #else + #define UINT64_C( value ) value ## ULL + #endif +#endif + +#if 0 +#pragma mark == bool == +#endif + +//=========================================================================================================================== +// Boolean Constants and Types +//=========================================================================================================================== + +// C++ defines bool, true, and false. Metrowerks allows this to be controlled by the "bool" option though. +// C99 defines __bool_true_false_are_defined when bool, true, and false are defined. +// MacTypes.h defines true and false (Mac builds only). +// +// Note: The Metrowerks has to be in its own block because Microsoft Visual Studio .NET does not completely +// short-circuit and gets confused by the option( bool ) portion of the conditional. + +#if( defined( __MWERKS__ ) ) + + // Note: The following test is done on separate lines because CodeWarrior doesn't like it all on one line. + + #if( !__bool_true_false_are_defined && ( !defined( __cplusplus ) || !__option( bool ) ) ) + #define COMMON_SERVICES_NEEDS_BOOL 1 + #else + #define COMMON_SERVICES_NEEDS_BOOL 0 + #endif + + // Workaround when building with CodeWarrior, but using the Apple stdbool.h header, which uses _Bool. + + #if( __bool_true_false_are_defined && !defined( __cplusplus ) && !__option( c9x ) ) + #define _Bool int + #endif + + // Workaround when building with CodeWarrior for C++ with bool disabled and using the Apple stdbool.h header, + // which defines true and false to map to C++ true and false (which are not enabled). Serenity Now! + + #if( __bool_true_false_are_defined && defined( __cplusplus ) && !__option( bool ) ) + #define true 1 + #define false 0 + #endif +#else + #define COMMON_SERVICES_NEEDS_BOOL ( !defined( __cplusplus ) && !__bool_true_false_are_defined ) +#endif + +#if( COMMON_SERVICES_NEEDS_BOOL ) + + typedef int bool; + + #define bool bool + + #if( !defined( __MACTYPES__ ) && !defined( true ) && !defined( false ) ) + #define true 1 + #define false 0 + #endif + + #define __bool_true_false_are_defined 1 +#endif + +// IOKit IOTypes.h typedef's bool if TYPE_BOOL is not defined so define it here to prevent redefinition by IOTypes.h. + +#if( TARGET_API_MAC_OSX_KERNEL ) + #define TYPE_BOOL 1 +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef CStr255 + + @abstract 255 character null-terminated (C-style) string. +*/ + +#if( TARGET_LANGUAGE_C_LIKE ) + typedef char CStr255[ 256 ]; +#endif + +#endif // TARGET_LANGUAGE_C_LIKE + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined TYPE_LONGLONG_NATIVE + + @abstract Defines whether long long (or its equivalent) is natively supported or requires special libraries. +*/ + +#if( !defined( TYPE_LONGLONG_NATIVE ) ) + #if( !TARGET_OS_VXWORKS ) + #define TYPE_LONGLONG_NATIVE 1 + #else + #define TYPE_LONGLONG_NATIVE 0 + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined long_long_compat + + @abstract Compatibility type to map to the closest thing to long long and unsigned long long. + + @discussion + + Neither long long nor unsigned long long are supported by Microsoft compilers, but they do support proprietary + "__int64" and "unsigned __int64" equivalents so map to those types if the real long long is not supported. +*/ + +#if( TARGET_LANGUAGE_C_LIKE ) + #if( TARGET_OS_WIN32 ) + typedef __int64 long_long_compat; + typedef unsigned __int64 unsigned_long_long_compat; + #else + typedef signed long long long_long_compat; + typedef unsigned long long unsigned_long_long_compat; + #endif +#endif + +#if 0 +#pragma mark == Errors == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum OSStatus + + @abstract Status Code + + @constant kNoErr 0 No error occurred. + @constant kInProgressErr 1 Operation in progress. + @constant kUnknownErr -6700 Unknown error occurred. + @constant kOptionErr -6701 Option was not acceptable. + @constant kSelectorErr -6702 Selector passed in is invalid or unknown. + @constant kExecutionStateErr -6703 Call made in the wrong execution state (e.g. called at interrupt time). + @constant kPathErr -6704 Path is invalid, too long, or otherwise not usable. + @constant kParamErr -6705 Parameter is incorrect, missing, or not appropriate. + @constant kParamCountErr -6706 Incorrect or unsupported number of parameters. + @constant kCommandErr -6707 Command invalid or not supported. + @constant kIDErr -6708 Unknown, invalid, or inappropriate identifier. + @constant kStateErr -6709 Not in appropriate state to perform operation. + @constant kRangeErr -6710 Index is out of range or not valid. + @constant kRequestErr -6711 Request was improperly formed or not appropriate. + @constant kResponseErr -6712 Response was incorrect or out of sequence. + @constant kChecksumErr -6713 Checksum does not match the actual data. + @constant kNotHandledErr -6714 Operation was not handled (or not handled completely). + @constant kVersionErr -6715 Version is not incorrect or not compatibile. + @constant kSignatureErr -6716 Signature did not match what was expected. + @constant kFormatErr -6717 Unknown, invalid, or inappropriate file/data format. + @constant kNotInitializedErr -6718 Action request before needed services were initialized. + @constant kAlreadyInitializedErr -6719 Attempt made to initialize when already initialized. + @constant kNotInUseErr -6720 Object not in use (e.g. cannot abort if not already in use). + @constant kInUseErr -6721 Object is in use (e.g. cannot reuse active param blocks). + @constant kTimeoutErr -6722 Timeout occurred. + @constant kCanceledErr -6723 Operation canceled (successful cancel). + @constant kAlreadyCanceledErr -6724 Operation has already been canceled. + @constant kCannotCancelErr -6725 Operation could not be canceled (maybe already done or invalid). + @constant kDeletedErr -6726 Object has already been deleted. + @constant kNotFoundErr -6727 Something was not found. + @constant kNoMemoryErr -6728 Not enough memory was available to perform the operation. + @constant kNoResourcesErr -6729 Resources unavailable to perform the operation. + @constant kDuplicateErr -6730 Duplicate found or something is a duplicate. + @constant kImmutableErr -6731 Entity is not changeable. + @constant kUnsupportedDataErr -6732 Data is unknown or not supported. + @constant kIntegrityErr -6733 Data is corrupt. + @constant kIncompatibleErr -6734 Data is not compatible or it is in an incompatible format. + @constant kUnsupportedErr -6735 Feature or option is not supported. + @constant kUnexpectedErr -6736 Error occurred that was not expected. + @constant kValueErr -6737 Value is not appropriate. + @constant kNotReadableErr -6738 Could not read or reading is not allowed. + @constant kNotWritableErr -6739 Could not write or writing is not allowed. + @constant kBadReferenceErr -6740 An invalid or inappropriate reference was specified. + @constant kFlagErr -6741 An invalid, inappropriate, or unsupported flag was specified. + @constant kMalformedErr -6742 Something was not formed correctly. + @constant kSizeErr -6743 Size was too big, too small, or not appropriate. + @constant kNameErr -6744 Name was not correct, allowed, or appropriate. + @constant kNotReadyErr -6745 Device or service is not ready. + @constant kReadErr -6746 Could not read. + @constant kWriteErr -6747 Could not write. + @constant kMismatchErr -6748 Something does not match. + @constant kDateErr -6749 Date is invalid or out-of-range. + @constant kUnderrunErr -6750 Less data than expected. + @constant kOverrunErr -6751 More data than expected. + @constant kEndingErr -6752 Connection, session, or something is ending. + @constant kConnectionErr -6753 Connection failed or could not be established. + @constant kAuthenticationErr -6754 Authentication failed or is not supported. + @constant kOpenErr -6755 Could not open file, pipe, device, etc. + @constant kTypeErr -6756 Incorrect or incompatible type (e.g. file, data, etc.). + @constant kSkipErr -6757 Items should be or was skipped. + @constant kNoAckErr -6758 No acknowledge. + @constant kCollisionErr -6759 Collision occurred (e.g. two on bus at same time). + @constant kBackoffErr -6760 Backoff in progress and operation intentionally failed. + @constant kNoAddressAckErr -6761 No acknowledge of address. + @constant kBusyErr -6762 Cannot perform because something is busy. + @constant kNoSpaceErr -6763 Not enough space to perform operation. +*/ + +#if( TARGET_LANGUAGE_C_LIKE ) + #if( !TARGET_OS_MAC && !TARGET_API_MAC_OSX_KERNEL ) + typedef int32_t OSStatus; + #endif +#endif + +#define kNoErr 0 +#define kInProgressErr 1 + +// Generic error codes are in the range -6700 to -6779. + +#define kGenericErrorBase -6700 // Starting error code for all generic errors. + +#define kUnknownErr -6700 +#define kOptionErr -6701 +#define kSelectorErr -6702 +#define kExecutionStateErr -6703 +#define kPathErr -6704 +#define kParamErr -6705 +#define kParamCountErr -6706 +#define kCommandErr -6707 +#define kIDErr -6708 +#define kStateErr -6709 +#define kRangeErr -6710 +#define kRequestErr -6711 +#define kResponseErr -6712 +#define kChecksumErr -6713 +#define kNotHandledErr -6714 +#define kVersionErr -6715 +#define kSignatureErr -6716 +#define kFormatErr -6717 +#define kNotInitializedErr -6718 +#define kAlreadyInitializedErr -6719 +#define kNotInUseErr -6720 +#define kInUseErr -6721 +#define kTimeoutErr -6722 +#define kCanceledErr -6723 +#define kAlreadyCanceledErr -6724 +#define kCannotCancelErr -6725 +#define kDeletedErr -6726 +#define kNotFoundErr -6727 +#define kNoMemoryErr -6728 +#define kNoResourcesErr -6729 +#define kDuplicateErr -6730 +#define kImmutableErr -6731 +#define kUnsupportedDataErr -6732 +#define kIntegrityErr -6733 +#define kIncompatibleErr -6734 +#define kUnsupportedErr -6735 +#define kUnexpectedErr -6736 +#define kValueErr -6737 +#define kNotReadableErr -6738 +#define kNotWritableErr -6739 +#define kBadReferenceErr -6740 +#define kFlagErr -6741 +#define kMalformedErr -6742 +#define kSizeErr -6743 +#define kNameErr -6744 +#define kNotReadyErr -6745 +#define kReadErr -6746 +#define kWriteErr -6747 +#define kMismatchErr -6748 +#define kDateErr -6749 +#define kUnderrunErr -6750 +#define kOverrunErr -6751 +#define kEndingErr -6752 +#define kConnectionErr -6753 +#define kAuthenticationErr -6754 +#define kOpenErr -6755 +#define kTypeErr -6756 +#define kSkipErr -6757 +#define kNoAckErr -6758 +#define kCollisionErr -6759 +#define kBackoffErr -6760 +#define kNoAddressAckErr -6761 +#define kBusyErr -6762 +#define kNoSpaceErr -6763 + +#define kGenericErrorEnd -6779 // Last generic error code (inclusive) + +#if 0 +#pragma mark == Mac Compatibility == +#endif + +//=========================================================================================================================== +// Mac Compatibility +//=========================================================================================================================== + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum Duration + + @abstract Type used to specify a duration of time. + + @constant kDurationImmediate Indicates no delay/wait time. + @constant kDurationMicrosecond Microsecond units. + @constant kDurationMillisecond Millisecond units. + @constant kDurationSecond Second units. + @constant kDurationMinute Minute units. + @constant kDurationHour Hour units. + @constant kDurationDay Day units. + @constant kDurationForever Infinite period of time (no timeout). + + @discussion + + Duration values are intended to be multiplied by the specific interval to achieve an actual duration. For example, + to wait for 5 seconds you would use "5 * kDurationSecond". +*/ + +#if( TARGET_LANGUAGE_C_LIKE ) + #if( !TARGET_OS_MAC ) + typedef int32_t Duration; + #endif +#endif + +#define kDurationImmediate 0L +#define kDurationMicrosecond -1L +#define kDurationMillisecond 1L +#define kDurationSecond ( 1000L * kDurationMillisecond ) +#define kDurationMinute ( 60L * kDurationSecond ) +#define kDurationHour ( 60L * kDurationMinute ) +#define kDurationDay ( 24L * kDurationHour ) +#define kDurationForever 0x7FFFFFFFL + +// Seconds <-> Minutes <-> Hours <-> Days <-> Weeks <-> Months <-> Years conversions + +#define kNanosecondsPerMicrosecond 1000 +#define kNanosecondsPerMillisecond 1000000 +#define kNanosecondsPerSecond 1000000000 +#define kMicrosecondsPerSecond 1000000 +#define kMicrosecondsPerMillisecond 1000 +#define kMillisecondsPerSecond 1000 +#define kSecondsPerMinute 60 +#define kSecondsPerHour ( 60 * 60 ) // 3600 +#define kSecondsPerDay ( 60 * 60 * 24 ) // 86400 +#define kSecondsPerWeek ( 60 * 60 * 24 * 7 ) // 604800 +#define kMinutesPerHour 60 +#define kMinutesPerDay ( 60 * 24 ) // 1440 +#define kHoursPerDay 24 +#define kDaysPerWeek 7 +#define kWeeksPerYear 52 +#define kMonthsPerYear 12 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined VersionStages + + @abstract NumVersion-style version stages. +*/ + +#define kVersionStageDevelopment 0x20 +#define kVersionStageAlpha 0x40 +#define kVersionStageBeta 0x60 +#define kVersionStageFinal 0x80 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function NumVersionBuild + + @abstract Builds a 32-bit Mac-style NumVersion value (e.g. NumVersionBuild( 1, 2, 3, kVersionStageBeta, 4 ) -> 1.2.3b4). +*/ + +#define NumVersionBuild( MAJOR, MINOR, BUGFIX, STAGE, REV ) \ + ( ( ( ( MAJOR ) & 0xFF ) << 24 ) | \ + ( ( ( MINOR ) & 0x0F ) << 20 ) | \ + ( ( ( BUGFIX ) & 0x0F ) << 16 ) | \ + ( ( ( STAGE ) & 0xFF ) << 8 ) | \ + ( ( ( REV ) & 0xFF ) ) ) + +#define NumVersionExtractMajor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 24 ) & 0xFF ) ) +#define NumVersionExtractMinorAndBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0xFF ) ) +#define NumVersionExtractMinor( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 20 ) & 0x0F ) ) +#define NumVersionExtractBugFix( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 16 ) & 0x0F ) ) +#define NumVersionExtractStage( VERSION ) ( (uint8_t)( ( ( VERSION ) >> 8 ) & 0xFF ) ) +#define NumVersionExtractRevision( VERSION ) ( (uint8_t)( ( VERSION ) & 0xFF ) ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function NumVersionCompare + + @abstract Compares two NumVersion values and returns the following values: + + left < right -> -1 + left > right -> 1 + left = right -> 0 +*/ + +#if( TARGET_LANGUAGE_C_LIKE ) + int NumVersionCompare( uint32_t inLeft, uint32_t inRight ); +#endif + +#if 0 +#pragma mark == Binary Constants == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined binary_4 + + @abstract Macro to generate an 4-bit constant using binary notation (e.g. binary_4( 1010 ) == 0xA). +*/ + +#define binary_4( a ) binary_4_hex_wrap( hex_digit4( a ) ) +#define binary_4_hex_wrap( a ) binary_4_hex( a ) +#define binary_4_hex( a ) ( 0x ## a ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined binary_8 + + @abstract Macro to generate an 8-bit constant using binary notation (e.g. binary_8( 01111011 ) == 0x7B). +*/ + +#define binary_8( a ) binary_8_hex_wrap( hex_digit8( a ) ) +#define binary_8_hex_wrap( a ) binary_8_hex( a ) +#define binary_8_hex( a ) ( 0x ## a ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined binary_16 + + @abstract Macro to generate an 16-bit constant using binary notation (e.g. binary_16( 01111011, 01111011 ) == 0x7B7B). +*/ + +#define binary_16( a, b ) binary_16_hex_wrap( hex_digit8( a ), hex_digit8( b ) ) +#define binary_16_hex_wrap( a, b ) binary_16_hex( a, b ) +#define binary_16_hex( a, b ) ( 0x ## a ## b ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined binary_32 + + @abstract Macro to generate an 32-bit constant using binary notation + (e.g. binary_32( 01111011, 01111011, 01111011, 01111011 ) == 0x7B7B7B7B). +*/ + +#define binary_32( a, b, c, d ) binary_32_hex_wrap( hex_digit8( a ), hex_digit8( b ), hex_digit8( c ), hex_digit8( d ) ) +#define binary_32_hex_wrap( a, b, c, d ) binary_32_hex( a, b, c, d ) +#define binary_32_hex( a, b, c, d ) ( 0x ## a ## b ## c ## d ) + +// Binary Constant Helpers + +#define hex_digit8( a ) HEX_DIGIT_ ## a +#define hex_digit4( a ) HEX_DIGIT_ ## 0000 ## a + +#define HEX_DIGIT_00000000 00 +#define HEX_DIGIT_00000001 01 +#define HEX_DIGIT_00000010 02 +#define HEX_DIGIT_00000011 03 +#define HEX_DIGIT_00000100 04 +#define HEX_DIGIT_00000101 05 +#define HEX_DIGIT_00000110 06 +#define HEX_DIGIT_00000111 07 +#define HEX_DIGIT_00001000 08 +#define HEX_DIGIT_00001001 09 +#define HEX_DIGIT_00001010 0A +#define HEX_DIGIT_00001011 0B +#define HEX_DIGIT_00001100 0C +#define HEX_DIGIT_00001101 0D +#define HEX_DIGIT_00001110 0E +#define HEX_DIGIT_00001111 0F +#define HEX_DIGIT_00010000 10 +#define HEX_DIGIT_00010001 11 +#define HEX_DIGIT_00010010 12 +#define HEX_DIGIT_00010011 13 +#define HEX_DIGIT_00010100 14 +#define HEX_DIGIT_00010101 15 +#define HEX_DIGIT_00010110 16 +#define HEX_DIGIT_00010111 17 +#define HEX_DIGIT_00011000 18 +#define HEX_DIGIT_00011001 19 +#define HEX_DIGIT_00011010 1A +#define HEX_DIGIT_00011011 1B +#define HEX_DIGIT_00011100 1C +#define HEX_DIGIT_00011101 1D +#define HEX_DIGIT_00011110 1E +#define HEX_DIGIT_00011111 1F +#define HEX_DIGIT_00100000 20 +#define HEX_DIGIT_00100001 21 +#define HEX_DIGIT_00100010 22 +#define HEX_DIGIT_00100011 23 +#define HEX_DIGIT_00100100 24 +#define HEX_DIGIT_00100101 25 +#define HEX_DIGIT_00100110 26 +#define HEX_DIGIT_00100111 27 +#define HEX_DIGIT_00101000 28 +#define HEX_DIGIT_00101001 29 +#define HEX_DIGIT_00101010 2A +#define HEX_DIGIT_00101011 2B +#define HEX_DIGIT_00101100 2C +#define HEX_DIGIT_00101101 2D +#define HEX_DIGIT_00101110 2E +#define HEX_DIGIT_00101111 2F +#define HEX_DIGIT_00110000 30 +#define HEX_DIGIT_00110001 31 +#define HEX_DIGIT_00110010 32 +#define HEX_DIGIT_00110011 33 +#define HEX_DIGIT_00110100 34 +#define HEX_DIGIT_00110101 35 +#define HEX_DIGIT_00110110 36 +#define HEX_DIGIT_00110111 37 +#define HEX_DIGIT_00111000 38 +#define HEX_DIGIT_00111001 39 +#define HEX_DIGIT_00111010 3A +#define HEX_DIGIT_00111011 3B +#define HEX_DIGIT_00111100 3C +#define HEX_DIGIT_00111101 3D +#define HEX_DIGIT_00111110 3E +#define HEX_DIGIT_00111111 3F +#define HEX_DIGIT_01000000 40 +#define HEX_DIGIT_01000001 41 +#define HEX_DIGIT_01000010 42 +#define HEX_DIGIT_01000011 43 +#define HEX_DIGIT_01000100 44 +#define HEX_DIGIT_01000101 45 +#define HEX_DIGIT_01000110 46 +#define HEX_DIGIT_01000111 47 +#define HEX_DIGIT_01001000 48 +#define HEX_DIGIT_01001001 49 +#define HEX_DIGIT_01001010 4A +#define HEX_DIGIT_01001011 4B +#define HEX_DIGIT_01001100 4C +#define HEX_DIGIT_01001101 4D +#define HEX_DIGIT_01001110 4E +#define HEX_DIGIT_01001111 4F +#define HEX_DIGIT_01010000 50 +#define HEX_DIGIT_01010001 51 +#define HEX_DIGIT_01010010 52 +#define HEX_DIGIT_01010011 53 +#define HEX_DIGIT_01010100 54 +#define HEX_DIGIT_01010101 55 +#define HEX_DIGIT_01010110 56 +#define HEX_DIGIT_01010111 57 +#define HEX_DIGIT_01011000 58 +#define HEX_DIGIT_01011001 59 +#define HEX_DIGIT_01011010 5A +#define HEX_DIGIT_01011011 5B +#define HEX_DIGIT_01011100 5C +#define HEX_DIGIT_01011101 5D +#define HEX_DIGIT_01011110 5E +#define HEX_DIGIT_01011111 5F +#define HEX_DIGIT_01100000 60 +#define HEX_DIGIT_01100001 61 +#define HEX_DIGIT_01100010 62 +#define HEX_DIGIT_01100011 63 +#define HEX_DIGIT_01100100 64 +#define HEX_DIGIT_01100101 65 +#define HEX_DIGIT_01100110 66 +#define HEX_DIGIT_01100111 67 +#define HEX_DIGIT_01101000 68 +#define HEX_DIGIT_01101001 69 +#define HEX_DIGIT_01101010 6A +#define HEX_DIGIT_01101011 6B +#define HEX_DIGIT_01101100 6C +#define HEX_DIGIT_01101101 6D +#define HEX_DIGIT_01101110 6E +#define HEX_DIGIT_01101111 6F +#define HEX_DIGIT_01110000 70 +#define HEX_DIGIT_01110001 71 +#define HEX_DIGIT_01110010 72 +#define HEX_DIGIT_01110011 73 +#define HEX_DIGIT_01110100 74 +#define HEX_DIGIT_01110101 75 +#define HEX_DIGIT_01110110 76 +#define HEX_DIGIT_01110111 77 +#define HEX_DIGIT_01111000 78 +#define HEX_DIGIT_01111001 79 +#define HEX_DIGIT_01111010 7A +#define HEX_DIGIT_01111011 7B +#define HEX_DIGIT_01111100 7C +#define HEX_DIGIT_01111101 7D +#define HEX_DIGIT_01111110 7E +#define HEX_DIGIT_01111111 7F +#define HEX_DIGIT_10000000 80 +#define HEX_DIGIT_10000001 81 +#define HEX_DIGIT_10000010 82 +#define HEX_DIGIT_10000011 83 +#define HEX_DIGIT_10000100 84 +#define HEX_DIGIT_10000101 85 +#define HEX_DIGIT_10000110 86 +#define HEX_DIGIT_10000111 87 +#define HEX_DIGIT_10001000 88 +#define HEX_DIGIT_10001001 89 +#define HEX_DIGIT_10001010 8A +#define HEX_DIGIT_10001011 8B +#define HEX_DIGIT_10001100 8C +#define HEX_DIGIT_10001101 8D +#define HEX_DIGIT_10001110 8E +#define HEX_DIGIT_10001111 8F +#define HEX_DIGIT_10010000 90 +#define HEX_DIGIT_10010001 91 +#define HEX_DIGIT_10010010 92 +#define HEX_DIGIT_10010011 93 +#define HEX_DIGIT_10010100 94 +#define HEX_DIGIT_10010101 95 +#define HEX_DIGIT_10010110 96 +#define HEX_DIGIT_10010111 97 +#define HEX_DIGIT_10011000 98 +#define HEX_DIGIT_10011001 99 +#define HEX_DIGIT_10011010 9A +#define HEX_DIGIT_10011011 9B +#define HEX_DIGIT_10011100 9C +#define HEX_DIGIT_10011101 9D +#define HEX_DIGIT_10011110 9E +#define HEX_DIGIT_10011111 9F +#define HEX_DIGIT_10100000 A0 +#define HEX_DIGIT_10100001 A1 +#define HEX_DIGIT_10100010 A2 +#define HEX_DIGIT_10100011 A3 +#define HEX_DIGIT_10100100 A4 +#define HEX_DIGIT_10100101 A5 +#define HEX_DIGIT_10100110 A6 +#define HEX_DIGIT_10100111 A7 +#define HEX_DIGIT_10101000 A8 +#define HEX_DIGIT_10101001 A9 +#define HEX_DIGIT_10101010 AA +#define HEX_DIGIT_10101011 AB +#define HEX_DIGIT_10101100 AC +#define HEX_DIGIT_10101101 AD +#define HEX_DIGIT_10101110 AE +#define HEX_DIGIT_10101111 AF +#define HEX_DIGIT_10110000 B0 +#define HEX_DIGIT_10110001 B1 +#define HEX_DIGIT_10110010 B2 +#define HEX_DIGIT_10110011 B3 +#define HEX_DIGIT_10110100 B4 +#define HEX_DIGIT_10110101 B5 +#define HEX_DIGIT_10110110 B6 +#define HEX_DIGIT_10110111 B7 +#define HEX_DIGIT_10111000 B8 +#define HEX_DIGIT_10111001 B9 +#define HEX_DIGIT_10111010 BA +#define HEX_DIGIT_10111011 BB +#define HEX_DIGIT_10111100 BC +#define HEX_DIGIT_10111101 BD +#define HEX_DIGIT_10111110 BE +#define HEX_DIGIT_10111111 BF +#define HEX_DIGIT_11000000 C0 +#define HEX_DIGIT_11000001 C1 +#define HEX_DIGIT_11000010 C2 +#define HEX_DIGIT_11000011 C3 +#define HEX_DIGIT_11000100 C4 +#define HEX_DIGIT_11000101 C5 +#define HEX_DIGIT_11000110 C6 +#define HEX_DIGIT_11000111 C7 +#define HEX_DIGIT_11001000 C8 +#define HEX_DIGIT_11001001 C9 +#define HEX_DIGIT_11001010 CA +#define HEX_DIGIT_11001011 CB +#define HEX_DIGIT_11001100 CC +#define HEX_DIGIT_11001101 CD +#define HEX_DIGIT_11001110 CE +#define HEX_DIGIT_11001111 CF +#define HEX_DIGIT_11010000 D0 +#define HEX_DIGIT_11010001 D1 +#define HEX_DIGIT_11010010 D2 +#define HEX_DIGIT_11010011 D3 +#define HEX_DIGIT_11010100 D4 +#define HEX_DIGIT_11010101 D5 +#define HEX_DIGIT_11010110 D6 +#define HEX_DIGIT_11010111 D7 +#define HEX_DIGIT_11011000 D8 +#define HEX_DIGIT_11011001 D9 +#define HEX_DIGIT_11011010 DA +#define HEX_DIGIT_11011011 DB +#define HEX_DIGIT_11011100 DC +#define HEX_DIGIT_11011101 DD +#define HEX_DIGIT_11011110 DE +#define HEX_DIGIT_11011111 DF +#define HEX_DIGIT_11100000 E0 +#define HEX_DIGIT_11100001 E1 +#define HEX_DIGIT_11100010 E2 +#define HEX_DIGIT_11100011 E3 +#define HEX_DIGIT_11100100 E4 +#define HEX_DIGIT_11100101 E5 +#define HEX_DIGIT_11100110 E6 +#define HEX_DIGIT_11100111 E7 +#define HEX_DIGIT_11101000 E8 +#define HEX_DIGIT_11101001 E9 +#define HEX_DIGIT_11101010 EA +#define HEX_DIGIT_11101011 EB +#define HEX_DIGIT_11101100 EC +#define HEX_DIGIT_11101101 ED +#define HEX_DIGIT_11101110 EE +#define HEX_DIGIT_11101111 EF +#define HEX_DIGIT_11110000 F0 +#define HEX_DIGIT_11110001 F1 +#define HEX_DIGIT_11110010 F2 +#define HEX_DIGIT_11110011 F3 +#define HEX_DIGIT_11110100 F4 +#define HEX_DIGIT_11110101 F5 +#define HEX_DIGIT_11110110 F6 +#define HEX_DIGIT_11110111 F7 +#define HEX_DIGIT_11111000 F8 +#define HEX_DIGIT_11111001 F9 +#define HEX_DIGIT_11111010 FA +#define HEX_DIGIT_11111011 FB +#define HEX_DIGIT_11111100 FC +#define HEX_DIGIT_11111101 FD +#define HEX_DIGIT_11111110 FE +#define HEX_DIGIT_11111111 FF + +#if 0 +#pragma mark == Debugging == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function CommonServicesTest + + @abstract Unit test. +*/ + +#if( DEBUG ) + #if( TARGET_LANGUAGE_C_LIKE ) + OSStatus CommonServicesTest( void ); + #endif +#endif + +#ifdef __cplusplus + } +#endif + +#endif // __COMMON_SERVICES__ diff --git a/mDNSWindows/DNSSD.c b/mDNSWindows/DNSSD.c new file mode 100644 index 0000000..24bc560 --- /dev/null +++ b/mDNSWindows/DNSSD.c @@ -0,0 +1,1720 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSSD.c,v $ +Revision 1.3 2004/05/11 03:08:53 bradley +Updated TXT Record API based on latest proposal. This still includes dynamic TXT record building for +a final CVS snapshot for private libraries before this functionality is removed from the public API. + +Revision 1.2 2004/05/03 10:34:24 bradley +Implemented preliminary version of the TXTRecord API. + +Revision 1.1 2004/01/30 02:45:21 bradley +High-level implementation of the DNS-SD API. Supports both "direct" (compiled-in mDNSCore) and "client" +(IPC<->service) usage. Conditionals can exclude either "direct" or "client" to reduce code size. + +*/ + +#include +#include +#include +#include + +#include "CommonServices.h" +#include "DebugServices.h" + +#include "DNSSDDirect.h" +#include "RMxClient.h" + +#include "DNSSD.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[DNS-SD] " + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +DEBUG_LOCAL int DomainEndsInDot( const char *dom ); + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +#if( DNS_SD_CLIENT_ENABLED ) + const char * gDNSSDServer = NULL; + DEBUG_LOCAL bool gDNSSDServerCompatible = false; +#endif + +#if 0 +#pragma mark == General == +#endif + +//=========================================================================================================================== +// DNSServiceInitialize +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceInitialize( DNSServiceInitializeFlags inFlags, int inCacheEntryCount ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + err = RMxClientInitialize(); + if( err == kNoErr ) + { + // Perform a version check to see if the server is compatible. + + if( !( inFlags & kDNSServiceInitializeFlagsNoServerCheck ) ) + { + err = DNSServiceCheckVersion(); + if( err == kNoErr ) + { + goto exit; + } + } + else + { + // Version check disabled so just assume the server is compatible. + + gDNSSDServerCompatible = true; + goto exit; + } + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + #if( DNS_SD_CLIENT_ENABLED ) + dlog( kDebugLevelNotice, DEBUG_NAME "server missing or incompatible...falling back to direct implementation\n" ); + #endif + err = DNSServiceInitialize_direct( inFlags, inCacheEntryCount ); + goto exit; + #else + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inCacheEntryCount ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceFinalize +//=========================================================================================================================== + +void DNSServiceFinalize( void ) +{ + #if( DNS_SD_CLIENT_ENABLED ) + RMxClientFinalize(); + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + DNSServiceFinalize_direct(); + #endif +} + +//=========================================================================================================================== +// DNSServiceCheckVersion +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceCheckVersion( void ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + err = DNSServiceCheckVersion_client( gDNSSDServer ); + if( err == kNoErr ) + { + gDNSSDServerCompatible = true; + } + #else + err = kUnsupportedErr; + #endif + + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Properties == +#endif + +//=========================================================================================================================== +// DNSServiceCopyProperty +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceCopyProperty( DNSPropertyCode inCode, DNSPropertyData *outData ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceCopyProperty_client( gDNSSDServer, inCode, outData ); + goto exit; + } + #else + DEBUG_UNUSED( inCode ); + DEBUG_UNUSED( outData ); + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = kUnsupportedErr; + goto exit; + #else + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceReleaseProperty +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceReleaseProperty( DNSPropertyData *inData ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceReleaseProperty_client( inData ); + goto exit; + } + #else + DEBUG_UNUSED( inData ); + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = kUnsupportedErr; + goto exit; + #else + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Unix Domain Socket Access == +#endif + +//=========================================================================================================================== +// DNSServiceRefSockFD +//=========================================================================================================================== + +int DNSServiceRefSockFD( DNSServiceRef inRef ) +{ + DEBUG_UNUSED( inRef ); + + dlog( kDebugLevelError, "DNSServiceRefSockFD is not supported\n" ); + return( -1 ); +} + +//=========================================================================================================================== +// DNSServiceProcessResult +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceProcessResult( DNSServiceRef inRef ) +{ + DEBUG_UNUSED( inRef ); + + dlog( kDebugLevelError, "DNSServiceProcessResult is not supported\n" ); + return( kDNSServiceErr_Unsupported ); +} + +//=========================================================================================================================== +// DNSServiceRefDeallocate +//=========================================================================================================================== + +void DNSServiceRefDeallocate( DNSServiceRef inRef ) +{ + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + DNSServiceRefDeallocate_client( inRef ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + DNSServiceRefDeallocate_direct( inRef ); + goto exit; + #else + DEBUG_UNUSED( inRef ); + + goto exit; + #endif + +exit: + return; +} + +#if 0 +#pragma mark - +#pragma mark == Domain Enumeration == +#endif + +//=========================================================================================================================== +// DNSServiceEnumerateDomains +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceEnumerateDomains( + DNSServiceRef * outRef, + const DNSServiceFlags inFlags, + const uint32_t inInterfaceIndex, + const DNSServiceDomainEnumReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceEnumerateDomains_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inCallBack, inContext ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceEnumerateDomains_direct( outRef, inFlags, inInterfaceIndex, inCallBack, inContext ); + goto exit; + #else + DEBUG_UNUSED( outRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inCallBack ); + DEBUG_UNUSED( inContext ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Service Registration == +#endif + +//=========================================================================================================================== +// DNSServiceRegister +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceRegister( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + const char * inHost, + uint16_t inPort, + uint16_t inTXTSize, + const void * inTXT, + DNSServiceRegisterReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceRegister_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inName, inType, inDomain, + inHost, inPort, inTXTSize, inTXT, inCallBack, inContext ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceRegister_direct( outRef, inFlags, inInterfaceIndex, inName, inType, inDomain, inHost, inPort, + inTXTSize, inTXT, inCallBack, inContext ); + goto exit; + #else + DEBUG_UNUSED( outRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inName ); + DEBUG_UNUSED( inType ); + DEBUG_UNUSED( inDomain ); + DEBUG_UNUSED( inHost ); + DEBUG_UNUSED( inPort ); + DEBUG_UNUSED( inTXTSize ); + DEBUG_UNUSED( inTXT ); + DEBUG_UNUSED( inCallBack ); + DEBUG_UNUSED( inContext ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceAddRecord +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceAddRecord( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint16_t inRRType, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceAddRecord_client( inRef, outRecordRef, inFlags, inRRType, inRDataSize, inRData, inTTL ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceAddRecord_direct( inRef, outRecordRef, inFlags, inRRType, inRDataSize, inRData, inTTL ); + goto exit; + #else + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( outRecordRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inRRType ); + DEBUG_UNUSED( inRDataSize ); + DEBUG_UNUSED( inRData ); + DEBUG_UNUSED( inTTL ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceUpdateRecord +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceUpdateRecord( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceUpdateRecord_client( inRef, inRecordRef, inFlags, inRDataSize, inRData, inTTL ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceUpdateRecord_direct( inRef, inRecordRef, inFlags, inRDataSize, inRData, inTTL ); + goto exit; + #else + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( inRecordRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inRDataSize ); + DEBUG_UNUSED( inRData ); + DEBUG_UNUSED( inTTL ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceRemoveRecord +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceRemoveRecord( DNSServiceRef inRef, DNSRecordRef inRecordRef, DNSServiceFlags inFlags ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceRemoveRecord_client( inRef, inRecordRef, inFlags ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceRemoveRecord_direct( inRef, inRecordRef, inFlags ); + goto exit; + #else + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( inRecordRef ); + DEBUG_UNUSED( inFlags ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Service Discovery == +#endif + +//=========================================================================================================================== +// DNSServiceBrowse +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceBrowse( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inType, + const char * inDomain, + DNSServiceBrowseReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceBrowse_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inType, inDomain, + inCallBack, inContext ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceBrowse_direct( outRef, inFlags, inInterfaceIndex, inType, inDomain, inCallBack, inContext ); + goto exit; + #else + DEBUG_UNUSED( outRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inType ); + DEBUG_UNUSED( inDomain ); + DEBUG_UNUSED( inCallBack ); + DEBUG_UNUSED( inContext ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceResolve +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceResolve( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + DNSServiceResolveReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceResolve_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inName, inType, inDomain, + inCallBack, inContext ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceResolve_direct( outRef, inFlags, inInterfaceIndex, inName, inType, inDomain, inCallBack, inContext ); + goto exit; + #else + DEBUG_UNUSED( outRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inName ); + DEBUG_UNUSED( inType ); + DEBUG_UNUSED( inDomain ); + DEBUG_UNUSED( inCallBack ); + DEBUG_UNUSED( inContext ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Special Purpose == +#endif + +//=========================================================================================================================== +// DNSServiceConstructFullName +// +// Copied from dnssd_clientstub.c with minimal changes to support NULL/empty name, type, and domain. +//=========================================================================================================================== + +int DNSServiceConstructFullName( char *fullName, const char *service, const char *regtype, const char *domain ) +{ + size_t len; + unsigned char c; + char *fn = fullName; + const char *s = service; + const char *r = regtype; + const char *d = domain; + + if (service && *service) + { + while(*s) + { + c = (unsigned char) *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 = (unsigned char)('0' + (c % 10)); + } + *fn++ = (char) c; + } + *fn++ = '.'; + } + + if (regtype && *regtype) + { + len = strlen(regtype); + if (DomainEndsInDot(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 (!DomainEndsInDot(regtype)) *fn++ = '.'; + } + + if (!domain || !(*domain)) + { + domain = "local."; + d = domain; + } + len = strlen(domain); + if (!len) return -1; + while(*d) + *fn++ = *d++; + if (!DomainEndsInDot(domain)) *fn++ = '.'; + *fn = '\0'; + return 0; +} + +//=========================================================================================================================== +// DNSServiceCreateConnection +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceCreateConnection( DNSServiceRef *outRef ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceCreateConnection_client( outRef, gDNSSDServer ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceCreateConnection_direct( outRef ); + goto exit; + #else + DEBUG_UNUSED( outRef ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceRegisterRecord +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceRegisterRecord( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + DNSServiceRegisterRecordReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceRegisterRecord_client( inRef, outRecordRef, inFlags, inInterfaceIndex, inName, + inRRType, inRRClass, inRDataSize, inRData, inTTL, inCallBack, inContext ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceRegisterRecord_direct( inRef, outRecordRef, inFlags, inInterfaceIndex, inName, + inRRType, inRRClass, inRDataSize, inRData, inTTL, inCallBack, inContext ); + goto exit; + #else + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( outRecordRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inName ); + DEBUG_UNUSED( inRRType ); + DEBUG_UNUSED( inRRClass ); + DEBUG_UNUSED( inRDataSize ); + DEBUG_UNUSED( inRData ); + DEBUG_UNUSED( inTTL ); + DEBUG_UNUSED( inCallBack ); + DEBUG_UNUSED( inContext ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceQueryRecord +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceQueryRecord( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + DNSServiceQueryRecordReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + err = DNSServiceQueryRecord_client( outRef, gDNSSDServer, inFlags, inInterfaceIndex, inName, inRRType, inRRClass, + inCallBack, inContext ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + err = DNSServiceQueryRecord_direct( outRef, inFlags, inInterfaceIndex, inName, inRRType, inRRClass, + inCallBack, inContext ); + goto exit; + #else + DEBUG_UNUSED( outRef ); + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inName ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inName ); + DEBUG_UNUSED( inRRType ); + DEBUG_UNUSED( inRRClass ); + DEBUG_UNUSED( inCallBack ); + DEBUG_UNUSED( inContext ); + + err = kUnsupportedErr; + goto exit; + #endif + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceReconfirmRecord +//=========================================================================================================================== + +void + DNSServiceReconfirmRecord( + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData ) +{ + #if( DNS_SD_CLIENT_ENABLED ) + if( gDNSSDServerCompatible ) + { + DNSServiceReconfirmRecord_client( gDNSSDServer, inFlags, inInterfaceIndex, inName, inRRType, inRRClass, + inRDataSize, inRData ); + goto exit; + } + #endif + + #if( DNS_SD_DIRECT_ENABLED ) + DNSServiceReconfirmRecord_direct( inFlags, inInterfaceIndex, inName, inRRType, inRRClass, inRDataSize, inRData ); + goto exit; + #else + DEBUG_UNUSED( inFlags ); + DEBUG_UNUSED( inInterfaceIndex ); + DEBUG_UNUSED( inName ); + DEBUG_UNUSED( inRRType ); + DEBUG_UNUSED( inRRClass ); + DEBUG_UNUSED( inRDataSize ); + DEBUG_UNUSED( inRData ); + + goto exit; + #endif + +exit: + return; +} + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +//=========================================================================================================================== +// DomainEndsInDot +// +// Copied from dnssd_clientstub.c. +//=========================================================================================================================== + +DEBUG_LOCAL int DomainEndsInDot( const char *dom ) +{ + check( 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 == '.'); +} + +#if 0 +#pragma mark - +#pragma mark == TXT Record Building == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +typedef struct _TXTRecordRef_t _TXTRecordRef_t; +struct _TXTRecordRef_t +{ + uint8_t * txt; + uint16_t size; +}; + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +DEBUG_LOCAL DNSServiceErrorType + TXTRecordGetValuePtrInternal( + uint16_t inTXTSize, + const void * inTXTBytes, + const char * inKey, + uint8_t ** outItem, + uint16_t * outItemSize, + const void ** outValue, + uint8_t * outValueSize ); + +DEBUG_LOCAL DNSServiceErrorType + TXTRecordGetItemAtIndexInternal( + uint16_t inTXTSize, + const void * inTXTBytes, + uint16_t inIndex, + uint8_t ** outItem, + uint16_t * outItemSize, + char * inKeyBuffer, + const void ** outValue, + uint8_t * outValueSize ); + +DEBUG_LOCAL int TXTRecordMemEqv( const void *inLeft, size_t inLeftSize, const void *inRight, size_t inRightSize ); + +//=========================================================================================================================== +// TXTRecordCreate +//=========================================================================================================================== + +DNSServiceErrorType TXTRecordCreate( TXTRecordRef *outRef ) +{ + DNSServiceErrorType err; + TXTRecordRef obj; + + require_action_expect( outRef, exit, err = kDNSServiceErr_BadParam ); + + obj = (TXTRecordRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + *outRef = obj; + err = kDNSServiceErr_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// TXTRecordDeallocate +//=========================================================================================================================== + +void TXTRecordDeallocate( TXTRecordRef inRef ) +{ + check( inRef ); + if( inRef ) + { + if( inRef->txt ) free( inRef->txt ); + free( inRef ); + } +} + +//=========================================================================================================================== +// TXTRecordSetValue +//=========================================================================================================================== + +DNSServiceErrorType TXTRecordSetValue( TXTRecordRef inRef, const char *inKey, uint8_t inValueSize, const void *inValue ) +{ + DNSServiceErrorType err; + const char * p; + size_t keySize; + uint8_t * item; + uint8_t * itemEnd; + uintptr_t itemOffset; + uint16_t oldItemSize; + uint16_t newItemSize; + uint16_t delta; + uint8_t * txt; + + require_action_expect( inRef, exit, err = kDNSServiceErr_BadParam ); + require_action_expect( inKey, exit, err = kDNSServiceErr_BadParam ); + require_action_expect( inValue || ( inValueSize == 0 ), exit, err = kDNSServiceErr_BadParam ); + + // Make sure the key is printable US-ASCII values (0x20-0x7E), excluding '=' (0x3D). + + for( p = inKey; *p != '\0'; ++p ) + { + if( ( *p < 0x20 ) || ( *p > 0x7E ) || ( *p == 0x3D ) ) + { + break; + } + } + keySize = (size_t)( p - inKey ); + require_action( ( keySize > 0 ) && ( keySize <= 255 ) && ( *p == '\0' ), exit, err = kDNSServiceErr_Invalid ); + + // Make sure the total item size does not exceed 255 bytes. + + newItemSize = (uint16_t) keySize; + if( inValue ) newItemSize += 1; // Add '=' (only if there is a non-NULL value) + newItemSize = (uint16_t)( newItemSize + inValueSize ); + require_action( newItemSize <= 255, exit, err = kDNSServiceErr_Invalid ); + + // Search for an existing item with the same key. If found, replace its value. Otherwise, append a new item. + + err = TXTRecordGetValuePtrInternal( inRef->size, inRef->txt, inKey, &item, &oldItemSize, NULL, NULL ); + if( err == kDNSServiceErr_NoError ) + { + if( newItemSize > oldItemSize ) + { + // Expand the buffer then shift subsequent item(s) forward to make room for the larger value. + // Note: this saves off and restores the item pointer since the pointer is invalidated by realloc. + + itemOffset = (uintptr_t)( item - inRef->txt ); + delta = (uint16_t)( newItemSize - oldItemSize ); + txt = (uint8_t *) realloc( inRef->txt, (size_t)( inRef->size + delta ) ); + require_action( txt, exit, err = kDNSServiceErr_NoMemory ); + + item = txt + itemOffset; + itemEnd = item + ( 1 + oldItemSize ); + memmove( item + ( 1 + newItemSize ), itemEnd, (size_t)( ( inRef->txt + inRef->size ) - itemEnd ) ); + inRef->txt = txt; + inRef->size = (uint16_t)( inRef->size + delta ); + } + else if( newItemSize < oldItemSize ) + { + // Shift subsequent item(s) backward to take up the slack then shrink the buffer to fit. + // Note: this saves off and restores the item pointer since the pointer is invalidated by realloc. + + itemEnd = item + ( 1 + oldItemSize ); + memmove( item + ( 1 + newItemSize ), itemEnd, (size_t)( ( inRef->txt + inRef->size ) - itemEnd ) ); + + itemOffset = (uintptr_t)( item - inRef->txt ); + inRef->size -= ( oldItemSize - newItemSize ); + txt = (uint8_t *) realloc( inRef->txt, inRef->size ); + require_action( txt, exit, err = kDNSServiceErr_NoMemory ); + + item = txt + itemOffset; + inRef->txt = txt; + } + } + else + { + // Resize the buffer to hold the new item. + + delta = (uint16_t)( 1 + newItemSize ); + txt = (uint8_t *) realloc( inRef->txt, (size_t)( inRef->size + delta ) ); + require_action( txt, exit, err = kDNSServiceErr_NoMemory ); + + item = txt + inRef->size; + inRef->txt = txt; + inRef->size = (uint16_t)( inRef->size + delta ); + } + + // Write the new key/value pair to the TXT record in the form key[=]. + // Note: This always writes the entire item to handle case changes in the key. + + *item++ = (uint8_t) newItemSize; + memcpy( item, inKey, keySize ); + item += keySize; + if( inValue ) *item++ = '='; // Add '=' (only if there is a non-NULL value) + memcpy( item, inValue, inValueSize ); + err = kDNSServiceErr_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// TXTRecordRemoveValue +//=========================================================================================================================== + +DNSServiceErrorType TXTRecordRemoveValue( TXTRecordRef inRef, const char *inKey ) +{ + DNSServiceErrorType err; + uint8_t * item; + uint8_t * itemEnd; + uint16_t size; + uint8_t * txt; + uint8_t * txtEnd; + + err = TXTRecordGetValuePtrInternal( inRef->size, inRef->txt, inKey, &item, &size, NULL, NULL ); + require_noerr_quiet( err, exit ); + + // Shift subsequent item(s) back to take up the slack of removing the item. + + itemEnd = item + ( 1 + size ); + txtEnd = inRef->txt + inRef->size; + if( itemEnd < txtEnd ) + { + memmove( item, itemEnd, (size_t)( txtEnd - itemEnd ) ); + } + + // Shrink the buffer to fit. If size goes to 0, use free because realloc with size 0 is not consistent across platforms. + + inRef->size = (uint16_t)( inRef->size - ( (uint16_t)( itemEnd - item ) ) ); + if( inRef->size > 0 ) + { + txt = (uint8_t *) realloc( inRef->txt, inRef->size ); + require_action_quiet( txt, exit, err = kDNSServiceErr_Unknown ); + } + else + { + free( inRef->txt ); + txt = NULL; + } + inRef->txt = txt; + +exit: + return( err ); +} + +//=========================================================================================================================== +// TXTRecordGetLength +//=========================================================================================================================== + +uint16_t TXTRecordGetLength( TXTRecordRef inRef ) +{ + check( inRef ); + + if( inRef && inRef->txt ) + { + return( inRef->size ); + } + return( 0 ); +} + +//=========================================================================================================================== +// TXTRecordGetBytesPtr +//=========================================================================================================================== + +const void * TXTRecordGetBytesPtr( TXTRecordRef inRef ) +{ + check( inRef ); + + if( inRef && inRef->txt ) + { + return( inRef->txt ); + } + return( NULL ); +} + +#if 0 +#pragma mark - +#pragma mark == TXT Record Parsing == +#endif + +//=========================================================================================================================== +// TXTRecordGetValuePtr +//=========================================================================================================================== + +DNSServiceErrorType + TXTRecordGetValuePtr( + uint16_t inTXTSize, + const void * inTXTBytes, + const char * inKey, + const void ** outValue, + uint8_t * outValueSize ) +{ + return( TXTRecordGetValuePtrInternal( inTXTSize, inTXTBytes, inKey, NULL, NULL, outValue, outValueSize ) ); +} + +//=========================================================================================================================== +// TXTRecordGetCount +//=========================================================================================================================== + +uint16_t TXTRecordGetCount( uint16_t inTXTSize, const void *inTXTBytes ) +{ + uint16_t n; + const uint8_t * p; + const uint8_t * q; + + require_action( inTXTBytes, exit, n = 0 ); + + n = 0; + p = (const uint8_t *) inTXTBytes; + q = p + inTXTSize; + while( p < q ) + { + ++n; + p += ( 1 + *p ); + require_action( p <= q, exit, n = 0 ); + } + +exit: + return( n ); +} + +//=========================================================================================================================== +// TXTRecordGetItemAtIndex +//=========================================================================================================================== + +DNSServiceErrorType + TXTRecordGetItemAtIndex( + uint16_t inTXTSize, + const void * inTXTBytes, + uint16_t inIndex, + char * inKeyBuffer, + const void ** outValue, + uint8_t * outValueSize ) +{ + return( TXTRecordGetItemAtIndexInternal( inTXTSize, inTXTBytes, inIndex, NULL, NULL, inKeyBuffer, outValue, outValueSize ) ); +} + +#if 0 +#pragma mark - +#pragma mark == TXT Record Internal == +#endif + +//=========================================================================================================================== +// TXTRecordGetValuePtrInternal +//=========================================================================================================================== + +DEBUG_LOCAL DNSServiceErrorType + TXTRecordGetValuePtrInternal( + uint16_t inTXTSize, + const void * inTXTBytes, + const char * inKey, + uint8_t ** outItem, + uint16_t * outItemSize, + const void ** outValue, + uint8_t * outValueSize ) +{ + DNSServiceErrorType err; + size_t keySize; + const uint8_t * p; + const uint8_t * q; + const uint8_t * r; + const uint8_t * key; + const uint8_t * keyEnd; + const uint8_t * value; + const uint8_t * valueEnd; + + require_action_quiet( inTXTBytes, exit, err = kDNSServiceErr_NoSuchKey ); + require_action_expect( inKey, exit, err = kDNSServiceErr_BadParam ); + keySize = strlen( inKey ); + + // The following initializations are not necessary, but some compilers warn because they cannot detect it. + + keyEnd = NULL; + value = NULL; + valueEnd = NULL; + + // Find the item with the specified key. + + p = (const uint8_t *) inTXTBytes; + q = p + inTXTSize; + while( p < q ) + { + // Parse the key/value tokens. No '=' means the key takes up the entire item. + + r = p + ( 1 + *p ); + require_action( r <= q, exit, err = kDNSServiceErr_Invalid ); + + key = p + 1; + keyEnd = (const uint8_t *) memchr( key, '=', *p ); + if( keyEnd ) + { + value = keyEnd + 1; + } + else + { + keyEnd = r; + value = r; + } + valueEnd = r; + if( TXTRecordMemEqv( key, (size_t)( keyEnd - key ), inKey, keySize ) == 0 ) + { + break; + } + + p = r; + check( p <= q ); + } + require_action_quiet( p < q, exit, err = kDNSServiceErr_NoSuchKey ); + + // Fill in the results the caller wants. + + if( outItem ) *outItem = (uint8_t *) p; + if( outItemSize ) *outItemSize = *p; + if( outValue ) *outValue = ( value == keyEnd ) ? NULL : value; // NULL value ptr means no value. + if( outValueSize ) *outValueSize = (uint8_t)( valueEnd - value ); + err = kDNSServiceErr_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// TXTRecordGetItemAtIndexInternal +//=========================================================================================================================== + +DEBUG_LOCAL DNSServiceErrorType + TXTRecordGetItemAtIndexInternal( + uint16_t inTXTSize, + const void * inTXTBytes, + uint16_t inIndex, + uint8_t ** outItem, + uint16_t * outItemSize, + char * inKeyBuffer, + const void ** outValue, + uint8_t * outValueSize ) +{ + DNSServiceErrorType err; + uint16_t n; + const uint8_t * p; + const uint8_t * q; + const uint8_t * r; + const uint8_t * key; + const uint8_t * keyEnd; + const uint8_t * value; + const uint8_t * valueEnd; + + require_action_quiet( inTXTBytes, exit, err = kDNSServiceErr_Invalid ); + + // Find the Nth item in the TXT record. + + n = 0; + p = (const uint8_t *) inTXTBytes; + q = p + inTXTSize; + while( p < q ) + { + if( n == inIndex ) break; + ++n; + + p += ( 1 + *p ); + require_action( p <= q, exit, err = kDNSServiceErr_Invalid ); + } + require_action_quiet( p < q, exit, err = kDNSServiceErr_Invalid ); + + // Item found. Parse the key/value tokens. No '=' means the key takes up the entire item. + + r = p + ( 1 + *p ); + require_action( r <= q, exit, err = kDNSServiceErr_Invalid ); + + key = p + 1; + keyEnd = (const uint8_t *) memchr( key, '=', *p ); + if( keyEnd ) + { + value = keyEnd + 1; + } + else + { + keyEnd = r; + value = r; + } + valueEnd = r; + + // Fill in the results the caller wants. + + if( outItem ) *outItem = (uint8_t *) p; + if( outItemSize ) *outItemSize = *p; + if( inKeyBuffer ) + { + n = (uint16_t)( keyEnd - key ); + memcpy( inKeyBuffer, key, n ); + inKeyBuffer[ n ] = '\0'; + } + if( outValue ) *outValue = ( value == keyEnd ) ? NULL : value; // NULL value ptr means no value. + if( outValueSize ) *outValueSize = (uint8_t)( valueEnd - value ); + err = kDNSServiceErr_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// TXTRecordMemEqv +//=========================================================================================================================== + +DEBUG_LOCAL int TXTRecordMemEqv( const void *inLeft, size_t inLeftSize, const void *inRight, size_t inRightSize ) +{ + const uint8_t * p1; + const uint8_t * p2; + const uint8_t * end; + int c1; + int c2; + + if( inLeftSize < inRightSize ) return( -1 ); + if( inLeftSize > inRightSize ) return( 1 ); + + p1 = (const uint8_t *) inLeft; + p2 = (const uint8_t *) inRight; + end = p1 + inLeftSize; + while( p1 < end ) + { + c1 = tolower( *p1 ); + c2 = tolower( *p2 ); + if( c1 < c2 ) return( -1 ); + if( c1 > c2 ) return( 1 ); + + ++p1; + ++p2; + } + return( 0 ); +} + +#if( DEBUG ) +//=========================================================================================================================== +// TXTRecordTest +//=========================================================================================================================== + +DNSServiceErrorType TXTRecordTest( void ); + +DNSServiceErrorType TXTRecordTest( void ) +{ + DNSServiceErrorType err; + const char * s; + TXTRecordRef ref; + const void * txt; + uint16_t size; + uint16_t n; + char key[ 256 ]; + const void * value; + uint8_t valueSize; + + // Create Existing Test + + s = "\014Anon Allowed" + "\010Options=" + "\025InstalledPlugins=JPEG"; + size = (uint16_t) strlen( s ); + + n = TXTRecordGetCount( size, s ); + require_action( n == 3, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, s, "test", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, s, "Anon", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, s, "Allowed", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, s, "", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, s, "Anon Allowed", &value, &valueSize ); + require_noerr( err, exit ); + require_action( value == NULL, exit, err = kDNSServiceErr_Unknown ); + require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, s, "Options", &value, &valueSize ); + require_noerr( err, exit ); + require_action( value != NULL, exit, err = kDNSServiceErr_Unknown ); + require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, s, "InstalledPlugins", &value, &valueSize ); + require_noerr( err, exit ); + require_action( valueSize == 4, exit, err = kDNSServiceErr_Unknown ); + require_action( memcmp( value, "JPEG", 4 ) == 0, exit, err = kDNSServiceErr_Unknown ); + + key[ 0 ] = '\0'; + err = TXTRecordGetItemAtIndex( size, s, 0, key, &value, &valueSize ); + require_noerr( err, exit ); + require_action( strcmp( key, "Anon Allowed" ) == 0, exit, err = kDNSServiceErr_Unknown ); + require_action( value == NULL, exit, err = kDNSServiceErr_Unknown ); + require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown ); + + key[ 0 ] = '\0'; + err = TXTRecordGetItemAtIndex( size, s, 1, key, &value, &valueSize ); + require_noerr( err, exit ); + require_action( strcmp( key, "Options" ) == 0, exit, err = kDNSServiceErr_Unknown ); + require_action( value != NULL, exit, err = kDNSServiceErr_Unknown ); + require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown ); + + key[ 0 ] = '\0'; + err = TXTRecordGetItemAtIndex( size, s, 2, key, &value, &valueSize ); + require_noerr( err, exit ); + require_action( strcmp( key, "InstalledPlugins" ) == 0, exit, err = kDNSServiceErr_Unknown ); + require_action( value != NULL, exit, err = kDNSServiceErr_Unknown ); + require_action( valueSize == 4, exit, err = kDNSServiceErr_Unknown ); + require_action( memcmp( value, "JPEG", valueSize ) == 0, exit, err = kDNSServiceErr_Unknown ); + + key[ 0 ] = '\0'; + err = TXTRecordGetItemAtIndex( size, s, 3, key, &value, &valueSize ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + // Empty Test + + err = TXTRecordCreate( &ref ); + require_noerr( err, exit ); + + txt = TXTRecordGetBytesPtr( ref ); + require_action( txt == NULL, exit, err = kDNSServiceErr_Unknown ); + size = TXTRecordGetLength( ref ); + require_action( size == 0, exit, err = kDNSServiceErr_Unknown ); + + TXTRecordDeallocate( ref ); + + // Create Single Test + + err = TXTRecordCreate( &ref ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL ); + require_noerr( err, exit ); + + txt = TXTRecordGetBytesPtr( ref ); + require_action( txt, exit, err = kDNSServiceErr_Unknown ); + size = TXTRecordGetLength( ref ); + require_action( size == 13, exit, err = kDNSServiceErr_Unknown ); + require_action( memcmp( txt, "\014Anon Allowed", size ) == 0, exit, err = kDNSServiceErr_Unknown ); + + n = TXTRecordGetCount( size, txt ); + require_action( n == 1, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "test", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "Anon", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "Allowed", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "Anon Allowed", &value, &valueSize ); + require_noerr( err, exit ); + require_action( value == NULL, exit, err = kDNSServiceErr_Unknown ); + require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "aNoN aLlOwEd", &value, &valueSize ); + require_noerr( err, exit ); + require_action( value == NULL, exit, err = kDNSServiceErr_Unknown ); + require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown ); + + TXTRecordDeallocate( ref ); + + // Create Multiple Test + + err = TXTRecordCreate( &ref ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Options", 0, "" ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "InstalledPlugins", 4, "JPEG" ); + require_noerr( err, exit ); + + txt = TXTRecordGetBytesPtr( ref ); + require_action( txt, exit, err = kDNSServiceErr_Unknown ); + size = TXTRecordGetLength( ref ); + require_action( size == 44, exit, err = kDNSServiceErr_Unknown ); + require_action( memcmp( txt, + "\014Anon Allowed" + "\010Options=" + "\025InstalledPlugins=JPEG", + size ) == 0, exit, err = kDNSServiceErr_Unknown ); + + n = TXTRecordGetCount( size, txt ); + require_action( n == 3, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "test", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "Anon", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "Allowed", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "", NULL, NULL ); + require_action( err != kDNSServiceErr_NoError, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "Anon Allowed", &value, &valueSize ); + require_noerr( err, exit ); + require_action( value == NULL, exit, err = kDNSServiceErr_Unknown ); + require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "Options", &value, &valueSize ); + require_noerr( err, exit ); + require_action( value != NULL, exit, err = kDNSServiceErr_Unknown ); + require_action( valueSize == 0, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordGetValuePtr( size, txt, "InstalledPlugins", &value, &valueSize ); + require_noerr( err, exit ); + require_action( valueSize == 4, exit, err = kDNSServiceErr_Unknown ); + require_action( memcmp( value, "JPEG", 4 ) == 0, exit, err = kDNSServiceErr_Unknown ); + + TXTRecordDeallocate( ref ); + + // Remove Single Test + + err = TXTRecordCreate( &ref ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL ); + require_noerr( err, exit ); + + err = TXTRecordRemoveValue( ref, "Anon Allowed" ); + require_noerr( err, exit ); + + txt = TXTRecordGetBytesPtr( ref ); + require_action( txt == NULL, exit, err = kDNSServiceErr_Unknown ); + size = TXTRecordGetLength( ref ); + require_action( size == 0, exit, err = kDNSServiceErr_Unknown ); + + TXTRecordDeallocate( ref ); + + // Remove Multiple Test + + err = TXTRecordCreate( &ref ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Options", 0, "" ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "InstalledPlugins", 4, "JPEG" ); + require_noerr( err, exit ); + + err = TXTRecordRemoveValue( ref, "Options" ); + require_noerr( err, exit ); + + txt = TXTRecordGetBytesPtr( ref ); + require_action( txt, exit, err = kDNSServiceErr_Unknown ); + size = TXTRecordGetLength( ref ); + require_action( size == 35, exit, err = kDNSServiceErr_Unknown ); + require_action( memcmp( txt, + "\014Anon Allowed" + "\025InstalledPlugins=JPEG", + size ) == 0, exit, err = kDNSServiceErr_Unknown ); + + n = TXTRecordGetCount( size, txt ); + require_action( n == 2, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordRemoveValue( ref, "Anon Allowed" ); + require_noerr( err, exit ); + + txt = TXTRecordGetBytesPtr( ref ); + require_action( txt, exit, err = kDNSServiceErr_Unknown ); + size = TXTRecordGetLength( ref ); + require_action( size == 22, exit, err = kDNSServiceErr_Unknown ); + require_action( memcmp( txt, + "\025InstalledPlugins=JPEG", + size ) == 0, exit, err = kDNSServiceErr_Unknown ); + + n = TXTRecordGetCount( size, txt ); + require_action( n == 1, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordRemoveValue( ref, "InstalledPlugins" ); + require_noerr( err, exit ); + + txt = TXTRecordGetBytesPtr( ref ); + require_action( txt == NULL, exit, err = kDNSServiceErr_Unknown ); + size = TXTRecordGetLength( ref ); + require_action( size == 0, exit, err = kDNSServiceErr_Unknown ); + + TXTRecordDeallocate( ref ); + + // Replace Test + + err = TXTRecordCreate( &ref ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Options", 0, "" ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Options", 0, "" ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "InstalledPlugins", 4, "JPEG" ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "InstalledPlugins", 4, "JPEG" ); + require_noerr( err, exit ); + + txt = TXTRecordGetBytesPtr( ref ); + require_action( txt, exit, err = kDNSServiceErr_Unknown ); + size = TXTRecordGetLength( ref ); + require_action( size == 44, exit, err = kDNSServiceErr_Unknown ); + require_action( memcmp( txt, + "\014Anon Allowed" + "\010Options=" + "\025InstalledPlugins=JPEG", + size ) == 0, exit, err = kDNSServiceErr_Unknown ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 4, "test" ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 0, "" ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 0, NULL ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 0, "" ); + require_noerr( err, exit ); + + err = TXTRecordSetValue( ref, "Anon Allowed", 4, "test" ); + require_noerr( err, exit ); + + txt = TXTRecordGetBytesPtr( ref ); + require_action( txt, exit, err = kDNSServiceErr_Unknown ); + size = TXTRecordGetLength( ref ); + require_action( size == 49, exit, err = kDNSServiceErr_Unknown ); + require_action( memcmp( txt, + "\021Anon Allowed=test" + "\010Options=" + "\025InstalledPlugins=JPEG", + size ) == 0, exit, err = kDNSServiceErr_Unknown ); + + TXTRecordDeallocate( ref ); + +exit: + return( err ); +} +#endif // DEBUG + +#ifdef __cplusplus + } +#endif diff --git a/mDNSWindows/DNSSD.h b/mDNSWindows/DNSSD.h new file mode 100644 index 0000000..11cd6e3 --- /dev/null +++ b/mDNSWindows/DNSSD.h @@ -0,0 +1,1689 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSSD.h,v $ +Revision 1.7 2004/05/11 03:08:53 bradley +Updated TXT Record API based on latest proposal. This still includes dynamic TXT record building for +a final CVS snapshot for private libraries before this functionality is removed from the public API. + +Revision 1.6 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +: Remove flags with zero value +: Passing in NULL causes a crash. + +Revision 1.5 2004/05/03 10:34:24 bradley +Implemented preliminary version of the TXTRecord API. + +Revision 1.4 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.3 2004/04/09 21:03:14 bradley +Changed port numbers to use network byte order for consistency with other platforms. + +Revision 1.2 2004/04/08 09:43:42 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.1 2004/01/30 02:45:21 bradley +High-level implementation of the DNS-SD API. Supports both "direct" (compiled-in mDNSCore) and "client" +(IPC<->service) usage. Conditionals can exclude either "direct" or "client" to reduce code size. + +*/ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header DNSSD.h + + @abstract DNS Service Discovery provides registration, domain and service discovery, and name resolving support. + + @discussion + + High-level implementation of the DNS-SD API. This supports both "direct" (compiled-in mDNSCore) and "client" (IPC to + service) usage. Conditionals can exclude either "direct" or "client" to reduce code size. +*/ + +#ifndef _DNS_SD_H +#define _DNS_SD_H + +#include "CommonServices.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == Configuration == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DNS_SD_DIRECT_ENABLED + + @abstract Enables or disables DNS-SD direct. +*/ + +#if( !defined( DNS_SD_DIRECT_ENABLED ) ) + #define DNS_SD_DIRECT_ENABLED 1 +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DNS_SD_CLIENT_ENABLED + + @abstract Enables or disables DNS-SD client. +*/ + +#if( !defined( DNS_SD_CLIENT_ENABLED ) ) + #define DNS_SD_CLIENT_ENABLED 1 +#endif + +#if 0 +#pragma mark == Types == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSServiceRef + + @abstract Reference to a DNSService object. + + @discussion + + Note: client is responsible for serializing access to these structures if they are shared between concurrent threads. +*/ + +typedef struct _DNSServiceRef_t * DNSServiceRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSRecordRef + + @abstract Reference to a DNSRecord object. + + @discussion + + Note: client is responsible for serializing access to these structures if they are shared between concurrent threads. +*/ + +typedef struct _DNSRecordRef_t * DNSRecordRef; + +#if 0 +#pragma mark == Flags == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSServiceFlags + + @abstract General flags used in DNS-SD functions. + + @constant kDNSServiceFlagsNone + No flags (makes it clearer to use this symbolic constant rather than using a 0). + + @constant kDNSServiceFlagsMoreComing + MoreComing indicates to a callback that at least one more result is queued and will be delivered + following immediately after this one. Applications should not update their UI to display browse + results when the MoreComing flag is set, because this would result in a great deal of ugly + flickering on the screen. Applications should instead wait until until MoreComing is not set + (i.e. "Finished", for now), and then update their UI. When MoreComing is not set (i.e. + "Finished") that doesn't mean there will be no more answers EVER, just that there are no more + answers immediately available right now at this instant. If more answers become available in + the future they will be delivered as usual. + + @constant kDNSServiceFlagsAdd + Service or domain has been added during browsing/querying or domain enumeration. + + @constant kDNSServiceFlagsDefault + Default domain has been added during domain enumeration. + + @constant kDNSServiceFlagsNoAutoRename + Auto renaming should not be performed. Only valid if a name is explicitly specified when + registering a service (i.e. the default name is not used). + + @constant kDNSServiceFlagsShared + Shared flag for registering individual records on a connected DNSServiceRef. Indicates that there + may be multiple records with this name on the network (e.g. PTR records). + + @constant kDNSServiceFlagsUnique + Shared flag for registering individual records on a connected DNSServiceRef. Indicates that the + record's name is to be unique on the network (e.g. SRV records). + + @constant kDNSServiceFlagsBrowseDomains + Enumerates domains recommended for browsing. + + @constant kDNSServiceFlagsRegistrationDomains + Enumerates domains recommended for registration. +*/ + +typedef uint32_t DNSServiceFlags; +enum +{ + kDNSServiceFlagsNone = 0, + + kDNSServiceFlagsMoreComing = ( 1 << 0 ), + + kDNSServiceFlagsAdd = ( 1 << 1 ), + kDNSServiceFlagsDefault = ( 1 << 2 ), + + kDNSServiceFlagsNoAutoRename = ( 1 << 3 ), + + kDNSServiceFlagsShared = ( 1 << 4 ), + kDNSServiceFlagsUnique = ( 1 << 5 ), + + kDNSServiceFlagsBrowseDomains = ( 1 << 6 ), + kDNSServiceFlagsRegistrationDomains = ( 1 << 7 ) +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSServiceInitializeFlags + + @abstract Flags used with DNSServiceInitialize. + + @constant kDNSServiceInitializeFlagsAdvertise + Indicates that interfaces should be advertised on the network. Software that only performs searches + do not need to set this flag. + + @constant kDNSServiceInitializeFlagsNoServerCheck + No check for a valid server should be performed. Only applicable if client support is enabled. +*/ + +typedef uint32_t DNSServiceInitializeFlags; + +#define kDNSServiceInitializeFlagsNone 0 +#define kDNSServiceInitializeFlagsAdvertise ( 1 << 0 ) +#define kDNSServiceInitializeFlagsNoServerCheck ( 1 << 1 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kDNSServiceMaxDomainName + + @abstract Maximum length, in bytes, of a domain name represented as an escaped C-String +*/ + +#define kDNSServiceMaxDomainName 1005 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kDNSServiceInterfaceIndexAny + + @abstract Interface index to indicate any interface. +*/ + +#define kDNSServiceInterfaceIndexAny 0 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kDNSServiceInterfaceIndexAny + + @abstract Use the local interface only. +*/ + +#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) ~0 ) + +#if 0 +#pragma mark == Errors == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSServiceErrorType + + @abstract Possible error code values. +*/ + +typedef int32_t DNSServiceErrorType; +enum +{ + // Error codes are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537) + + kDNSServiceErr_NoError = 0, + kDNSServiceErr_Unknown = -65537, // 0xFFFE FFFF (first error code) + 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_NoCache = -65546, + kDNSServiceErr_AlreadyRegistered = -65547, + kDNSServiceErr_NameConflict = -65548, + kDNSServiceErr_Invalid = -65549, + kDNSServiceErr_Incompatible = -65551, + kDNSServiceErr_BadInterfaceIndex = -65552, + kDNSServiceErr_Refused = -65553, + kDNSServiceErr_NoSuchRecord = -65554, + kDNSServiceErr_NoAuth = -65555, + kDNSServiceErr_NoSuchKey = -65556, + kDNSServiceErr_NoValue = -65557, + kDNSServiceErr_BufferTooSmall = -65558, + + // TCP Connection Status + + kDNSServiceErr_ConnectionPending = -65570, + kDNSServiceErr_ConnectionFailed = -65571, + kDNSServiceErr_ConnectionEstablished = -65572, + + // Non-error values + + kDNSServiceErr_GrowCache = -65790, + kDNSServiceErr_ConfigChanged = -65791, + kDNSServiceErr_MemFree = -65792 // 0xFFFE FF00 (last error code) +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @var gDNSSDServer + + @abstract Global controlling the server to communicate with. Set to NULL for local operation. +*/ + +extern const char * gDNSSDServer; + +#if 0 +#pragma mark == DNS Classes == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSServiceDNSClass + + @abstract DNS Class +*/ + +typedef enum // From RFC 1035 +{ + kDNSServiceDNSClass_IN = 1, // Internet + kDNSServiceDNSClass_CS = 2, // CSNET + kDNSServiceDNSClass_CH = 3, // CHAOS + kDNSServiceDNSClass_HS = 4, // Hesiod + kDNSServiceDNSClass_NONE = 254, // Used in DNS UPDATE [RFC 2136] + + kDNSServiceDNSClass_Mask = 0x7FFF, // Multicast DNS uses the bottom 15 bits to identify the record class... + kDNSServiceDNSClass_UniqueRRSet = 0x8000, // ... and the top bit indicates that all other cached records are now invalid + + kDNSServiceDNSQClass_ANY = 255, // Not a DNS class, but a DNS query class, meaning "all classes" + kDNSServiceDNSQClass_UnicastResponse = 0x8000 // Top bit set in a question means "unicast response acceptable" + +} DNSServiceDNSClass; + +#if 0 +#pragma mark == DNS Types == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DNSServiceDNSType + + @abstract DNS Type +*/ + +typedef enum // From RFC 1035 +{ + kDNSServiceDNSType_A = 1, // Address + kDNSServiceDNSType_NS = 2, // Name Server + kDNSServiceDNSType_MD = 3, // Mail Destination + kDNSServiceDNSType_MF = 4, // Mail Forwarder + kDNSServiceDNSType_CNAME = 5, // Canonical Name + kDNSServiceDNSType_SOA = 6, // Start of Authority + kDNSServiceDNSType_MB = 7, // Mailbox + kDNSServiceDNSType_MG = 8, // Mail Group + kDNSServiceDNSType_MR = 9, // Mail Rename + kDNSServiceDNSType_NULL = 10, // NULL RR + kDNSServiceDNSType_WKS = 11, // Well-known-service + kDNSServiceDNSType_PTR = 12, // Domain name pointer + kDNSServiceDNSType_HINFO = 13, // Host information + kDNSServiceDNSType_MINFO = 14, // Mailbox information + kDNSServiceDNSType_MX = 15, // Mail Exchanger + kDNSServiceDNSType_TXT = 16, // Arbitrary text string + kDNSServiceDNSType_AAAA = 28, // IPv6 address + kDNSServiceDNSType_SRV = 33, // Service record + kDNSServiceDNSTypeQType_ANY = 255 // Not a DNS type, but a DNS query type, meaning "all types" + +} DNSServiceDNSType; + +#if 0 +#pragma mark == General == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceInitialize + + @abstract Initializes DNS-SD. + + @param inFlags Flags to control the initialization process (only applicable for DNS-SD direct). + @param inCacheEntryCount Size of the cache (only applicable for DNS-SD direct). +*/ + +DNSServiceErrorType DNSServiceInitialize( DNSServiceInitializeFlags inFlags, int inCacheEntryCount ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceFinalize + + @abstract Finalizes DNS-SD. +*/ + +void DNSServiceFinalize( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceCheckVersion + + @abstract Performs a version check. Mainly only needed when client-mode is enabled to verify the server is compatible. +*/ + +DNSServiceErrorType DNSServiceCheckVersion( void ); + +#if 0 +#pragma mark == Properties == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSPropertyCode + + @abstract Identifies a property. +*/ + +typedef uint32_t DNSPropertyCode; + +#define kDNSPropertyCodeVersion 0x76657273 // 'vers' + // Field: "version" + // Format: + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct DNSPropertyData + + @abstract Data for a property. +*/ + +typedef struct DNSPropertyData DNSPropertyData; +struct DNSPropertyData +{ + DNSPropertyCode code; + + union + { + struct // version + { + uint32_t clientCurrentVersion; + uint32_t clientOldestServerVersion; + uint32_t serverCurrentVersion; + uint32_t serverOldestClientVersion; + + } version; + + } u; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceCopyProperty + + @abstract Copies a property into a newly allocated buffer. + + @param inCode Property to copy. + @param outData Receives the copied property data. Must be release with DNSServiceReleaseProperty. +*/ + +DNSServiceErrorType DNSServiceCopyProperty( DNSPropertyCode inCode, DNSPropertyData *outData ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceReleaseProperty + + @abstract Releases a property copied with a successful call to DNSServiceCopyProperty. +*/ + +DNSServiceErrorType DNSServiceReleaseProperty( DNSPropertyData *inData ); + +#if 0 +#pragma mark == Unix Domain Socket Access == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRefSockFD + + @param inRef A DNSServiceRef initialized by any of the DNSService calls. + + @result The DNSServiceRef's underlying socket descriptor, or -1 on error. + + @discussion + + 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. +*/ + +int DNSServiceRefSockFD( DNSServiceRef inRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceProcessResult + + @param inRef A DNSServiceRef initialized by any of the DNSService calls that take a callback parameter. + + @result Returns kDNSServiceErr_NoError on success, otherwise returns an error code indicating the specific failure + that occurred. + + @discussion + + 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. +*/ + +DNSServiceErrorType DNSServiceProcessResult( DNSServiceRef inRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRefDeallocate + + @param inRef A DNSServiceRef initialized by any of the DNSService calls. + + @discussion + + 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. + + Note: 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: If the reference was initialized with DNSServiceCreateConnection(), any DNSRecordRefs created via this reference + will be invalidated by this call - the resource records are deregistered, and their DNSRecordRefs may not be used in + subsequent functions. Similarly, if the reference was initialized with DNSServiceRegister, and an extra resource record + was added to the service via DNSServiceAddRecord(), the DNSRecordRef created by the Add() call is invalidated when this + function is called - the DNSRecordRef may not be used in subsequent functions. + + 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. +*/ + +void DNSServiceRefDeallocate( DNSServiceRef inRef ); + +#if 0 +#pragma mark == Domain Enumeration == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSServiceDomainEnumReply + + @abstract Callback function for DNSServiceEnumerateDomains. + + @param inRef + The DNSServiceRef initialized by DNSServiceEnumerateDomains(). + + @param inFlags + Possible values are: + 1 (MoreComing) + 2 (Add/Remove) + 4 (Add Default) + + @param inInterfaceIndex + Specifies the interface on which the domain exists. (The index for a given interface is determined + via the if_nametoindex() family of calls). + + @param inErrorCode + Will be kDNSServiceErr_NoError (0) on success, otherwise indicates the failure that occurred (other + parameters are undefined if errorCode is nonzero). + + @param inDomain + The name of the domain. + + @param inContext + The context pointer passed to DNSServiceEnumerateDomains. +*/ + +typedef void + ( CALLBACK_COMPAT *DNSServiceDomainEnumReply )( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inDomain, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceEnumerateDomains + + @abstract Asynchronously enumerate domains available for browsing and registration. + + @param outRef + A pointer to an uninitialized DNSServiceRef. May be passed to DNSServiceRefDeallocate() to cancel the + enumeration. + + @param inFlags + Possible values are: + 64 (BrowseDomains) to enumerate domains recommended for browsing. + 128 (RegistrationDomains) to enumerate domains recommended for registration. + + @param inInterfaceIndex + 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. + + @param inCallBack + The function to be called when a domain is found or the call asynchronously fails. + + @param inContext + An application context pointer which is passed to the callback function (may be NULL). + + @result Returns kDNSServiceErr_NoError on success (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). + + @discussion + + 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. +*/ + +DNSServiceErrorType + DNSServiceEnumerateDomains( + DNSServiceRef * outRef, + const DNSServiceFlags inFlags, + const uint32_t inInterfaceIndex, + const DNSServiceDomainEnumReply inCallBack, + void * inContext ); // may be NULL + +#if 0 +#pragma mark == Service Registration == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSServiceRegisterReply + + @abstract Callback function for DNSServiceRegister. + + @param inRef + The DNSServiceRef initialized by DNSServiceRegister(). + + @param inFlags + Currently unused, reserved for future use. + + @param inErrorCode + 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. + + @param inName + The service name registered (if the application did not specify a name in DNSServiceRegister(), this + indicates what name was automatically chosen). + + @param inType + The type of service registered, as it was passed to the callout. + + @param inDomain + 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). + + @param inContext + The context pointer that was passed to the callout. +*/ + +typedef void + ( CALLBACK_COMPAT *DNSServiceRegisterReply )( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRegister + + @abstract Register a service that is discovered via Browse() and Resolve() calls. + + @param outRef + A pointer to an uninitialized DNSServiceRef. If this call succeeds, the reference may be passed to + DNSServiceRefDeallocate() to deregister the service. + + @param inInterfaceIndex + 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). + + @param inFlags + Indicates the renaming behavior on name conflict (most applications will pass 0). See flag definitions + above for details. + + @param inName + 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). + + @param inType + The service type followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). The transport protocol + must be "_tcp" or "_udp". + + @param inDomain + 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). + + @param inHost + 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(). + + @param inPort + The port on which the service accepts connections in network byte order. 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. + + @param inTXTSize + The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL. + + @param inTXT + The txt record rdata. May be NULL. Note that a non-NULL txtRecord MUST be a properly formatted DNS TXT + record, i.e. ... + + @param inCallBack + 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(). + + @param inContext + An application context pointer which is passed to the callback function (may be NULL). + + @result Returns kDNSServiceErr_NoError on success (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 * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, // may be NULL + const char * inType, + const char * inDomain, // may be NULL + const char * inHost, // may be NULL + uint16_t inPort, + uint16_t inTXTSize, + const void * inTXT, // may be NULL + DNSServiceRegisterReply inCallBack, // may be NULL + void * inContext ); // may be NULL + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceAddRecord + + @abstract Add a record to a registered service. + + @param inRef + A DNSServiceRef initialized by DNSServiceRegister(). + + @param inRecordRef + A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this call, this ref may be + passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). If the above DNSServiceRef is passed + to DNSServiceRefDeallocate(), RecordRef is also invalidated and may not be used further. + + @param inFlags + Currently ignored, reserved for future use. + + @param inRRType + The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h. + + @param inRDataSize + The length, in bytes, of the rdata. + + @param inRData + The raw rdata to be contained in the added resource record. + + @param inTTL + The time to live of the resource record, in seconds. + + @result Returns kDNSServiceErr_NoError on success, otherwise returns an error code indicating the error that + occurred (the RecordRef is not initialized). + + @discussion + + 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(). +*/ + +DNSServiceErrorType + DNSServiceAddRecord( + DNSServiceRef inRef, + DNSRecordRef * inRecordRef, + DNSServiceFlags inFlags, + uint16_t inRRType, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceUpdateRecord + + @abstract Update a registered resource record. + + @param inRef + A DNSServiceRef that was initialized by DNSServiceRegister() or DNSServiceCreateConnection(). + + @param inRecordRef + A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the service's primary txt record. + + @param inFlags + Currently ignored, reserved for future use. + + @param inRDataSize + The length, in bytes, of the new rdata. + + @param inRData + The new rdata to be contained in the updated resource record. + + @param inTTL + The time to live of the updated resource record, in seconds. + + @result Returns kDNSServiceErr_NoError on success, otherwise returns an error code indicating the error that occurred. + + @discussion + + 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() +*/ + +DNSServiceErrorType + DNSServiceUpdateRecord( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, // may be NULL + DNSServiceFlags inFlags, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRemoveRecord + + @abstract Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister + an record registered individually via DNSServiceRegisterRecord(). + + @param inRef + A DNSServiceRef that was initialized by DNSServiceRegister() or DNSServiceCreateConnection(). + + @param inRecordRef + A DNSRecordRef initialized by a successful call to DNSServiceAddRecord() or DNSServiceRegisterRecord(). + + @param inFlags + Currently ignored, reserved for future use. + + @result Returns kDNSServiceErr_NoError on success, otherwise returns an error code indicating the error that occurred. +*/ + +DNSServiceErrorType + DNSServiceRemoveRecord( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags ); + +#if 0 +#pragma mark == Service Discovery == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSServiceBrowseReply + + @abstract Callback function for DNSServiceBrowse. + + @param inRef + The DNSServiceRef initialized by DNSServiceBrowse(). + + @param inFlags + Possible values are MoreComing and Add/Remove. See flag definitions for details. + + @param inInterfaceIndex + The interface on which the service is advertised. This index should be passed to DNSServiceResolve() + when resolving the service. + + @param inErrorCode + Will be kDNSServiceErr_NoError (0) on success, otherwise will indicate the failure that occurred. + Other parameters are undefined if the errorCode is nonzero. + + @param inName + The service name discovered. + + @param inType + The service type, as passed in to DNSServiceBrowse(). + + @param inDomain + 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). + + @param inContext + The context pointer that was passed to the callout. +*/ + +typedef void + ( CALLBACK_COMPAT *DNSServiceBrowseReply )( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceBrowse + + @abstract Browse for instances of a service. + + @param outRef + A pointer to an uninitialized DNSServiceRef. May be passed to DNSServiceRefDeallocate() to terminate + the browse. + + @param inFlags + Currently ignored, reserved for future use. + + @param inInterfaceIndex + 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. + + @param inType + 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". + + @param inDomain + 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). + + @param inCallBack + The function to be called when an instance of the service being browsed for is found, or if the call + asynchronously fails. + + @param inContext + An application context pointer which is passed to the callback function (may be NULL). + + @result Returns kDNSServiceErr_NoError on success (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 * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inType, + const char * inDomain, // may be NULL + DNSServiceBrowseReply inCallBack, + void * inContext ); // may be NULL + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSServiceResolveReply + + @abstract Callback function for DNSServiceResolve. + + @param inRef + The DNSServiceRef initialized by DNSServiceResolve(). + + @param inFlags + Currently unused, reserved for future use. + + @param inInterfaceIndex + The interface on which the service was resolved. + + @param inErrorCode + Will be kDNSServiceErr_NoError (0) on success, otherwise will indicate the failure that occurred. + Other parameters are undefined if the errorCode is nonzero. + + @param inFullName + The full service domain name, in the form ... (Any literal dots (".") + are escaped with a backslash ("\."), and literal backslashes are escaped with a second backslash ("\\"), + e.g. a web server named "Dr. Pepper" would have the fullname "Dr\.\032Pepper._http._tcp.local."). + This is the appropriate format to pass to standard system DNS APIs such as res_query(), or to the + special-purpose functions included in this API that take fullname parameters. + + @param inHostName + 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. + + @param inPort + The port number on which connections are accepted for this service in network byte order. + + @param inTXTSize + The length of the txt record, in bytes. + + @param inTXT + The service's primary txt record, in standard txt record format. + + @param inContext + The context pointer that was passed to the callout. +*/ + +typedef void + ( CALLBACK_COMPAT *DNSServiceResolveReply )( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceResolve + + @abstract Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and txt record. + + @param outRef + A pointer to an uninitialized DNSServiceRef. May be passed to DNSServiceRefDeallocate() to terminate + the resolve. + + @param inFlags + Currently unused, reserved for future use. + + @param inInterfaceIndex + The interface on which to resolve the service. The client should pass the interface on which the + servicename was discovered, i.e. the inInterfaceIndex passed to the DNSServiceBrowseReply callback, or 0 + to resolve the named service on all available interfaces. + + @param inName + The service name to be resolved. + + @param inType + 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". + + @param inDomain + The domain on which the service is registered, i.e. the domain passed to the DNSServiceBrowseReply + callback. + + @param inCallBack + The function to be called when a result is found, or if the call asynchronously fails. + + @param inContext + An application context pointer which is passed to the callback function (may be NULL). + + @result Returns kDNSServiceErr_NoError on success (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). The context pointer that was passed to the callout. + + @discussion + + 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. +*/ + +DNSServiceErrorType + DNSServiceResolve( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + DNSServiceResolveReply inCallBack, + void * inContext ); // may be NULL + +#if 0 +#pragma mark == Special Purpose == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceConstructFullName + + @abstract 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. + + @param outFullName + 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. + + @param inName + 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"). + + @param inType + The service type followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). + + @param inDomain + The domain name, e.g. "apple.com". Any literal dots or backslashes must be escaped. + + @result Returns 0 on success, -1 on error. + + @discussion + + 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). +*/ + +int + DNSServiceConstructFullName( + char * outFullName, + const char * inName, // may be NULL + const char * inType, + const char * inDomain ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceCreateConnection + + @abstract Create a connection to the daemon allowing efficient registration of multiple individual records. + + @param outRef + A pointer to an uninitialized DNSServiceRef. Deallocating the reference (via DNSServiceRefDeallocate()) + severs the connection and deregisters all records registered on this connection. + + @result 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 *outRef ); + + //--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSServiceRegisterRecordReply + + @abstract Callback function for DNSServiceRegisterRecord. + + @param inRef + The connected DNSServiceRef initialized by DNSServiceCreateConnection(). + + @param inRecordRef + The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above DNSServiceRef is passed to + DNSServiceRefDeallocate(), this DNSRecordRef is invalidated, and may not be used further. + + @param inFlags + Currently unused, reserved for future use. + + @param inErrorCode + Will be kDNSServiceErr_NoError on success, otherwise will indicate the failure that occurred (including + name conflicts). Other parameters are undefined if errorCode is nonzero. + + @param inContext + The context pointer that was passed to the callout. +*/ + + typedef void + ( CALLBACK_COMPAT *DNSServiceRegisterRecordReply )( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRegisterRecord + + @abstract Register an individual resource record on a connected DNSServiceRef. + + @param inRef + A DNSServiceRef initialized by DNSServiceCreateConnection(). + + @param inRecordRef + 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()). + + @param inFlags + Possible values are Shared/Unique (see flag type definitions for details). + + @param inInterfaceIndex + 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. + + @param inFullName + The full domain name of the resource record. + + @param inRRType + The numerical type of the resource record (e.g. PTR, SRV, etc), as defined in nameser.h. + + @param inRRClass + The class of the resource record, as defined in nameser.h (usually 1 for the Internet class). + + @param inRDataSize + Length, in bytes, of the rdata. + + @param inRData + A pointer to the raw rdata, as it is to appear in the DNS record. + + @param inTTL + The time to live of the resource record, in seconds. + + @param inCallBack + The function to be called when a result is found, or if the call asynchronously fails (e.g. because + of a name conflict). + + @param inContext + An application context pointer which is passed to the callback function (may be NULL). + + @result Returns kDNSServiceErr_NoError on success (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). + + @discussion + + Note that name conflicts occurring for records registered via this call must be handled by the client in the callback. +*/ + +DNSServiceErrorType + DNSServiceRegisterRecord( + DNSServiceRef inRef, + DNSRecordRef * inRecordRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inFullName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + DNSServiceRegisterRecordReply inCallBack, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DNSServiceQueryRecordReply + + @abstract Callback function for DNSServiceQueryRecord. + + @param inRef + The DNSServiceRef initialized by DNSServiceQueryRecord(). + + @param inFlags + Possible values are Finished/MoreComing and Add/Remove. The Remove flag is set for PTR records with + a TTL of 0. + + @param inInterfaceIndex + The interface on which the query was resolved (the index for a given interface is determined via the + if_nametoindex() family of calls). + + @param inErrorCode + Will be kDNSServiceErr_NoError on success, otherwise will indicate the failure that occurred. Other + parameters are undefined if errorCode is nonzero. + + @param inFullName + The resource record's full domain name. + + @param inRRType + The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. + + @param inRRClass + The class of the resource record, as defined in nameser.h (usually 1). + + @param inRDataSize + The length, in bytes, of the resource record rdata. + + @param inRData + The raw rdata of the resource record. + + @param inTTL + The resource record's time to live, in seconds. + + @param inContext + The context pointer that was passed to the callout. + */ + +typedef void + ( CALLBACK_COMPAT *DNSServiceQueryRecordReply )( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceQueryRecord + + @abstract Query for an arbitrary DNS record. + + @param outRef + A pointer to an uninitialized DNSServiceRef. + + @param inFlags + Currently unused, reserved for future use. + + @param inInterfaceIndex + 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. + + @param inName + The full domain name of the resource record to be queried for. + + @param inRRType + The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc) as defined in nameser.h. + + @param inRRClass + The class of the resource record, as defined in nameser.h (usually 1 for the Internet class). + + @param inCallBack + The function to be called when a result is found, or if the call asynchronously fails. + + @param inContext + An application context pointer which is passed to the callback function (may be NULL). + + @result Returns kDNSServiceErr_NoError on success (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). + + @discussion + + Note that name conflicts occurring for records registered via this call must be handled by the client in the callback. +*/ + +DNSServiceErrorType + DNSServiceQueryRecord( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + DNSServiceQueryRecordReply inCallBack, + void * inContext ); // may be NULL + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceReconfirmRecord + + @abstract 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. + + @param inFlags + Currently unused, reserved for future use. + + @param inName + The resource record's full domain name. + + @param inRRType + The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. + + @param inRRClass + The class of the resource record, as defined in nameser.h (usually 1). + + @param inRDataSize + The length, in bytes, of the resource record rdata. + + @param inRData + The raw rdata of the resource record. +*/ + +void + DNSServiceReconfirmRecord( + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData ); + +#if 0 +#pragma mark == TXT Record Building == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef TXTRecordRef + + @abstract Reference to a TXTRecord object representing a DNS-SD TXT record. + + @discussion + + Note: client is responsible for serializing access to these structures if they are shared between concurrent threads. +*/ + +typedef struct _TXTRecordRef_t * TXTRecordRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function TXTRecordCreate + + @abstract Creates an empty TXTRecordRef. + + @param outRef + A pointer to an uninitialized TXTRecordRef. Filled in on success. + + @result Returns kDNSServiceErr_NoError on success. + Returns kDNSServiceErr_BadParam if the reference pointer is NULL. + Returns kDNSServiceErr_NoMemory if there is not enough memory to create the TXTRecord. + + @discussion + + Once you've created a TXTRecordRef, you can pass it to TXTRecordSetValue and other functions to add "key=value" + pairs to it. Finally, you can extract the raw bytes again to pass to DNSServiceRegister() or DNSServiceUpdateRecord(). + + A typical calling sequence for TXT record construction is something like: + + TXTRecordCreate(); + TXTRecordSetValue(); + TXTRecordSetValue(); + TXTRecordSetValue(); + ... + DNSServiceRegister( ... TXTRecordGetLength(), TXTRecordGetBytesPtr() ... ); + TXTRecordDeallocate(); +*/ + +DNSServiceErrorType TXTRecordCreate( TXTRecordRef *outRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function TXTRecordDeallocate + + @abstract Releases the memory associated with a TXTRecordRef. + + @param inRef + A TXTRecordRef initialized by calling TXTRecordCreate. +*/ + +void TXTRecordDeallocate( TXTRecordRef inRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function TXTRecordSetValue + + @abstract Adds a key (optionally with a value) to a TXTRecordRef. + + @param inRef + A TXTRecordRef initialized by TXTRecordCreate. + + @param inKey + Null-terminated key of the value to add. Must be at least 1 character and consist of only printable + US-ASCII characters (0x20-0x7E), excluding '=' (0x3D). Should be 14 characters or less (not counting + the terminating null). Keys are case insensitive (i.e. key "test" replaces key "TEST"). + + @param inValue + Pointer to value to add. For values that represent textual data, UTF-8 is STRONGLY recommended. + If NULL, then the key will be added with no value. + If non-NULL but valueSize is zero, then "key=" will be added with an empty value. + + @param inValueSize + Number of bytes in the value. Must be 0 if inValue is NULL. + + @result Returns kDNSServiceErr_NoError on success. + Returns kDNSServiceErr_BadParam if the parameters are illegal or not supported. + Returns kDNSServiceErr_Invalid if the key string contains illegal characters. + Returns kDNSServiceErr_NoMemory if there is not enough memory to set the value. + + @discussion + + If the key is already present in the TXTRecordRef, then the current value will be replaced with the new value. + Keys may be in four states with respect to a given TXT record: + + - Absent (key does not appear at all). + - Present with no value ("key" appears alone). + - Present with empty value ("key=" appears in the TXT record). + - Present with non-empty value ("key=value" appears in the TXT record). + + For more details refer to "Data Syntax for DNS-SD TXT Records" in + +*/ + +DNSServiceErrorType + TXTRecordSetValue( + TXTRecordRef inRef, + const char * inKey, + uint8_t inValueSize, // may be zero + const void * inValue ); // may be NULL + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function TXTRecordRemoveValue + + @abstract Removes a key from a TXTRecordRef. + + @param inRef + A TXTRecordRef initialized by TXTRecordCreate. + + @param inKey + Null-terminated key to remove. Note: keys are case insensitive. + + @result Returns kDNSServiceErr_NoError on success. + Returns kDNSServiceErr_NoSuchKey if the key is not present in the TXTRecordRef. + Returns kDNSServiceErr_BadParam if the parameters are illegal or not supported. +*/ + +DNSServiceErrorType TXTRecordRemoveValue( TXTRecordRef inRef, const char *inKey ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function TXTRecordGetLength + + @abstract Returns the number of raw bytes inside a TXTRecordRef. + + @param inRef + A TXTRecordRef initialized by TXTRecordCreate. + + @result Returns the number of raw bytes inside a TXTRecordRef which you can pass directly to DNSServiceRegister() + or to DNSServiceUpdateRecord(). Returns 0 if the TXTRecordRef is empty. + + @discussion + + The length may become invalid if you subsequently make changes to the TXTRecordRef by calling TXTRecordSetValue() + or TXTRecordRemoveValue(). +*/ + +uint16_t TXTRecordGetLength( TXTRecordRef inRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function TXTRecordGetBytesPtr + + @abstract Returns a pointer to the raw bytes inside the TXTRecordRef. + + @param inRef + A TXTRecordRef initialized by TXTRecordCreate. + + @result Returns a pointer to the raw bytes inside the TXTRecordRef which you can pass directly to + DNSServiceRegister() or to DNSServiceUpdateRecord(). Returns NULL if the TXTRecordRef is empty. + + @discussion + + The pointer may become invalid if you subsequently make changes to the TXTRecordRef by calling TXTRecordSetValue() + or TXTRecordRemoveValue(). +*/ + +const void * TXTRecordGetBytesPtr( TXTRecordRef inRef ); + +#if 0 +#pragma mark == TXT Record Parsing == +#endif + +/*--------------------------------------------------------------------------------------------------------------------------- + A typical calling sequence for TXT record parsing is something like: + Receive TXT record data in the DNSServiceResolve() callback then: + + cosnt void * value1Ptr; + uint8_t value1Size; + + err = TXTRecordGetValuePtr( txtSize, txtRecord, "key1", &value1Ptr, &value1Size ); + if( err == kDNSServiceErr_NoError ) + { + // "key1" found. Do work with "value1Ptr" data if needed. + } + ... + return; + + If you wish to retain the values after returning from the DNSServiceResolve() callback, then you need to copy the data + to your own storage using memcpy() or something similar so it does not go out of scope until you're done with it. + + If for some reason you need to parse a TXT record you built yourself using the TXT record construction functions above, + then you can do that using TXTRecordGetLength and TXTRecordGetBytesPtr functions: + + TXTRecordGetValue( TXTRecordGetLength( x ), TXTRecordGetBytesPtr( x ), "key1", &value1Ptr, &value1Size ); + + Most applications only fetch keys they know about from a TXT record and ignore the rest. + However, some debugging tools wish to fetch and display all keys. To do that, use the TXTRecordGetCount() and + TXTRecordGetItemAtIndex() functions. +*/ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function TXTRecordGetValuePtr + + @abstract Allows you to retrieve the value for a given key from a TXT Record. + + @param inTXTSize + Number of bytes in the TXT record. + + @param inTXTBytes + Pointer to the raw TXT record bytes. + + @param inKey + A null-terminated key to search for. Note: keys are case insensitive. + + @param outValue + Pointer to be filled in with a pointer to the value within the TXT record bytes. + Resulting pointer will be NULL if the key is present, but has no value. + Resulting pointer will be non-NULL and size zero if the key is present, but has an empty value. + Resulting pointer will be non-NULL and size non-zero if key is present and has a non-empty value. + May be NULL if only interested in the value size or if the key is present. + + @param outValueSize + Pointer to receive the size of the value. + Size will be 0 if there is no value or the value is empty. + May be NULL if not interested in getting the size. + + @result Returns kDNSServiceErr_NoError if a key with the specified name is found. + Returns kDNSServiceErr_NoSuchKey if the key does not exist in the TXTRecordRef. + Returns kDNSServiceErr_BadParam if the parameters are illegal or not supported. + Returns kDNSServiceErr_Invalid if the TXT record is malformed. + + @discussion + + The pointer may become invalid if you subsequently make changes to the TXT record by calling TXTRecordSetValue() + or TXTRecordRemoveValue(). +*/ + +DNSServiceErrorType + TXTRecordGetValuePtr( + uint16_t inTXTSize, + const void * inTXTBytes, + const char * inKey, + const void ** outValue, // may be NULL + uint8_t * outValueSize ); // may be NULL + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function TXTRecordGetCount + + @abstract Returns the total number of keys in the TXT Record. + + @param inTXTSize + Number of bytes in the TXT record. + + @param inTXTBytes + Pointer to the raw TXT record bytes. + + @result Returns the total number of keys in the TXT Record. + + @discussion + + The count can be used with TXTRecordGetItemAtIndex() to iterate through the keys. + The count may become invalid if you subsequently make changes to the TXT record by calling TXTRecordSetValue() + or TXTRecordRemoveValue(). +*/ + +uint16_t TXTRecordGetCount( uint16_t inTXTSize, const void *inTXTBytes ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function TXTRecordGetItemAtIndex + + @abstract Allows you to retrieve a key name, given an index into a TXT Record. + + @param inTXTSize + Number of bytes in the TXT record. + + @param inTXTBytes + Pointer to the raw TXT record bytes. + + @param inIndex + Index of item to get. The index is 0-based (0 is the first item). + Legal index values range from 0 to TXTRecordGetCount() - 1. + + @param inKeyBuffer + A string buffer used to store the null-terminated key name. The buffer must be at least 256 bytes + in order to hold the maximum possible key name. + May be NULL if not interested in the key name. + + @param outValue + Pointer to be filled in with a pointer to the value within the TXT record bytes. + Resulting pointer will be NULL if the key is present, but has no value. + Resulting pointer will be non-NULL and size zero if the key is present, but has an empty value. + Resulting pointer will be non-NULL and size non-zero if key is present and has a non-empty value. + May be NULL if only interested in the value size or if the key is present. + + @param outValueSize + Pointer to receive the size of the value. + Size will be 0 if there is no value or the value is empty. + May be NULL if not interested in getting the size. + + @result Returns kDNSServiceErr_NoError if a key with the specified name is found. + Returns kDNSServiceErr_Invalid if the index is greater than TXTRecordGetCount() - 1 or the TXT record is malformed. + Returns kDNSServiceErr_BadParam if the parameters are illegal or not supported. + + @discussion + + It also possible to iterate through keys in a TXT record by simply calling TXTRecordGetItemAtIndex() repeatedly, + beginning with index zero and increasing until TXTRecordGetItemAtIndex() returns an non-zero error code. + + The pointer may become invalid if you subsequently make changes to the TXTRecordRef by calling TXTRecordSetValue() + or TXTRecordRemoveValue(). +*/ + +DNSServiceErrorType + TXTRecordGetItemAtIndex( + uint16_t inTXTSize, + const void * inTXTBytes, + uint16_t inIndex, + char * inKeyBuffer, // may be NULL + const void ** outValue, // may be NULL + uint8_t * outValueSize ); // may be NULL + +#ifdef __cplusplus + } +#endif + +#endif // _DNS_SD_H diff --git a/mDNSWindows/DNSSDDirect.c b/mDNSWindows/DNSSDDirect.c new file mode 100644 index 0000000..c20a97e --- /dev/null +++ b/mDNSWindows/DNSSDDirect.c @@ -0,0 +1,1863 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSSDDirect.c,v $ +Revision 1.7 2004/06/05 00:04:27 cheshire +: wide-area domains should be returned in reg. domain enumeration + +Revision 1.6 2004/05/08 12:25:50 bradley +Changed to use symbolic enums to prevent some compilers from treating it as a sign conversion. + +Revision 1.5 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +: Remove flags with zero value +: Passing in NULL causes a crash. + +Revision 1.4 2004/04/15 06:55:50 bradley +Changed resolving to manually query for SRV and TXT records instead of using mDNS_StartResolveService. +This avoids extra A/AAAA record queries and allows local-only resolves to work with normal services. + +Revision 1.3 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.2 2004/04/09 21:03:14 bradley +Changed port numbers to use network byte order for consistency with other platforms. + +Revision 1.1 2004/01/30 02:46:15 bradley +Portable implementation of the DNS-SD API. This interacts with mDNSCore to perform all the real work +of the DNS-SD API. This code does not rely on any platform-specifics so it should run on any platform +with an mDNS platform plugin available. Software that cannot or does not want to use the IPC mechanism +(e.g. Windows CE, VxWorks, etc.) can use this code directly without any of the IPC pieces. + +*/ + +#include + +#include "CommonServices.h" +#include "DebugServices.h" + +#include "DNSSD.h" + +#if( DNS_SD_DIRECT_ENABLED ) + +#include "mDNSClientAPI.h" + +#include "DNSSDDirect.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[DNS-SD Direct] " + +typedef uint32_t DNSServiceRegisterFlags; + +#define kDNSServiceRegisterFlagsAutoName ( 1 << 0 ) +#define kDNSServiceRegisterFlagsAutoNameOnFree ( 1 << 1 ) +#define kDNSServiceRegisterFlagsRenameOnConflict ( 1 << 2 ) + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +typedef void ( *DNSServiceRefReleaseCallBack )( DNSServiceRef inRef ); + +// DNSServiceRef + +typedef struct _DNSServiceRef_t _DNSServiceRef_t; +struct _DNSServiceRef_t +{ + DNSServiceRef next; + DNSServiceRefReleaseCallBack releaseCallBack; + void * context; + + union + { + struct // EnumerateDomains + { + DNSQuestion question; + mDNSBool questionActive; + DNSQuestion defaultQuestion; + mDNSBool defaultQuestionActive; + DNSServiceDomainEnumReply callback; + + } domain; + + struct // Register + { + ServiceRecordSet * set; + domainlabel name; + DNSServiceRegisterFlags flags; + DNSServiceRegisterReply callback; + + } reg; + + struct // Browse + { + DNSQuestion question; + mDNSBool questionActive; + DNSServiceBrowseReply callback; + + } browse; + + struct // Resolve + { + DNSServiceFlags flags; + DNSQuestion srvQuestion; + mDNSBool srvQuestionActive; + const ResourceRecord * srvAnswer; + DNSQuestion txtQuestion; + mDNSBool txtQuestionActive; + const ResourceRecord * txtAnswer; + DNSServiceResolveReply callback; + + } resolve; + + struct // CreateConnection + { + DNSRecordRef records; + + } connection; + + struct // Query + { + DNSQuestion question; + mDNSBool questionActive; + DNSServiceQueryRecordReply callback; + + } query; + + } u; +}; + +// DNSRecordRef + +typedef struct _DNSRecordRef_t _DNSRecordRef_t; +struct _DNSRecordRef_t +{ + union + { + struct // Service-based records (i.e. DNSServiceRegister-based DNSServiceRef's) + { + ExtraResourceRecord extra; + + } service; + + struct // Connection-based records (i.e. DNSServiceCreateConnection-based DNSServiceRef's) + { + DNSRecordRef next; + DNSServiceRef owner; + DNSServiceRegisterRecordReply callback; + void * context; + AuthRecord rr; + + } connection; + + } u; + + // WARNING: Do not add fields after the resource record. That is where oversized RData space is allocated. +}; + +#define kDNSRecordServiceFixedSize sizeof_field( _DNSRecordRef_t, u.service ) +#define kDNSRecordConnectionFixedSize sizeof_field( _DNSRecordRef_t, u.connection ) + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// General + +#define DNSServiceLock() mDNSPlatformLock( gMDNSPtr ) +#define DNSServiceUnlock() mDNSPlatformUnlock( gMDNSPtr ) + +DEBUG_LOCAL void DNSServiceMDNSCallBack( mDNS * const inMDNS, mStatus inStatus ); + +// Domain Enumeration + +DEBUG_LOCAL void DNSServiceEnumerateDomainsRelease_direct( DNSServiceRef inRef ); +DEBUG_LOCAL void + DNSServiceEnumerateDomainsCallBack_direct( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ); + +// Service Discovery + +DEBUG_LOCAL void DNSServiceBrowseRelease_direct( DNSServiceRef inRef ); +DEBUG_LOCAL void + DNSServiceBrowseCallBack_direct( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ); + +DEBUG_LOCAL void DNSServiceResolveRelease_direct( DNSServiceRef inRef ); +DEBUG_LOCAL void + DNSServiceResolveCallBack_direct( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ); + +// Service Registration + +DEBUG_LOCAL void DNSServiceRegisterRelease_direct( DNSServiceRef inRef ); +DEBUG_LOCAL void DNSServiceRegisterCallBack_direct( mDNS * const inMDNS, ServiceRecordSet * const inSet, mStatus inResult ); +DEBUG_LOCAL void DNSServiceRegisterFree_direct( DNSServiceRef inRef ); + +DEBUG_LOCAL void DNSServiceUpdateRecordCallBack_direct( mDNS * const inMDNS, AuthRecord * const inRR, RData *inOldRData ); + +// Special Purpose + +DEBUG_LOCAL void DNSServiceCreateConnectionRelease_direct( DNSServiceRef inRef ); +DEBUG_LOCAL DNSRecordRef DNSServiceConnectionRecordRemove_direct( DNSServiceRef inRef, DNSRecordRef inRecordRef ); + +DEBUG_LOCAL void DNSServiceRegisterRecordCallBack_direct( mDNS *const inMDNS, AuthRecord *const inRR, mStatus inResult ); + +DEBUG_LOCAL void DNSServiceQueryRecordRelease_direct( DNSServiceRef inRef ); +DEBUG_STATIC void + DNSServiceQueryRecordCallBack_direct( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ); + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +mDNS gMDNS; +DEBUG_LOCAL mDNS * gMDNSPtr = NULL; +DEBUG_LOCAL CacheRecord * gMDNSCache = NULL; +DEBUG_LOCAL DNSServiceRef gDNSServiceRefList = NULL; +DEBUG_LOCAL DNSServiceRef gDNSCurrentServiceRef = NULL; +DEBUG_LOCAL DNSRecordRef gDNSCurrentRecord = NULL; + +#if 0 +#pragma mark - +#pragma mark == General == +#endif + +//=========================================================================================================================== +// DNSServiceInitialize_direct +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceInitialize_direct( DNSServiceInitializeFlags inFlags, int inCacheEntryCount ) +{ + DNSServiceErrorType err; + mDNSBool advertise; + + dlog( kDebugLevelTrace, DEBUG_NAME "initializing (flags=0x%08X, cache=%d/%d)\n", (int) inFlags, + inCacheEntryCount, ( inCacheEntryCount == 0 ) ? kDNSServiceCacheEntryCountDefault : inCacheEntryCount ); + + // Allocate the record cache. + + if( inCacheEntryCount == 0 ) + { + inCacheEntryCount = kDNSServiceCacheEntryCountDefault; + } + gMDNSCache = (CacheRecord *) malloc( inCacheEntryCount * sizeof( *gMDNSCache ) ); + require_action( gMDNSCache, exit, err = kDNSServiceErr_NoMemory ); + + // Initialize mDNS. + + if( inFlags & kDNSServiceInitializeFlagsAdvertise ) + { + advertise = mDNS_Init_AdvertiseLocalAddresses; + } + else + { + advertise = mDNS_Init_DontAdvertiseLocalAddresses; + } + err = mDNS_Init( &gMDNS, NULL, gMDNSCache, (mDNSu32) inCacheEntryCount, advertise, DNSServiceMDNSCallBack, NULL ); + require_noerr( err, exit ); + err = gMDNS.mDNSPlatformStatus; + require_noerr( err, exit ); + + gMDNSPtr = &gMDNS; + +exit: + dlog( kDebugLevelTrace, DEBUG_NAME "initializing done (err=%d %m)\n", err, err ); + if( err ) + { + DNSServiceFinalize_direct(); + } + return( err ); +} + +//=========================================================================================================================== +// DNSServiceFinalize_direct +//=========================================================================================================================== + +void DNSServiceFinalize_direct( void ) +{ + dlog( kDebugLevelTrace, DEBUG_NAME "finalizing\n" ); + + if( gMDNSPtr ) + { + mDNS_Close( &gMDNS ); + gMDNSPtr = NULL; + } + if( gMDNSCache ) + { + free( gMDNSCache ); + gMDNSCache = mDNSNULL; + } + + dlog( kDebugLevelTrace, DEBUG_NAME "finalizing done\n" ); +} + +//=========================================================================================================================== +// DNSServiceMDNSCallBack +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceMDNSCallBack( mDNS * const inMDNS, mStatus inStatus ) +{ + DEBUG_USE_ONLY( inMDNS ); + check( inMDNS ); + + dlog( kDebugLevelTrace, DEBUG_NAME "MDNS callback (status=%d)\n", inStatus ); + + if( inStatus == mStatus_ConfigChanged ) + { + // Notify all callbacks that the configuration has changed so they can do any additional processing. + // + // Warning: This is likely to call a user callback, which may change the object lists. Any code walking + // Warning: or changing these lists must use the "current" ptr mechanism to protect against this. + + DNSServiceLock(); + dlog( kDebugLevelTrace, DEBUG_NAME "handling ConfigChanged\n" ); + + check_string( !gDNSCurrentServiceRef, "somebody is already using gDNSCurrentServiceRef!" ); + gDNSCurrentServiceRef = gDNSServiceRefList; + while( gDNSCurrentServiceRef ) + { + DNSServiceRef obj; + + obj = gDNSCurrentServiceRef; + gDNSCurrentServiceRef = obj->next; + + // Call the callback with the ConfigChanged error code. Use the releaseCallBack to determine the type. + + if( obj->releaseCallBack == DNSServiceEnumerateDomainsRelease_direct ) + { + obj->u.domain.callback( obj, 0, 0, kDNSServiceErr_ConfigChanged, "", obj->context ); + } + else if( obj->releaseCallBack == DNSServiceRegisterRelease_direct ) + { + // If auto-renaming and the system name has changed then trigger a re-register with the new name. + + if( obj->u.reg.flags & kDNSServiceRegisterFlagsAutoName ) + { + if( !SameDomainLabel( obj->u.reg.name.c, gMDNSPtr->nicelabel.c ) ) + { + mStatus err; + + obj->u.reg.flags |= kDNSServiceRegisterFlagsAutoNameOnFree; + err = mDNS_DeregisterService( gMDNSPtr, obj->u.reg.set ); + check_noerr( err ); + } + } + else + { + check_string( obj->u.reg.callback, "not auto-naming, but no callback?" ); + + obj->u.reg.callback( obj, 0, kDNSServiceErr_ConfigChanged, "", "", "", obj->context ); + } + } + else if( obj->releaseCallBack == DNSServiceBrowseRelease_direct ) + { + obj->u.browse.callback( obj, 0, 0, kDNSServiceErr_ConfigChanged, "", "", "", obj->context ); + } + else if( obj->releaseCallBack == DNSServiceResolveRelease_direct ) + { + obj->u.resolve.callback( obj, 0, 0, kDNSServiceErr_ConfigChanged, "", "", 0, 0, NULL, obj->context ); + } + else if( obj->releaseCallBack == DNSServiceCreateConnectionRelease_direct ) + { + check_string( !gDNSCurrentRecord, "somebody is already using gDNSCurrentRecord!" ); + gDNSCurrentRecord = obj->u.connection.records; + while( gDNSCurrentRecord ) + { + DNSRecordRef record; + + record = gDNSCurrentRecord; + gDNSCurrentRecord = record->u.connection.next; + + record->u.connection.callback( record->u.connection.owner, record, 0, kDNSServiceErr_ConfigChanged, + record->u.connection.context ); + } + } + else if( obj->releaseCallBack == DNSServiceQueryRecordRelease_direct ) + { + obj->u.query.callback( obj, 0, 0, kDNSServiceErr_ConfigChanged, "", 0, 0, 0,NULL, 0, obj->context ); + } + } + DNSServiceUnlock(); + } +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// DNSServiceRefDeallocate_direct +//=========================================================================================================================== + +void DNSServiceRefDeallocate_direct( DNSServiceRef inRef ) +{ + check( inRef ); + + dlog( kDebugLevelNotice, DEBUG_NAME "%s: %#p\n", __ROUTINE__, inRef ); + + DNSServiceLock(); + if( inRef ) + { + DNSServiceRef * p; + + // Remove the object from the list. + + for( p = &gDNSServiceRefList; *p; p = &( *p )->next ) + { + if( *p == inRef ) + { + break; + } + } + check( *p ); + + // Release the object if it was found. + + if( *p ) + { + *p = inRef->next; + + // If somebody will be looking at this object next, move the current ptr to the next object. + + if( inRef == gDNSCurrentServiceRef ) + { + dlog( kDebugLevelInfo, DEBUG_NAME "deleting gDNSCurrentServiceRef (%#p)\n", inRef ); + gDNSCurrentServiceRef = inRef->next; + } + + check( inRef->releaseCallBack ); + if( inRef->releaseCallBack ) + { + inRef->releaseCallBack( inRef ); + } + } + } + DNSServiceUnlock(); +} + +#if 0 +#pragma mark - +#pragma mark == Domain Enumeration == +#endif + +//=========================================================================================================================== +// DNSServiceEnumerateDomains_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceEnumerateDomains_direct( + DNSServiceRef * outRef, + const DNSServiceFlags inFlags, + const uint32_t inInterfaceIndex, + const DNSServiceDomainEnumReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + mDNS_DomainType type; + mDNS_DomainType defaultType; + DNSServiceFlags flags; + mDNSInterfaceID interfaceID; + + obj = NULL; + DNSServiceLock(); + require_action( outRef, exit, err = kDNSServiceErr_BadParam ); + require_action( ( inFlags == kDNSServiceFlagsBrowseDomains ) || + ( inFlags == kDNSServiceFlagsRegistrationDomains ), + exit, err = kDNSServiceErr_BadFlags ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadParam ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->releaseCallBack = DNSServiceEnumerateDomainsRelease_direct; + obj->context = inContext; + obj->u.domain.callback = inCallBack; + + obj->next = gDNSServiceRefList; + gDNSServiceRefList = obj; + + // Start the browse operations. + + if( inFlags & kDNSServiceFlagsRegistrationDomains ) + { + type = mDNS_DomainTypeRegistration; + defaultType = mDNS_DomainTypeRegistrationDefault; + } + else + { + type = mDNS_DomainTypeBrowse; + defaultType = mDNS_DomainTypeBrowseDefault; + } + interfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex( gMDNSPtr, inInterfaceIndex ); + + err = mDNS_GetDomains( gMDNSPtr, &obj->u.domain.question, type, NULL, interfaceID, + DNSServiceEnumerateDomainsCallBack_direct, obj ); + require_noerr( err, exit ); + obj->u.domain.questionActive = mDNStrue; + + err = mDNS_GetDomains( gMDNSPtr, &obj->u.domain.defaultQuestion, defaultType, NULL, interfaceID, + DNSServiceEnumerateDomainsCallBack_direct, obj ); + require_noerr( err, exit ); + obj->u.domain.defaultQuestionActive = mDNStrue; + + // Call back immediately with "local." since that is always available for all types of browsing. + + flags = kDNSServiceFlagsDefault | kDNSServiceFlagsAdd; + inCallBack( obj, flags, inInterfaceIndex, kDNSServiceErr_NoError, "local.", inContext ); + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_direct( obj ); + } + DNSServiceUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceEnumerateDomainsRelease_direct +// +// Warning: Assumes the mDNS platform lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceEnumerateDomainsRelease_direct( DNSServiceRef inRef ) +{ + OSStatus err; + + check( inRef ); + + if( inRef->u.domain.questionActive ) + { + err = mDNS_StopGetDomains( gMDNSPtr, &inRef->u.domain.question ); + check_noerr( err ); + } + if( inRef->u.domain.defaultQuestionActive ) + { + err = mDNS_StopGetDomains( gMDNSPtr, &inRef->u.domain.defaultQuestion ); + check_noerr( err ); + } + free( inRef ); +} + +//=========================================================================================================================== +// DNSServiceEnumerateDomainsCallBack_direct +// +// Warning: Assumes the mDNS platform lock is held (held by mDNS before invoking this callback). +//=========================================================================================================================== + +DEBUG_LOCAL void + DNSServiceEnumerateDomainsCallBack_direct( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ) +{ + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + char domain[ MAX_ESCAPED_DOMAIN_NAME ]; + + DEBUG_UNUSED( inMDNS ); + + check( inQuestion ); + obj = (DNSServiceRef) inQuestion->QuestionContext; + check( obj ); + + flags = inAddRecord ? kDNSServiceFlagsAdd : kDNSServiceFlagsNone; + if( inAddRecord ) + { + if( inQuestion == &obj->u.domain.defaultQuestion ) + { + flags |= kDNSServiceFlagsDefault; + } + } + interfaceIndex = mDNSPlatformInterfaceIndexfromInterfaceID( &gMDNS, inAnswer->InterfaceID ); + ConvertDomainNameToCString( &inAnswer->rdata->u.name, domain ); + + obj->u.domain.callback( obj, flags, interfaceIndex, kDNSServiceErr_NoError, domain, obj->context ); +} + +#if 0 +#pragma mark - +#pragma mark == Service Registration == +#endif + +//=========================================================================================================================== +// DNSServiceRegister_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceRegister_direct( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + const char * inHost, + uint16_t inPort, + uint16_t inTXTSize, + const void * inTXT, + DNSServiceRegisterReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + mDNSBool autoName; + domainname * host; + domainname tempHost; + mDNSBool ok; + size_t size; + domainlabel name; + domainname type; + domainname domain; + mDNSIPPort port; + mDNSInterfaceID interfaceID; + + obj = NULL; + DNSServiceLock(); + require_action( outRef, exit, err = kDNSServiceErr_BadReference ); + require_action( ( inFlags == 0 ) || ( inFlags == kDNSServiceFlagsNoAutoRename ), exit, err = kDNSServiceErr_BadFlags ); + autoName = !inName || ( *inName == '\0' ); + require_action( !autoName || !( inFlags & kDNSServiceFlagsNoAutoRename ), exit, err = kDNSServiceErr_BadParam ); + require_action( inType, exit, err = kDNSServiceErr_BadParam ); + require_action( inTXT || ( inTXTSize == 0 ), exit, err = kDNSServiceErr_BadParam ); + require_action( inCallBack || autoName, exit, err = kDNSServiceErr_BadParam ); + + // Convert all the input strings and make sure they are valid. Use the system name if auto-naming. + + if( autoName ) + { + name = gMDNSPtr->nicelabel; + } + else + { + ok = MakeDomainLabelFromLiteralString( &name, inName ); + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + } + + ok = MakeDomainNameFromDNSNameString( &type, inType ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + if( !inDomain || ( *inDomain == '\0' ) ) + { + inDomain = "local."; + } + ok = MakeDomainNameFromDNSNameString( &domain, inDomain ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + // Set up the host name (if not using the default). + + host = NULL; + if( inHost && ( *inHost != '\0' ) ) + { + host = &tempHost; + ok = MakeDomainNameFromDNSNameString( host, inHost ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + AppendDomainName( host, &domain ); + } + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->releaseCallBack = DNSServiceRegisterRelease_direct; + obj->context = inContext; + obj->u.reg.flags = 0; + if( autoName ) + { + obj->u.reg.flags |= kDNSServiceRegisterFlagsAutoName; + } + if( !( inFlags & kDNSServiceFlagsNoAutoRename ) ) + { + obj->u.reg.flags |= kDNSServiceRegisterFlagsRenameOnConflict; + } + obj->u.reg.callback = inCallBack; + + // Allocate space for the records, including any extra space to handle an oversized TXT record. + + size = sizeof( ServiceRecordSet ); + if( inTXTSize > sizeof( RDataBody ) ) + { + size += ( inTXTSize - sizeof( RDataBody ) ); + } + obj->u.reg.set = (ServiceRecordSet *) calloc( 1, size ); + require_action( obj->u.reg.set, exit, err = kDNSServiceErr_NoMemory ); + + obj->next = gDNSServiceRefList; + gDNSServiceRefList = obj; + + // Register the service with mDNS. + + port.NotAnInteger = inPort; + interfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex( gMDNSPtr, inInterfaceIndex ); + + err = mDNS_RegisterService( gMDNSPtr, obj->u.reg.set, &name, &type, &domain, host, port, + (const mDNSu8 *) inTXT, inTXTSize, NULL, 0, interfaceID, DNSServiceRegisterCallBack_direct, obj ); + require_noerr( err, exit ); + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRegisterFree_direct( obj ); + } + DNSServiceUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceRegisterRelease_direct +// +// Warning: Assumes the mDNS platform lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRegisterRelease_direct( DNSServiceRef inRef ) +{ + mStatus err; + + check( inRef ); + + // Deregister the service. If an error occurs (which should never happen), we have to assume that mDNS does not + // know about the registration and will not call us back with mStatus_MemFree so we free the memory here. + // Otherwise, mDNS will call us back with a mStatus_MemFree error code when it is safe for us to free the memory. + + err = mDNS_DeregisterService( gMDNSPtr, inRef->u.reg.set ); + check_noerr( err ); + if( err != mStatus_NoError ) + { + DNSServiceRegisterFree_direct( inRef ); + } +} + +//=========================================================================================================================== +// DNSServiceRegisterCallBack_direct +// +// Warning: Assumes the mDNS platform lock is held (held by mDNS before invoking this callback). +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRegisterCallBack_direct( mDNS * const inMDNS, ServiceRecordSet * const inSet, mStatus inResult ) +{ + DNSServiceRef obj; + mDNSBool ok; + domainlabel name; + domainname type; + domainname domain; + char nameString[ MAX_DOMAIN_LABEL + 1 ]; + char typeString[ MAX_ESCAPED_DOMAIN_NAME ]; + char domainString[ MAX_ESCAPED_DOMAIN_NAME ]; + + DEBUG_UNUSED( inMDNS ); + DEBUG_UNUSED( inSet ); + + obj = (DNSServiceRef) inSet->ServiceContext; + check( obj ); + + if( inResult == mStatus_NoError ) + { + // Successful Registration. + + if( obj->u.reg.callback ) + { + ok = DeconstructServiceName( &inSet->RR_SRV.resrec.name, &name, &type, &domain ); + check( ok ); + + ConvertDomainLabelToCString_unescaped( &name, nameString ); + ConvertDomainNameToCString( &type, typeString ); + ConvertDomainNameToCString( &domain, domainString ); + + obj->u.reg.callback( obj, 0, kDNSServiceErr_NoError, nameString, typeString, domainString, obj->context ); + } + } + else if( inResult == mStatus_MemFree ) + { + // If the AutoNameOnFree flag is set, it means we should re-register with the system name instead of freeing. + // Otherwise, mDNS is done with the memory so we can safely free it. + + if( obj->u.reg.flags & kDNSServiceRegisterFlagsAutoNameOnFree ) + { + obj->u.reg.flags &= ~kDNSServiceRegisterFlagsAutoNameOnFree; + obj->u.reg.name = gMDNSPtr->nicelabel; + inResult = mDNS_RenameAndReregisterService( gMDNSPtr, obj->u.reg.set, &obj->u.reg.name ); + check_noerr( inResult ); + } + if( inResult != mStatus_NoError ) + { + DNSServiceRegisterFree_direct( obj ); + } + } + else if( inResult == mStatus_NameConflict ) + { + // Name conflict. If the auto renaming flags are enabled silently rename and re-register. + // Otherwise, mDNS will not send call us again with mStatus_MemFree so free the memory here. + + if( obj->u.reg.flags & ( kDNSServiceRegisterFlagsAutoName | kDNSServiceRegisterFlagsRenameOnConflict ) ) + { + inResult = mDNS_RenameAndReregisterService( gMDNSPtr, obj->u.reg.set, mDNSNULL ); + } + if( inResult != mStatus_NoError ) + { + if( obj->u.reg.callback ) + { + obj->u.reg.callback( obj, 0, kDNSServiceErr_NameConflict, "", "", "", obj->context ); + } + DNSServiceRegisterFree_direct( obj ); + } + } + else + { + dlog( kDebugLevelNotice, "unknown register result (%d)\n", inResult ); + if( obj->u.reg.callback ) + { + obj->u.reg.callback( obj, 0, inResult, "", "", "", obj->context ); + } + } +} + +//=========================================================================================================================== +// DNSServiceRegisterFree_direct +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRegisterFree_direct( DNSServiceRef inRef ) +{ + check( inRef ); + + if( inRef->u.reg.set ) + { + // Note: Each "Extras" record is a "DNSServiceRef", which is just a syntactic wrapper for ExtraResourceRecord. + // This avoids the need for casting and simplifies access, but still allows the following loop to work correctly. + + while( inRef->u.reg.set->Extras ) + { + ExtraResourceRecord * extra; + + extra = inRef->u.reg.set->Extras; + inRef->u.reg.set->Extras = extra->next; + + free( extra ); + } + free( inRef->u.reg.set ); + } + free( inRef ); +} + +//=========================================================================================================================== +// DNSServiceAddRecord_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceAddRecord_direct( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint16_t inRRType, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ) +{ + DNSServiceErrorType err; + size_t size; + DNSRecordRef obj; + ExtraResourceRecord * extra; + + obj = NULL; + DNSServiceLock(); + require_action( inRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inRef->releaseCallBack == DNSServiceRegisterRelease_direct, exit, err = kDNSServiceErr_BadParam ); + require_action( inRef->u.reg.set, exit, err = kDNSServiceErr_NotInitialized ); + require_action( outRecordRef, exit, err = kDNSServiceErr_BadParam ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inRData && ( inRDataSize > 0 ), exit, err = kDNSServiceErr_BadParam ); + + // Allocate and initialize the record. Allocate oversized record space at the end of the record. + + size = ( inRDataSize > sizeof( RDataBody ) ) ? inRDataSize : sizeof( RDataBody ); + obj = (DNSRecordRef) calloc( 1, ( kDNSRecordServiceFixedSize - sizeof( RDataBody ) ) + size ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + extra = &obj->u.service.extra; + extra->r.resrec.rrtype = inRRType; + extra->r.resrec.rdlength = inRDataSize; + extra->r.rdatastorage.MaxRDLength = (mDNSu16) size; + memcpy( extra->r.rdatastorage.u.data, inRData, inRDataSize ); + + // Register the record with mDNS. + + err = mDNS_AddRecordToService( gMDNSPtr, inRef->u.reg.set, extra, &extra->r.rdatastorage, inTTL ); + require_noerr( err, exit ); + + // Success! + + *outRecordRef = (DNSRecordRef) obj; + obj = NULL; + +exit: + if( obj ) + { + free( obj ); + } + DNSServiceUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceUpdateRecord_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceUpdateRecord_direct( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ) +{ + DNSServiceErrorType err; + AuthRecord * rr; + size_t size; + RData * rd; + + rd = NULL; + DNSServiceLock(); + require_action( inRef, exit, err = kDNSServiceErr_BadReference ); + require_action( ( inRef->releaseCallBack == DNSServiceRegisterRelease_direct ) || + ( inRef->releaseCallBack == DNSServiceCreateConnectionRelease_direct ), + exit, err = kDNSServiceErr_BadParam ); + require_action( inRef->u.reg.set, exit, err = kDNSServiceErr_NotInitialized ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inRData, exit, err = kDNSServiceErr_BadParam ); + + // Get the embedded AuthRecord from the DNSRecordRef. Determine the type of DNSServiceRef from the releaseCallBack. + + if( inRef->releaseCallBack == DNSServiceRegisterRelease_direct ) + { + rr = inRecordRef ? &inRecordRef->u.service.extra.r : &inRef->u.reg.set->RR_TXT; + } + else if( inRef->releaseCallBack == DNSServiceCreateConnectionRelease_direct ) + { + require_action( inRecordRef, exit, err = kDNSServiceErr_BadReference ); + rr = &inRecordRef->u.connection.rr; + } + else + { + dlog( kDebugLevelError, DEBUG_NAME "trying to remove a DNSRecordRef with an unsupported DNSServiceRef\n" ); + err = kDNSServiceErr_Unsupported; + goto exit; + } + + // Allocate and initialize the data. Allocate oversized data at the end of the record. + + size = ( inRDataSize > sizeof( RDataBody ) ) ? inRDataSize : sizeof( RDataBody ); + rd = (RData *) calloc( 1, ( sizeof( *rd ) - sizeof( RDataBody ) ) + size ); + require_action( rd, exit, err = kDNSServiceErr_NoMemory ); + + rd->MaxRDLength = (mDNSu16) size; + memcpy( rd->u.data, inRData, inRDataSize ); + + // Update the record. A NULL record means to update the primary TXT record. + + err = mDNS_Update( gMDNSPtr, rr, inTTL, inRDataSize, rd, DNSServiceUpdateRecordCallBack_direct ); + require_noerr( err, exit ); + + // Success! + + rd = NULL; + +exit: + if( rd ) + { + free( rd ); + } + DNSServiceUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceUpdateRecord_direct +// +// Warning: It is not safe to make any mDNS calls here. +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceUpdateRecordCallBack_direct( mDNS * const inMDNS, AuthRecord * const inRR, RData *inOldRData ) +{ + DEBUG_UNUSED( inMDNS ); + + check( inOldRData ); + + if( inOldRData != &inRR->rdatastorage ) + { + free( inOldRData ); + } +} + +//=========================================================================================================================== +// DNSServiceRemoveRecord_direct +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceRemoveRecord_direct( DNSServiceRef inRef, DNSRecordRef inRecordRef, DNSServiceFlags inFlags ) +{ + DNSServiceErrorType err; + + DNSServiceLock(); + require_action( inRef, exit, err = kDNSServiceErr_BadReference ); + require_action( ( inRef->releaseCallBack == DNSServiceRegisterRelease_direct ) || + ( inRef->releaseCallBack == DNSServiceCreateConnectionRelease_direct ), + exit, err = kDNSServiceErr_BadParam ); + require_action( inRef->u.reg.set, exit, err = kDNSServiceErr_NotInitialized ); + require_action( inRecordRef, exit, err = kDNSServiceErr_BadParam ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + + // Get the embedded AuthRecord from the DNSRecordRef. Determine the type of DNSServiceRef from the releaseCallBack. + + if( inRef->releaseCallBack == DNSServiceRegisterRelease_direct ) + { + err = mDNS_RemoveRecordFromService( gMDNSPtr, inRef->u.reg.set, &inRecordRef->u.service.extra ); + free( inRecordRef ); + require_noerr( err, exit ); + } + else if( inRef->releaseCallBack == DNSServiceCreateConnectionRelease_direct ) + { + mDNSBool freeRR; + + inRecordRef = DNSServiceConnectionRecordRemove_direct( inRef, inRecordRef ); + require_action( inRecordRef, exit, err = kDNSServiceErr_BadParam ); + + freeRR = ( inRecordRef->u.connection.rr.resrec.RecordType != kDNSRecordTypeShared ); + err = mDNS_Deregister( gMDNSPtr, &inRecordRef->u.connection.rr ); + check_noerr( err ); + if( freeRR || ( err != mStatus_NoError ) ) + { + free( inRecordRef ); + } + } + else + { + dlog( kDebugLevelError, DEBUG_NAME "trying to remove a DNSRecordRef with an unsupported DNSServiceRef\n" ); + err = kDNSServiceErr_Unsupported; + goto exit; + } + +exit: + DNSServiceUnlock(); + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Service Discovery == +#endif + +//=========================================================================================================================== +// DNSServiceBrowse_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceBrowse_direct( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inType, + const char * inDomain, + DNSServiceBrowseReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + mDNSBool ok; + domainname type; + domainname domain; + mDNSInterfaceID interfaceID; + + obj = NULL; + DNSServiceLock(); + require_action( outRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inType, exit, err = kDNSServiceErr_BadParam ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadParam ); + + // Convert the input strings and make sure they are valid. + + ok = MakeDomainNameFromDNSNameString( &type, inType ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + if( !inDomain || ( *inDomain == '\0' ) ) + { + inDomain = "local."; + } + ok = MakeDomainNameFromDNSNameString( &domain, inDomain ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->releaseCallBack = DNSServiceBrowseRelease_direct; + obj->context = inContext; + obj->u.browse.callback = inCallBack; + + obj->next = gDNSServiceRefList; + gDNSServiceRefList = obj; + + // Start browsing. + + interfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex( gMDNSPtr, inInterfaceIndex ); + + err = mDNS_StartBrowse( gMDNSPtr, &obj->u.browse.question, &type, &domain, interfaceID, + DNSServiceBrowseCallBack_direct, obj ); + require_noerr( err, exit ); + obj->u.browse.questionActive = mDNStrue; + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_direct( obj ); + } + DNSServiceUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceBrowseRelease_direct +// +// Warning: Assumes the mDNS platform lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceBrowseRelease_direct( DNSServiceRef inRef ) +{ + OSStatus err; + + check( inRef ); + + if( inRef->u.browse.questionActive ) + { + err = mDNS_StopBrowse( gMDNSPtr, &inRef->u.browse.question ); + check_noerr( err ); + } + free( inRef ); +} + +//=========================================================================================================================== +// DNSServiceBrowseCallBack_direct +// +// Warning: Assumes the mDNS platform lock is held (held by mDNS before invoking this callback). +//=========================================================================================================================== + +DEBUG_LOCAL void + DNSServiceBrowseCallBack_direct( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ) +{ + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + mDNSBool ok; + domainlabel name; + domainname type; + domainname domain; + char nameString[ MAX_DOMAIN_LABEL + 1 ]; + char typeString[ MAX_ESCAPED_DOMAIN_NAME ]; + char domainString[ MAX_ESCAPED_DOMAIN_NAME ]; + + DEBUG_UNUSED( inMDNS ); + check( inQuestion ); + obj = (DNSServiceRef) inQuestion->QuestionContext; + check( obj ); + + flags = inAddRecord ? kDNSServiceFlagsAdd : kDNSServiceFlagsNone; + interfaceIndex = mDNSPlatformInterfaceIndexfromInterfaceID( &gMDNS, inAnswer->InterfaceID ); + + ok = DeconstructServiceName( &inAnswer->rdata->u.name, &name, &type, &domain ); + check( ok ); + + ConvertDomainLabelToCString_unescaped( &name, nameString ); + ConvertDomainNameToCString( &type, typeString ); + ConvertDomainNameToCString( &domain, domainString ); + + obj->u.browse.callback( obj, flags, interfaceIndex, kDNSServiceErr_NoError, nameString, typeString, domainString, + obj->context ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// DNSServiceResolve_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceResolve_direct( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + DNSServiceResolveReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + mDNSBool ok; + domainlabel name; + domainname type; + domainname domain; + domainname fqdn; + mDNSInterfaceID interfaceID; + DNSQuestion * q; + + obj = NULL; + DNSServiceLock(); + dlog( kDebugLevelTrace, DEBUG_NAME "%s\n", __ROUTINE__ ); + require_action( outRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inName, exit, err = kDNSServiceErr_BadParam ); + require_action( inType, exit, err = kDNSServiceErr_BadParam ); + if( !inDomain || ( *inDomain == '\0' ) ) inDomain = "local."; + require_action( inCallBack, exit, err = kDNSServiceErr_BadParam ); + + // Convert all the input strings and make sure they are valid. + + ok = MakeDomainLabelFromLiteralString( &name, inName ); + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + ok = MakeDomainNameFromDNSNameString( &type, inType ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + ok = MakeDomainNameFromDNSNameString( &domain, inDomain ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + ok = ConstructServiceName( &fqdn, &name, &type, &domain ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + interfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex( gMDNSPtr, inInterfaceIndex ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->releaseCallBack = DNSServiceResolveRelease_direct; + obj->context = inContext; + obj->u.resolve.flags = inFlags; + obj->u.resolve.callback = inCallBack; + + obj->next = gDNSServiceRefList; + gDNSServiceRefList = obj; + + // Start the SRV query. + + q = &obj->u.resolve.srvQuestion; + q->InterfaceID = interfaceID; + AssignDomainName( q->qname, fqdn ); + q->qtype = kDNSType_SRV; + q->qclass = kDNSClass_IN; + q->QuestionCallback = DNSServiceResolveCallBack_direct; + q->QuestionContext = obj; + + err = mDNS_StartQuery( gMDNSPtr, q ); + require_noerr( err, exit ); + obj->u.resolve.srvQuestionActive = mDNStrue; + + // Start the TXT query. + + q = &obj->u.resolve.txtQuestion; + q->InterfaceID = interfaceID; + AssignDomainName( q->qname, fqdn ); + q->qtype = kDNSType_TXT; + q->qclass = kDNSClass_IN; + q->QuestionCallback = DNSServiceResolveCallBack_direct; + q->QuestionContext = obj; + + err = mDNS_StartQuery( gMDNSPtr, q ); + require_noerr( err, exit ); + obj->u.resolve.txtQuestionActive = mDNStrue; + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_direct( obj ); + } + DNSServiceUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceResolveRelease_direct +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceResolveRelease_direct( DNSServiceRef inRef ) +{ + OSStatus err; + + check( inRef ); + + if( inRef->u.resolve.srvQuestionActive ) + { + inRef->u.resolve.srvQuestionActive = mDNSfalse; + err = mDNS_StopQuery( gMDNSPtr, &inRef->u.resolve.srvQuestion ); + check_noerr( err ); + } + if( inRef->u.resolve.txtQuestionActive ) + { + inRef->u.resolve.txtQuestionActive = mDNSfalse; + err = mDNS_StopQuery( gMDNSPtr, &inRef->u.resolve.txtQuestion ); + check_noerr( err ); + } + free( inRef ); +} + +//=========================================================================================================================== +// DNSServiceResolveCallBack_direct +//=========================================================================================================================== + +DEBUG_LOCAL void + DNSServiceResolveCallBack_direct( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ) +{ + DNSServiceRef obj; + const ResourceRecord ** answer; + uint32_t ifi; + char fullName[ MAX_ESCAPED_DOMAIN_NAME ]; + char hostName[ MAX_ESCAPED_DOMAIN_NAME ]; + uint16_t port; + const char * txt; + uint16_t txtSize; + + DEBUG_UNUSED( inMDNS ); + + check( inQuestion ); + obj = (DNSServiceRef) inQuestion->QuestionContext; + check( obj ); + check( inAnswer ); + + // Select the answer based on the type. + + if( inAnswer->rrtype == kDNSType_SRV ) answer = &obj->u.resolve.srvAnswer; + else if( inAnswer->rrtype == kDNSType_TXT ) answer = &obj->u.resolve.txtAnswer; + else + { + dlog( kDebugLevelError, DEBUG_NAME "%s: unexpected rrtype (%d)\n", __ROUTINE__, inAnswer->rrtype ); + goto exit; + } + + // If the record is being removed, invalidate the previous answer. Otherwise, update with the new answer. + + if( !inAddRecord ) + { + if( *answer == inAnswer ) *answer = NULL; + goto exit; + } + *answer = inAnswer; + + // Only deliver the result if we have both answers. + + if( !obj->u.resolve.srvAnswer || !obj->u.resolve.txtAnswer ) + { + goto exit; + } + + // Convert the results to the appropriate format and call the callback. + + ifi = mDNSPlatformInterfaceIndexfromInterfaceID( &gMDNS, obj->u.resolve.srvAnswer->InterfaceID ); + ConvertDomainNameToCString( &obj->u.resolve.srvAnswer->name, fullName ); + ConvertDomainNameToCString( &obj->u.resolve.srvAnswer->rdata->u.srv.target, hostName ); + port = obj->u.resolve.srvAnswer->rdata->u.srv.port.NotAnInteger; + txt = (const char *) obj->u.resolve.txtAnswer->rdata->u.txt.c; + txtSize = obj->u.resolve.txtAnswer->rdlength; + + obj->u.resolve.callback( obj, 0, ifi, kDNSServiceErr_NoError, fullName, hostName, port, txtSize, txt, obj->context ); + +exit: + return; +} + +#if 0 +#pragma mark - +#pragma mark == Special Purpose == +#endif + +//=========================================================================================================================== +// DNSServiceCreateConnection_direct +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceCreateConnection_direct( DNSServiceRef *outRef ) +{ + OSStatus err; + DNSServiceRef obj; + + DNSServiceLock(); + require_action( outRef, exit, err = kDNSServiceErr_BadReference ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->releaseCallBack = DNSServiceCreateConnectionRelease_direct; + + obj->next = gDNSServiceRefList; + gDNSServiceRefList = obj; + + // Success! + + *outRef = obj; + err = kNoErr; + +exit: + DNSServiceUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceCreateConnectionRelease_direct +// +// Warning: Assumes the mDNS platform lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceCreateConnectionRelease_direct( DNSServiceRef inRef ) +{ + check( inRef ); + + while( inRef->u.connection.records ) + { + DNSRecordRef record; + mStatus err; + mDNSBool freeRR; + + record = inRef->u.connection.records; + inRef->u.connection.records = record->u.connection.next; + + // If somebody will be looking at this object next, move the current ptr to the next object. + + if( record == gDNSCurrentRecord ) + { + dlog( kDebugLevelInfo, DEBUG_NAME "deleting gDNSCurrentRecord (%#p)\n", record ); + gDNSCurrentRecord = record->u.connection.next; + } + + freeRR = ( record->u.connection.rr.resrec.RecordType != kDNSRecordTypeShared ); + err = mDNS_Deregister( gMDNSPtr, &record->u.connection.rr ); + check_noerr( err ); + if( freeRR || ( err != mStatus_NoError ) ) + { + free( record ); + } + } + free( inRef ); +} + +//=========================================================================================================================== +// DNSServiceConnectionRecordRemove_direct +// +// Warning: Assumes the mDNS platform lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL DNSRecordRef DNSServiceConnectionRecordRemove_direct( DNSServiceRef inRef, DNSRecordRef inRecordRef ) +{ + DNSRecordRef * p; + + for( p = &inRef->u.connection.records; *p; p = &( *p )->u.connection.next ) + { + if( *p == inRecordRef ) + { + break; + } + } + inRecordRef = *p; + if( inRecordRef ) + { + *p = inRecordRef->u.connection.next; + + // If somebody will be looking at this object next, move the current ptr to the next object. + + if( inRecordRef == gDNSCurrentRecord ) + { + gDNSCurrentRecord = inRecordRef->u.connection.next; + } + } + return( inRecordRef ); +} + +//=========================================================================================================================== +// DNSServiceRegisterRecord_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceRegisterRecord_direct( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + DNSServiceRegisterRecordReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + size_t size; + DNSRecordRef obj; + AuthRecord * rr; + mDNSBool ok; + + obj = NULL; + DNSServiceLock(); + require_action( inRef, exit, err = kDNSServiceErr_BadReference ); + require_action( outRecordRef, exit, err = kDNSServiceErr_BadParam ); + require_action( ( inFlags == kDNSServiceFlagsShared ) || + ( inFlags == kDNSServiceFlagsUnique ), + exit, err = kDNSServiceErr_BadFlags ); + require_action( inRData && ( inRDataSize > 0 ), exit, err = kDNSServiceErr_BadParam ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadParam ); + + // Allocate and initialize the record. Allocate oversized record space at the end of the record. + + size = ( inRDataSize > sizeof( RDataBody ) ) ? inRDataSize : sizeof( RDataBody ); + obj = (DNSRecordRef) calloc( 1, ( kDNSRecordConnectionFixedSize - sizeof( RDataBody ) ) + size ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->u.connection.owner = inRef; + obj->u.connection.callback = inCallBack; + obj->u.connection.context = inContext; + + obj->u.connection.next = inRef->u.connection.records; + inRef->u.connection.records = obj; + + rr = &obj->u.connection.rr; + rr->resrec.RecordType = (mDNSu8)( ( inFlags == kDNSServiceFlagsShared ) ? kDNSRecordTypeShared : kDNSRecordTypeUnique ); + rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex( gMDNSPtr, inInterfaceIndex ); + + ok = MakeDomainNameFromDNSNameString( &rr->resrec.name, inName ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + rr->resrec.rrtype = inRRType; + rr->resrec.rrclass = inRRClass; + rr->resrec.rroriginalttl = inTTL; + rr->resrec.rdlength = inRDataSize; + rr->resrec.rdata = &rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = inRDataSize; + memcpy( rr->resrec.rdata->u.data, inRData, inRDataSize ); + rr->RecordContext = obj; + rr->RecordCallback = DNSServiceRegisterRecordCallBack_direct; + + // Register the record with mDNS. + + err = mDNS_Register( gMDNSPtr, rr ); + require_noerr( err, exit ); + + // Success! + + *outRecordRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceConnectionRecordRemove_direct( inRef, obj ); + free( obj ); + } + DNSServiceUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceRegisterRecord_direct +// +// Warning: Assumes the mDNS platform lock is held (held by mDNS before invoking this callback). +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRegisterRecordCallBack_direct( mDNS * const inMDNS, AuthRecord * const inRR, mStatus inResult ) +{ + DNSRecordRef obj; + + DEBUG_UNUSED( inMDNS ); + + check( inRR ); + obj = (DNSRecordRef) inRR->RecordContext; + check( obj ); + + if( inResult == mStatus_MemFree ) + { + DNSServiceConnectionRecordRemove_direct( obj->u.connection.owner, obj ); + free( inRR ); + } + else + { + obj->u.connection.callback( obj->u.connection.owner, obj, 0, inResult, obj->u.connection.context ); + } +} + +//=========================================================================================================================== +// DNSServiceQueryRecord_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceQueryRecord_direct( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + DNSServiceQueryRecordReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + DNSQuestion * q; + mDNSBool ok; + + obj = NULL; + DNSServiceLock(); + require_action( outRef, exit, err = kDNSServiceErr_BadParam ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inName, exit, err = kDNSServiceErr_BadParam ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadParam ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->releaseCallBack = DNSServiceQueryRecordRelease_direct; + obj->context = inContext; + obj->u.query.callback = inCallBack; + + q = &obj->u.query.question; + q->InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex( gMDNSPtr, inInterfaceIndex ); + ok = MakeDomainNameFromDNSNameString( &q->qname, inName ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + q->qtype = inRRType; + q->qclass = inRRClass; + q->QuestionCallback = DNSServiceQueryRecordCallBack_direct; + q->QuestionContext = obj; + + obj->next = gDNSServiceRefList; + gDNSServiceRefList = obj; + + // Start the query with mDNS. + + err = mDNS_StartQuery( gMDNSPtr, q ); + require_noerr( err, exit ); + obj->u.query.questionActive = mDNStrue; + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_direct( obj ); + } + DNSServiceUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceQueryRecordRelease_direct +// +// Warning: Assumes the mDNS platform lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceQueryRecordRelease_direct( DNSServiceRef inRef ) +{ + OSStatus err; + + check( inRef ); + + if( inRef->u.query.questionActive ) + { + err = mDNS_StopQuery( gMDNSPtr, &inRef->u.query.question ); + check_noerr( err ); + } + free( inRef ); +} + +//=========================================================================================================================== +// DNSServiceQueryRecordCallBack_direct +// +// Warning: Assumes the mDNS platform lock is held (held by mDNS before invoking this callback). +//=========================================================================================================================== + +DEBUG_LOCAL void + DNSServiceQueryRecordCallBack_direct( + mDNS * const inMDNS, + DNSQuestion * inQuestion, + const ResourceRecord * const inAnswer, + mDNSBool inAddRecord ) +{ + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + char name[ MAX_ESCAPED_DOMAIN_NAME ]; + + DEBUG_UNUSED( inMDNS ); + + check( inQuestion ); + obj = (DNSServiceRef) inQuestion->QuestionContext; + check( obj ); + check( inAnswer ); + + flags = inAddRecord ? kDNSServiceFlagsAdd : kDNSServiceFlagsNone; + interfaceIndex = mDNSPlatformInterfaceIndexfromInterfaceID( &gMDNS, inAnswer->InterfaceID ); + ConvertDomainNameToCString( &inAnswer->name, name ); + obj->u.query.callback( obj, flags, interfaceIndex, kDNSServiceErr_NoError, name, inAnswer->rrtype, inAnswer->rrclass, + inAnswer->rdlength, &inAnswer->rdata->u, inAddRecord ? inAnswer->rroriginalttl : 0, obj->context ); +} + +//=========================================================================================================================== +// DNSServiceReconfirmRecord_direct +//=========================================================================================================================== + +void + DNSServiceReconfirmRecord_direct( + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData ) +{ + DNSServiceErrorType err; + size_t size; + AuthRecord * rr; + mDNSBool ok; + + rr = NULL; + DNSServiceLock(); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inName, exit, err = kDNSServiceErr_BadParam ); + require_action( inRData && ( inRDataSize > 0 ), exit, err = kDNSServiceErr_BadParam ); + + size = ( inRDataSize > sizeof( RDataBody ) ) ? inRDataSize : sizeof( RDataBody ); + rr = (AuthRecord *) calloc( 1, ( sizeof( *rr ) - sizeof( RDataBody ) ) + size ); + require_action( rr, exit, err = kDNSServiceErr_NoMemory ); + + rr->resrec.RecordType = (mDNSu8)( ( inFlags == kDNSServiceFlagsShared ) ? kDNSRecordTypeShared : kDNSRecordTypeUnique ); + rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex( gMDNSPtr, inInterfaceIndex ); + + ok = MakeDomainNameFromDNSNameString( &rr->resrec.name, inName ) != NULL; + require_action( ok, exit, err = kDNSServiceErr_BadParam ); + + rr->resrec.rrtype = inRRType; + rr->resrec.rrclass = inRRClass; + rr->resrec.rdlength = inRDataSize; + rr->resrec.rdata = &rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = inRDataSize; + memcpy( rr->resrec.rdata->u.data, inRData, inRDataSize ); + + err = mDNS_ReconfirmByValue( gMDNSPtr, &rr->resrec ); + check( ( err == mStatus_BadReferenceErr ) || ( err == mStatus_NoError ) ); + +exit: + if( rr ) + { + free( rr ); + } + DNSServiceUnlock(); +} + +#ifdef __cplusplus + } +#endif + +#endif // DNS_SD_DIRECT_ENABLED diff --git a/mDNSWindows/DNSSDDirect.h b/mDNSWindows/DNSSDDirect.h new file mode 100644 index 0000000..be7acb0 --- /dev/null +++ b/mDNSWindows/DNSSDDirect.h @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DNSSDDirect.h,v $ +Revision 1.1 2004/01/30 02:46:15 bradley +Portable implementation of the DNS-SD API. This interacts with mDNSCore to perform all the real work +of the DNS-SD API. This code does not rely on any platform-specifics so it should run on any platform +with an mDNS platform plugin available. Software that cannot or does not want to use the IPC mechanism +(e.g. Windows CE, VxWorks, etc.) can use this code directly without any of the IPC pieces. + +*/ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header DNSSDDirect.h + + @abstract Direct (compiled-in) implementation of DNS-SD APIs. + + @discussion + + Portable implementation of the DNS-SD API. This interacts with mDNSCore to perform all the real work of the DNS-SD API. + This code does not rely on any platform-specifics so it should run on any platform with an mDNS platform plugin + available. Software that cannot or does not want to use the IPC mechanism (e.g. Windows CE, VxWorks, etc.) can use this + code directly without any of the IPC pieces. +*/ + +#ifndef __DNS_SD_DIRECT__ +#define __DNS_SD_DIRECT__ + +#include "CommonServices.h" + +#include "DNSSD.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == General == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kDNSServiceCacheEntryCountDefault + + @abstract Default number of mDNS cache entries. +*/ + +#define kDNSServiceCacheEntryCountDefault 512 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceInitialize_direct + + @abstract Initializes the DNSService API. No DNSService API's should be called before this call returns successfully. +*/ + +DNSServiceErrorType DNSServiceInitialize_direct( DNSServiceInitializeFlags inFlags, int inCacheEntryCount ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceFinalize_direct + + @abstract Finalizes the DNSService API. No DNSService API's should be called after this call is made. +*/ + +void DNSServiceFinalize_direct( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRefDeallocate_direct + + @abstract Direct version of DNSServiceRefDeallocate. +*/ + +void DNSServiceRefDeallocate_direct( DNSServiceRef inRef ); + +#if 0 +#pragma mark == Domain Enumeration == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceEnumerateDomains_client + + @abstract Direct version of DNSServiceEnumerateDomains. +*/ + +DNSServiceErrorType + DNSServiceEnumerateDomains_direct( + DNSServiceRef * outRef, + const DNSServiceFlags inFlags, + const uint32_t inInterfaceIndex, + const DNSServiceDomainEnumReply inCallBack, + void * inContext ); + +#if 0 +#pragma mark == Service Registration == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRegister_direct + + @abstract Direct version of DNSServiceRegister. +*/ + +DNSServiceErrorType + DNSServiceRegister_direct( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + const char * inHost, + uint16_t inPort, + uint16_t inTXTSize, + const void * inTXT, + DNSServiceRegisterReply inCallBack, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceAddRecord_direct + + @abstract Direct version of DNSServiceAddRecord. +*/ + +DNSServiceErrorType + DNSServiceAddRecord_direct( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint16_t inRRType, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceUpdateRecord_direct + + @abstract Direct version of DNSServiceUpdateRecord. +*/ + +DNSServiceErrorType + DNSServiceUpdateRecord_direct( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRemoveRecord_direct + + @abstract Direct version of DNSServiceRemoveRecord. +*/ + +DNSServiceErrorType DNSServiceRemoveRecord_direct( DNSServiceRef inRef, DNSRecordRef inRecordRef, DNSServiceFlags inFlags ); + +#if 0 +#pragma mark == Service Discovery == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceBrowse_direct + + @abstract Direct version of DNSServiceBrowse. +*/ + +DNSServiceErrorType + DNSServiceBrowse_direct( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inType, + const char * inDomain, + DNSServiceBrowseReply inCallBack, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceResolve_direct + + @abstract Direct version of DNSServiceResolve. +*/ + +DNSServiceErrorType + DNSServiceResolve_direct( + DNSServiceRef * inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + DNSServiceResolveReply inCallBack, + void * inContext ); + +#if 0 +#pragma mark == Special Purpose == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceCreateConnection_direct + + @abstract Direct version of DNSServiceCreateConnection. +*/ + +DNSServiceErrorType DNSServiceCreateConnection_direct( DNSServiceRef *outRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRegisterRecord_direct + + @abstract Direct version of DNSServiceRegisterRecord. +*/ + +DNSServiceErrorType + DNSServiceRegisterRecord_direct( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + DNSServiceRegisterRecordReply inCallBack, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceQueryRecord_direct + + @abstract Direct version of DNSServiceQueryRecord. +*/ + +DNSServiceErrorType + DNSServiceQueryRecord_direct( + DNSServiceRef * outRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + DNSServiceQueryRecordReply inCallBack, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceReconfirmRecord_direct + + @abstract Direct version of DNSServiceReconfirmRecord. +*/ + +void + DNSServiceReconfirmRecord_direct( + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData ); + +#ifdef __cplusplus + } +#endif + +#endif // __DNS_SD_DIRECT__ diff --git a/mDNSWindows/DNSServices/DNSServiceDiscovery.c b/mDNSWindows/DNSServices/DNSServiceDiscovery.c index 77e3dcb..e8d5e2e 100644 --- a/mDNSWindows/DNSServices/DNSServiceDiscovery.c +++ b/mDNSWindows/DNSServices/DNSServiceDiscovery.c @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,24 @@ Change History (most recent first): $Log: DNSServiceDiscovery.c,v $ +Revision 1.7 2004/05/08 12:24:48 bradley +Removed trailing character from zero value to fix compile error. + +Revision 1.6 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +: Remove flags with zero value +: Passing in NULL causes a crash. + +Revision 1.5 2004/01/30 02:56:34 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.4 2003/11/14 20:59:10 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.3 2003/10/04 04:47:08 bradley +Changed DNSServiceRegistrationCreate to treat the port in network byte order for end-to-end consistency. + Revision 1.2 2003/08/20 07:06:34 bradley Update to APSL 2.0. Updated change history to match other mDNSResponder files. @@ -55,7 +75,6 @@ Platform-neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDi #endif #include "mDNSClientAPI.h" -#include "mDNSPlatformFunctions.h" #include "DNSServices.h" #include "DNSServiceDiscovery.h" @@ -179,6 +198,7 @@ dns_service_discovery_ref dns_service_discovery_ref obj; void * txt; size_t txtSize; + DNSOpaque16 port; DNSRegistrationRef registration; result = NULL; @@ -203,7 +223,9 @@ dns_service_discovery_ref require_noerr( err, exit ); } - err = DNSRegistrationCreate( kDNSRegistrationFlagPreFormattedTextRecord, inName, inType, inDomain, inPort, txt, + port.v8[ 0 ] = (DNSUInt8)( inPort >> 8 ); + port.v8[ 1 ] = (DNSUInt8)( inPort & 0xFF ); + err = DNSRegistrationCreate( kDNSRegistrationFlagPreFormattedTextRecord, inName, inType, inDomain, port.v16, txt, (DNSCount) txtSize, NULL, NULL, DNSServiceRegistrationPrivateCallBack, obj, ®istration ); require_noerr( err, exit ); obj->ref = registration; @@ -426,7 +448,7 @@ DNS_LOCAL void if( callback ) { callback( DNSServiceDomainEnumerationReplyAddDomain, inEvent->data.addDomain.domain, - DNSServiceDiscoverReplyFlagsFinished, obj->context ); + 0, obj->context ); } break; @@ -436,7 +458,7 @@ DNS_LOCAL void if( callback ) { callback( DNSServiceDomainEnumerationReplyAddDomainDefault, inEvent->data.addDefaultDomain.domain, - DNSServiceDiscoverReplyFlagsFinished, obj->context ); + 0, obj->context ); } break; @@ -446,7 +468,7 @@ DNS_LOCAL void if( callback ) { callback( DNSServiceDomainEnumerationReplyRemoveDomain, inEvent->data.removeDomain.domain, - DNSServiceDiscoverReplyFlagsFinished, obj->context ); + 0, obj->context ); } break; @@ -547,7 +569,7 @@ DNS_LOCAL void inEvent->data.addService.name, inEvent->data.addService.type, inEvent->data.addService.domain, - DNSServiceDiscoverReplyFlagsFinished, + 0, obj->context ); } break; @@ -564,7 +586,7 @@ DNS_LOCAL void inEvent->data.removeService.name, inEvent->data.removeService.type, inEvent->data.removeService.domain, - DNSServiceDiscoverReplyFlagsFinished, + 0, obj->context ); } break; @@ -667,7 +689,7 @@ DNS_LOCAL void if( callback ) { callback( (struct sockaddr *) &interfaceAddr, (struct sockaddr *) &addr, inEvent->data.resolved.textRecord, - DNSServiceDiscoverReplyFlagsFinished, obj->context ); + 0, obj->context ); } break; diff --git a/mDNSWindows/DNSServices/DNSServiceDiscovery.h b/mDNSWindows/DNSServices/DNSServiceDiscovery.h index 618bbb3..eae6d47 100644 --- a/mDNSWindows/DNSServices/DNSServiceDiscovery.h +++ b/mDNSWindows/DNSServices/DNSServiceDiscovery.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,14 @@ Change History (most recent first): $Log: DNSServiceDiscovery.h,v $ +Revision 1.4 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +: Remove flags with zero value +: Passing in NULL causes a crash. + +Revision 1.3 2004/01/30 02:56:34 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + Revision 1.2 2003/08/20 07:06:34 bradley Update to APSL 2.0. Updated change history to match other mDNSResponder files. @@ -177,7 +187,6 @@ typedef enum typedef enum { - DNSServiceDiscoverReplyFlagsFinished, DNSServiceDiscoverReplyFlagsMoreComing, } DNSServiceDiscoveryReplyFlags; diff --git a/mDNSWindows/DNSServices/DNSServices.c b/mDNSWindows/DNSServices/DNSServices.c index ca84436..79a3258 100755 --- a/mDNSWindows/DNSServices/DNSServices.c +++ b/mDNSWindows/DNSServices/DNSServices.c @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,41 @@ Change History (most recent first): $Log: DNSServices.c,v $ +Revision 1.26 2004/06/05 00:04:27 cheshire +: wide-area domains should be returned in reg. domain enumeration + +Revision 1.25 2004/04/08 09:31:17 bradley +Renamed local variable to avoid hiding a system global in some libraries. + +Revision 1.24 2004/01/30 02:56:34 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.23 2004/01/24 23:57:29 cheshire +Change to use mDNSOpaque16fromIntVal() instead of shifting and masking + +Revision 1.22 2003/12/17 21:12:15 bradley +: Use the default .local domain when registering with an empty domain. + +Revision 1.21 2003/11/20 22:29:56 cheshire +Don't need to use MAX_ESCAPED_DOMAIN_LABEL for the name part -- that's not escaped + +Revision 1.20 2003/11/14 21:27:09 cheshire +: Security: Crashing bug in mDNSResponder +Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers. + +Revision 1.19 2003/11/14 20:59:10 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. + +Revision 1.18 2003/11/14 19:18:34 cheshire +Move AssignDomainName macro to mDNSClientAPI.h to that client layers can use it too + +Revision 1.17 2003/10/31 12:16:03 bradley +Added support for providing the resolved host name to the callback. + +Revision 1.16 2003/10/16 09:16:39 bradley +Unified address copying to fix a problem with IPv6 resolves not being passed up as IPv6. + 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 @@ -102,7 +139,6 @@ DNS Services for Windows #endif #include "mDNSClientAPI.h" -#include "mDNSPlatformFunctions.h" #include "DNSServices.h" @@ -289,8 +325,6 @@ struct DNSHostRegistration #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 @@ -367,7 +401,7 @@ mDNSlocal void DNSHostRegistrationPrivateCallBack( mDNS * const inMDNS, AuthReco mDNSlocal DNSStatus DNSMemAlloc( size_t inSize, void *outMem ); mDNSlocal void DNSMemFree( void *inMem ); -mDNSlocal void MDNSAddrToDNSAddress( const mDNSAddr *inAddr, DNSNetworkAddress *outAddr ); +mDNSlocal void MDNSAddrToDNSAddress( const mDNSAddr *inAddr, mDNSIPPort inPort, DNSNetworkAddress *outAddr ); // Platform Accessors @@ -736,11 +770,11 @@ DNSStatus DNSBrowserStartDomainSearch( DNSBrowserRef inRef, DNSBrowserFlags inFl // Start the browse operations. - err = mDNS_GetDomains( gMDNSPtr, &inRef->domainQuestion, type, mDNSInterface_Any, DNSBrowserPrivateCallBack, inRef ); + err = mDNS_GetDomains( gMDNSPtr, &inRef->domainQuestion, type, NULL, mDNSInterface_Any, DNSBrowserPrivateCallBack, inRef ); require_noerr( err, exit ); isDomainBrowsing = mDNStrue; - err = mDNS_GetDomains( gMDNSPtr, &inRef->defaultDomainQuestion, defaultType, mDNSInterface_Any, DNSBrowserPrivateCallBack, inRef ); + err = mDNS_GetDomains( gMDNSPtr, &inRef->defaultDomainQuestion, defaultType, NULL, mDNSInterface_Any, DNSBrowserPrivateCallBack, inRef ); require_noerr( err, exit ); inRef->domainSearchFlags = inFlags; @@ -815,7 +849,7 @@ DNSStatus require_action( !inRef->isServiceBrowsing, exit, err = kDNSBadStateErr ); require_action( inType, exit, err = kDNSBadParamErr ); - // Default to the local domain when null is passed in. + // Default to the local domain when a NULL, empty, or "." domain is passed in. if( !inDomain || ( inDomain[ 0 ] == '\0' ) || ( inDomain[ 0 ] == '.' ) ) { @@ -893,9 +927,9 @@ mDNSlocal void domainlabel name; domainname type; domainname domain; - char nameString[ 256 ]; - char typeString[ 256 ]; - char domainString[ 256 ]; + char nameString [ MAX_DOMAIN_LABEL + 1 ]; // Name part is not escaped + char typeString [ MAX_ESCAPED_DOMAIN_NAME ]; + char domainString[ MAX_ESCAPED_DOMAIN_NAME ]; DNSBrowserEvent event; mStatus err; @@ -953,7 +987,7 @@ mDNSlocal void if( err == mStatus_NoError ) { serviceDataPtr->interfaceName = info.name; - MDNSAddrToDNSAddress( &info.ip, &serviceDataPtr->interfaceIP ); + MDNSAddrToDNSAddress( &info.ip, zeroIPPort, &serviceDataPtr->interfaceIP ); } else { @@ -975,7 +1009,6 @@ mDNSlocal void if( ( browserFlags & kDNSBrowserFlagAutoResolve ) && inAddRecord ) { - DNSStatus err; DNSResolverFlags flags; flags = kDNSResolverFlagOnlyIfUnique | kDNSResolverFlagAutoReleaseByName; @@ -1032,7 +1065,7 @@ mDNSlocal void if( err == mStatus_NoError ) { domainDataPtr->interfaceName = info.name; - MDNSAddrToDNSAddress( &info.ip, &domainDataPtr->interfaceIP ); + MDNSAddrToDNSAddress( &info.ip, zeroIPPort, &domainDataPtr->interfaceIP ); } else { @@ -1079,15 +1112,6 @@ mDNSlocal void 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. @@ -1376,6 +1400,7 @@ mDNSlocal void DNSResolverPrivateCallBack( mDNS * const inMDNS, ServiceInfoQuery char * txtString; mStatus err; mDNSBool release; + char hostName[ MAX_ESCAPED_DOMAIN_NAME ]; txtString = NULL; @@ -1407,7 +1432,7 @@ mDNSlocal void DNSResolverPrivateCallBack( mDNS * const inMDNS, ServiceInfoQuery if( err == mStatus_NoError ) { event.data.resolved.interfaceName = info.name; - MDNSAddrToDNSAddress( &info.ip, &event.data.resolved.interfaceIP ); + MDNSAddrToDNSAddress( &info.ip, zeroIPPort, &event.data.resolved.interfaceIP ); } else { @@ -1415,13 +1440,13 @@ mDNSlocal void DNSResolverPrivateCallBack( mDNS * const inMDNS, ServiceInfoQuery } } 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; + MDNSAddrToDNSAddress( &inQuery->info->ip, inQuery->info->port, &event.data.resolved.address ); 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; + ConvertDomainNameToCString( &inQuery->qAv4.qname, hostName ); + event.data.resolved.hostName = hostName; release = (mDNSBool)( ( objectPtr->flags & kDNSResolverFlagOneShot ) != 0 ); objectPtr->callback( objectPtr->callbackContext, objectPtr, kDNSNoErr, &event ); @@ -1556,7 +1581,6 @@ DNSStatus domainlabel name; domainname type; domainname domain; - mDNSIPPort port; mDNSu8 textRecord[ 256 ]; const mDNSu8 * textRecordPtr; domainname * host; @@ -1576,9 +1600,9 @@ DNSStatus require_action( !inInterfaceName || ( strlen( inInterfaceName ) < sizeof( objectPtr->interfaceName ) ), exit, err = kDNSBadParamErr ); - // Default to the local domain when null is passed in. + // Default to the local domain when a NULL, empty, or "." domain is passed in. - if( !inDomain ) + if( !inDomain || ( inDomain[ 0 ] == '\0' ) || ( inDomain[ 0 ] == '.' ) ) { inDomain = kDNSLocalDomain; } @@ -1650,8 +1674,6 @@ DNSStatus } 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). @@ -1665,7 +1687,7 @@ DNSStatus // Register the service with mDNS. - err = mDNS_RegisterService( gMDNSPtr, &objectPtr->set, &name, &type, &domain, host, port, textRecordPtr, + err = mDNS_RegisterService( gMDNSPtr, &objectPtr->set, &name, &type, &domain, host, mDNSOpaque16fromIntVal(inPort), textRecordPtr, (mDNSu16) inTextRecordSize, NULL, 0, interfaceID, DNSRegistrationPrivateCallBack, objectPtr ); require_noerr( err, exit ); @@ -1720,9 +1742,9 @@ DNSStatus require_action( !inInterfaceName || ( strlen( inInterfaceName ) < sizeof( objectPtr->interfaceName ) ), exit, err = kDNSBadParamErr ); - // Default to the local domain when null is passed in. + // Default to the local domain when a NULL, empty, or "." domain is passed in. - if( !inDomain ) + if( !inDomain || ( inDomain[ 0 ] == '\0' ) || ( inDomain[ 0 ] == '.' ) ) { inDomain = kDNSLocalDomain; } @@ -1947,14 +1969,14 @@ mDNSlocal void DNSRegistrationPrivateCallBack( mDNS * const inMDNS, ServiceRecor case mStatus_NameConflict: { DNSStatus err; - mDNSBool remove; + mDNSBool removeIt; 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; + removeIt = mDNStrue; if( object->flags & kDNSRegistrationFlagAutoRenameOnConflict ) { err = mDNS_RenameAndReregisterService( inMDNS, inSet, mDNSNULL ); @@ -1962,10 +1984,10 @@ mDNSlocal void DNSRegistrationPrivateCallBack( mDNS * const inMDNS, ServiceRecor if( err == mStatus_NoError ) { debugf( DEBUG_NAME "registration callback: auto-renamed to \"%##s\"", inSet->RR_SRV.resrec.name.c ); - remove = mDNSfalse; + removeIt = mDNSfalse; } } - if( remove ) + if( removeIt ) { object = DNSRegistrationRemoveObject( object ); require( object, exit ); @@ -2324,9 +2346,9 @@ DNSStatus require_action( !inInterfaceName || ( strlen( inInterfaceName ) < sizeof( object->interfaceName ) ), exit, err = kDNSBadParamErr ); - // Default to the local domain when null is passed in. + // Default to the local domain when a NULL, empty, or "." domain is passed in. - if( !inDomain ) + if( !inDomain || ( inDomain[ 0 ] == '\0' ) || ( inDomain[ 0 ] == '.' ) ) { inDomain = kDNSLocalDomain; } @@ -2945,7 +2967,7 @@ DNSStatus DNSTextRecordEscape( const void *inTextRecord, size_t inTextSize, char } *dst++ = '\001'; // \001 record separator. May be overwritten later if this is the last record. } - check( ( dst - dstStorage ) <= inTextSize ); + check( (size_t)( dst - dstStorage ) <= inTextSize ); if( src != end ) { // Malformed TXT record. Assume an old-style TXT record and use the TXT record as a whole. @@ -3130,13 +3152,14 @@ exit: // MDNSAddrToDNSAddress //=========================================================================================================================== -mDNSlocal void MDNSAddrToDNSAddress( const mDNSAddr *inAddr, DNSNetworkAddress *outAddr ) +mDNSlocal void MDNSAddrToDNSAddress( const mDNSAddr *inAddr, mDNSIPPort inPort, DNSNetworkAddress *outAddr ) { switch( inAddr->type ) { case mDNSAddrType_IPv4: outAddr->addressType = kDNSNetworkAddressTypeIPv4; outAddr->u.ipv4.addr.v32 = inAddr->ip.v4.NotAnInteger; + outAddr->u.ipv4.port.v16 = inPort.NotAnInteger; break; case mDNSAddrType_IPv6: @@ -3145,6 +3168,7 @@ mDNSlocal void MDNSAddrToDNSAddress( const mDNSAddr *inAddr, DNSNetworkAddress * 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 ]; + outAddr->u.ipv6.port.v16 = inPort.NotAnInteger; break; default: diff --git a/mDNSWindows/DNSServices/DNSServices.h b/mDNSWindows/DNSServices/DNSServices.h index fbbb2bb..bbf48b1 100755 --- a/mDNSWindows/DNSServices/DNSServices.h +++ b/mDNSWindows/DNSServices/DNSServices.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,6 +25,12 @@ Change History (most recent first): $Log: DNSServices.h,v $ +Revision 1.10 2004/01/30 02:56:34 bradley +Updated to support full Unicode display. Added support for all services on www.dns-sd.org. + +Revision 1.9 2003/10/31 12:16:03 bradley +Added support for providing the resolved host name to the callback. + 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 @@ -648,6 +656,9 @@ enum @field textRecordRawSize Number of bytes in raw TXT record. May be needed if a custom TXT record format is used. + + @field hostName + Host name of the resolved service. */ typedef struct DNSResolverEventResolveData DNSResolverEventResolveData; @@ -664,6 +675,7 @@ struct DNSResolverEventResolveData DNSResolverFlags flags; const void * textRecordRaw; DNSCount textRecordRawSize; + const char * hostName; }; //--------------------------------------------------------------------------------------------------------------------------- @@ -1310,7 +1322,7 @@ DNSStatus //--------------------------------------------------------------------------------------------------------------------------- /*! @function DNSNoSuchServiceRegistrationCreate - @abstract Creates a registration object and publish the registration to assert non-existence of a particular service. + @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. diff --git a/mDNSWindows/DebugServices.c b/mDNSWindows/DebugServices.c new file mode 100644 index 0000000..7742b59 --- /dev/null +++ b/mDNSWindows/DebugServices.c @@ -0,0 +1,3101 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DebugServices.c,v $ +Revision 1.4 2004/04/15 08:59:08 bradley +Removed deprecated debug and log levels and replaced them with modern equivalents. + +Revision 1.3 2004/04/08 09:29:55 bradley +Manually do host->network byte order conversion to avoid needing libraries for htons/htonl. Changed +hex dumps to better separate hex and ASCII. Added support for %.8a syntax in DebugSNPrintF for Fibre +Channel addresses (00:11:22:33:44:55:66:77). Fixed a few places where HeaderDoc was incorrect. + +Revision 1.2 2004/03/07 05:59:34 bradley +Sync'd with internal version: Added expect macros, error codes, and CoreServices exclusion. + +Revision 1.1 2004/01/30 02:27:30 bradley +Debugging support for various platforms. + + + To Do: + + - Use StackWalk on Windows to optionally print stack frames. +*/ + +#if 0 +#pragma mark == Includes == +#endif + +//=========================================================================================================================== +// Includes +//=========================================================================================================================== + +#if( !KERNEL ) + #include + #include + #include +#endif + +#include "CommonServices.h" + +#include "DebugServices.h" + +#if( DEBUG ) + +#if( TARGET_OS_VXWORKS ) + #include "intLib.h" +#endif + +#if( TARGET_OS_WIN32 ) + #include + + #if( !TARGET_OS_WINDOWS_CE ) + #include + #include + #endif +#endif + +#if( DEBUG_IDEBUG_ENABLED && TARGET_API_MAC_OSX_KERNEL ) + #include +#endif + +// If MDNS_DEBUGMSGS is defined (even if defined 0), it is aware of mDNS and it is probably safe to include mDNSClientAPI.h. + +#if( defined( MDNS_DEBUGMSGS ) ) + #include "mDNSClientAPI.h" +#endif + +#if 0 +#pragma mark == Macros == +#endif + +//=========================================================================================================================== +// Macros +//=========================================================================================================================== + +#define DebugIsPrint( C ) ( ( ( C ) >= 0x20 ) && ( ( C ) <= 0x7E ) ) + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize ); + +// fprintf + +#if( DEBUG_FPRINTF_ENABLED ) + static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename ); + static void DebugFPrintFPrint( char *inData, size_t inSize ); +#endif + +// iDebug (Mac OS X user and kernel) + +#if( DEBUG_IDEBUG_ENABLED ) + static OSStatus DebugiDebugInit( void ); + static void DebugiDebugPrint( char *inData, size_t inSize ); +#endif + +// kprintf (Mac OS X Kernel) + +#if( DEBUG_KPRINTF_ENABLED ) + static void DebugKPrintFPrint( char *inData, size_t inSize ); +#endif + +// Mac OS X IOLog (Mac OS X Kernel) + +#if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) + static void DebugMacOSXIOLogPrint( char *inData, size_t inSize ); +#endif + +// Mac OS X Log + +#if( TARGET_OS_MAC ) + static OSStatus DebugMacOSXLogInit( void ); + static void DebugMacOSXLogPrint( char *inData, size_t inSize ); +#endif + +// Windows Debugger + +#if( TARGET_OS_WIN32 ) + static void DebugWindowsDebuggerPrint( char *inData, size_t inSize ); +#endif + +// Windows Event Log + +#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule ); + static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize ); +#endif + +// DebugLib support + +#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) + static pascal void + DebugAssertOutputHandler( + OSType inComponentSignature, + UInt32 inOptions, + const char * inAssertionString, + const char * inExceptionString, + const char * inErrorString, + const char * inFileName, + long inLineNumber, + void * inValue, + ConstStr255Param inOutputMsg ); +#endif + +// Utilities + +static char * DebugNumVersionToString( uint32_t inVersion, char *inString ); + +#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + static void DebugWinEnableConsole( void ); +#endif + +#if( TARGET_OS_WIN32 ) + static TCHAR * + DebugWinCharToTCharString( + const char * inCharString, + size_t inCharCount, + TCHAR * outTCharString, + size_t inTCharCountMax, + size_t * outTCharCount ); +#endif + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Private Globals +//=========================================================================================================================== + +#if( TARGET_OS_VXWORKS ) + // TCP States for inetstatShow. + + extern char ** pTcpstates; // defined in tcpLib.c + + const char * kDebugTCPStates[] = + { + "(0) TCPS_CLOSED", + "(1) TCPS_LISTEN", + "(2) TCPS_SYN_SENT", + "(3) TCPS_SYN_RECEIVED", + "(4) TCPS_ESTABLISHED", + "(5) TCPS_CLOSE_WAIT", + "(6) TCPS_FIN_WAIT_1", + "(7) TCPS_CLOSING", + "(8) TCPS_LAST_ACK", + "(9) TCPS_FIN_WAIT_2", + "(10) TCPS_TIME_WAIT", + }; +#endif + +// General + +static bool gDebugInitialized = false; +static DebugOutputType gDebugOutputType = kDebugOutputTypeNone; +static DebugLevel gDebugPrintLevelMin = kDebugLevelInfo; +static DebugLevel gDebugPrintLevelMax = kDebugLevelMax; +static DebugLevel gDebugBreakLevel = kDebugLevelAssert; +#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) + static DebugAssertOutputHandlerUPP gDebugAssertOutputHandlerUPP = NULL; +#endif + +// Custom + +static DebugOutputFunctionPtr gDebugCustomOutputFunction = NULL; +static void * gDebugCustomOutputContext = NULL; + +// fprintf + +#if( DEBUG_FPRINTF_ENABLED ) + static FILE * gDebugFPrintFFile = NULL; +#endif + +// MacOSXLog + +#if( TARGET_OS_MAC ) + typedef int ( *DebugMacOSXLogFunctionPtr )( const char *inFormat, ... ); + + static DebugMacOSXLogFunctionPtr gDebugMacOSXLogFunction = NULL; +#endif + +// WindowsEventLog + + +#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + static HANDLE gDebugWindowsEventLogEventSource = NULL; +#endif + +#if 0 +#pragma mark - +#pragma mark == General == +#endif + +//=========================================================================================================================== +// DebugInitialize +//=========================================================================================================================== + +DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... ) +{ + OSStatus err; + DebugOutputType type; + va_list args; + + va_start( args, inType ); + +#if( TARGET_OS_VXWORKS ) + // Set up the TCP state strings if they are not already set up by VxWorks (normally not set up for some reason). + + if( !pTcpstates ) + { + pTcpstates = (char **) kDebugTCPStates; + } +#endif + + // Set up DebugLib stuff (if building with Debugging.h). + +#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) + if( !gDebugAssertOutputHandlerUPP ) + { + gDebugAssertOutputHandlerUPP = NewDebugAssertOutputHandlerUPP( DebugAssertOutputHandler ); + check( gDebugAssertOutputHandlerUPP ); + if( gDebugAssertOutputHandlerUPP ) + { + InstallDebugAssertOutputHandler( gDebugAssertOutputHandlerUPP ); + } + } +#endif + + // Pre-process meta-output kind to pick an appropriate output kind for the platform. + + type = inType; + if( type == kDebugOutputTypeMetaConsole ) + { + #if( TARGET_OS_MAC ) + type = kDebugOutputTypeMacOSXLog; + #elif( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + #if( DEBUG_FPRINTF_ENABLED ) + type = kDebugOutputTypeFPrintF; + #else + type = kDebugOutputTypeWindowsDebugger; + #endif + #elif( TARGET_API_MAC_OSX_KERNEL ) + #if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) + type = kDebugOutputTypeMacOSXIOLog; + #elif( DEBUG_IDEBUG_ENABLED ) + type = kDebugOutputTypeiDebug; + #elif( DEBUG_KPRINTF_ENABLED ) + type = kDebugOutputTypeKPrintF; + #endif + #elif( TARGET_OS_VXWORKS ) + #if( DEBUG_FPRINTF_ENABLED ) + type = kDebugOutputTypeFPrintF; + #else + #error target is VxWorks, but fprintf output is disabled + #endif + #else + #if( DEBUG_FPRINTF_ENABLED ) + type = kDebugOutputTypeFPrintF; + #endif + #endif + } + + // Process output kind. + + gDebugOutputType = type; + switch( type ) + { + case kDebugOutputTypeNone: + err = kNoErr; + break; + + case kDebugOutputTypeCustom: + gDebugCustomOutputFunction = va_arg( args, DebugOutputFunctionPtr ); + gDebugCustomOutputContext = va_arg( args, void * ); + err = kNoErr; + break; + +#if( DEBUG_FPRINTF_ENABLED ) + case kDebugOutputTypeFPrintF: + if( inType == kDebugOutputTypeMetaConsole ) + { + err = DebugFPrintFInit( kDebugOutputTypeFlagsStdErr, NULL ); + } + else + { + DebugOutputTypeFlags flags; + const char * filename; + + flags = (DebugOutputTypeFlags) va_arg( args, unsigned int ); + if( ( flags & kDebugOutputTypeFlagsTypeMask ) == kDebugOutputTypeFlagsFile ) + { + filename = va_arg( args, const char * ); + } + else + { + filename = NULL; + } + err = DebugFPrintFInit( flags, filename ); + } + break; +#endif + +#if( DEBUG_IDEBUG_ENABLED ) + case kDebugOutputTypeiDebug: + err = DebugiDebugInit(); + break; +#endif + +#if( DEBUG_KPRINTF_ENABLED ) + case kDebugOutputTypeKPrintF: + err = kNoErr; + break; +#endif + +#if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) + case kDebugOutputTypeMacOSXIOLog: + err = kNoErr; + break; +#endif + +#if( TARGET_OS_MAC ) + case kDebugOutputTypeMacOSXLog: + err = DebugMacOSXLogInit(); + break; +#endif + +#if( TARGET_OS_WIN32 ) + case kDebugOutputTypeWindowsDebugger: + err = kNoErr; + break; +#endif + +#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + case kDebugOutputTypeWindowsEventLog: + { + const char * name; + HMODULE module; + + name = va_arg( args, const char * ); + module = va_arg( args, HMODULE ); + err = DebugWindowsEventLogInit( name, module ); + } + break; +#endif + + default: + err = kParamErr; + goto exit; + } + gDebugInitialized = true; + +exit: + va_end( args ); + return( err ); +} + +//=========================================================================================================================== +// DebugFinalize +//=========================================================================================================================== + +DEBUG_EXPORT void DebugFinalize( void ) +{ +#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) + check( gDebugAssertOutputHandlerUPP ); + if( gDebugAssertOutputHandlerUPP ) + { + InstallDebugAssertOutputHandler( NULL ); + DisposeDebugAssertOutputHandlerUPP( gDebugAssertOutputHandlerUPP ); + gDebugAssertOutputHandlerUPP = NULL; + } +#endif +} + +//=========================================================================================================================== +// DebugGetProperty +//=========================================================================================================================== + +DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... ) +{ + OSStatus err; + va_list args; + DebugLevel * level; + + va_start( args, inTag ); + switch( inTag ) + { + case kDebugPropertyTagPrintLevelMin: + level = va_arg( args, DebugLevel * ); + *level = gDebugPrintLevelMin; + err = kNoErr; + break; + + case kDebugPropertyTagPrintLevelMax: + level = va_arg( args, DebugLevel * ); + *level = gDebugPrintLevelMax; + err = kNoErr; + break; + + case kDebugPropertyTagBreakLevel: + level = va_arg( args, DebugLevel * ); + *level = gDebugBreakLevel; + err = kNoErr; + break; + + default: + err = kUnsupportedErr; + break; + } + va_end( args ); + return( err ); +} + +//=========================================================================================================================== +// DebugSetProperty +//=========================================================================================================================== + +DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... ) +{ + OSStatus err; + va_list args; + DebugLevel level; + + va_start( args, inTag ); + switch( inTag ) + { + case kDebugPropertyTagPrintLevelMin: + level = va_arg( args, DebugLevel ); + gDebugPrintLevelMin = level; + err = kNoErr; + break; + + case kDebugPropertyTagPrintLevelMax: + level = va_arg( args, DebugLevel ); + gDebugPrintLevelMax = level; + err = kNoErr; + break; + + case kDebugPropertyTagBreakLevel: + level = va_arg( args, DebugLevel ); + gDebugBreakLevel = level; + err = kNoErr; + break; + + default: + err = kUnsupportedErr; + break; + } + va_end( args ); + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Output == +#endif + +//=========================================================================================================================== +// DebugPrintF +//=========================================================================================================================== + +DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... ) +{ + va_list args; + size_t n; + + // Skip if the level is not in the enabled range.. + + if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) ) + { + n = 0; + goto exit; + } + + va_start( args, inFormat ); + n = DebugPrintFVAList( inLevel, inFormat, args ); + va_end( args ); + +exit: + return( n ); +} + +//=========================================================================================================================== +// DebugPrintFVAList +//=========================================================================================================================== + +DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs ) +{ + size_t n; + char buffer[ 512 ]; + + // Skip if the level is not in the enabled range.. + + if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) ) + { + n = 0; + goto exit; + } + + n = DebugSNPrintFVAList( buffer, sizeof( buffer ), inFormat, inArgs ); + DebugPrint( inLevel, buffer, (size_t) n ); + +exit: + return( n ); +} + +//=========================================================================================================================== +// DebugPrint +//=========================================================================================================================== + +static OSStatus DebugPrint( DebugLevel inLevel, char *inData, size_t inSize ) +{ + OSStatus err; + + // Skip if the level is not in the enabled range.. + + if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) ) + { + err = kRangeErr; + goto exit; + } + + // Printing is not safe at interrupt time so check for this and warn with an interrupt safe mechanism (if available). + + if( DebugTaskLevel() & kDebugInterruptLevelMask ) + { + #if( TARGET_OS_VXWORKS ) + logMsg( "\ncannot print at interrupt time\n\n", 1, 2, 3, 4, 5, 6 ); + #endif + + err = kExecutionStateErr; + goto exit; + } + + // Initialize the debugging library if it hasn't already been initialized (allows for zero-config usage). + + if( !gDebugInitialized ) + { + debug_initialize( kDebugOutputTypeMetaConsole ); + } + + // Print based on the current output type. + + switch( gDebugOutputType ) + { + case kDebugOutputTypeNone: + break; + + case kDebugOutputTypeCustom: + if( gDebugCustomOutputFunction ) + { + gDebugCustomOutputFunction( inData, inSize, gDebugCustomOutputContext ); + } + break; + +#if( DEBUG_FPRINTF_ENABLED ) + case kDebugOutputTypeFPrintF: + DebugFPrintFPrint( inData, inSize ); + break; +#endif + +#if( DEBUG_IDEBUG_ENABLED ) + case kDebugOutputTypeiDebug: + DebugiDebugPrint( inData, inSize ); + break; +#endif + +#if( DEBUG_KPRINTF_ENABLED ) + case kDebugOutputTypeKPrintF: + DebugKPrintFPrint( inData, inSize ); + break; +#endif + +#if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) + case kDebugOutputTypeMacOSXIOLog: + DebugMacOSXIOLogPrint( inData, inSize ); + break; +#endif + +#if( TARGET_OS_MAC ) + case kDebugOutputTypeMacOSXLog: + DebugMacOSXLogPrint( inData, inSize ); + break; +#endif + +#if( TARGET_OS_WIN32 ) + case kDebugOutputTypeWindowsDebugger: + DebugWindowsDebuggerPrint( inData, inSize ); + break; +#endif + +#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + case kDebugOutputTypeWindowsEventLog: + DebugWindowsEventLogPrint( inLevel, inData, inSize ); + break; +#endif + + default: + break; + } + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// DebugPrintAssert +// +// Warning: This routine relies on several of the strings being string constants that will exist forever because the +// underlying logMsg API that does the printing is asynchronous so it cannot use temporary/stack-based +// pointer variables (e.g. local strings). The debug macros that invoke this function only use constant +// constant strings, but if this function is invoked directly from other places, it must use constant strings. +//=========================================================================================================================== + +DEBUG_EXPORT void + DebugPrintAssert( + int_least32_t inErrorCode, + const char * inAssertString, + const char * inMessage, + const char * inFilename, + int_least32_t inLineNumber, + const char * inFunction ) +{ + // Skip if the level is not in the enabled range.. + + if( ( kDebugLevelAssert < gDebugPrintLevelMin ) || ( kDebugLevelAssert > gDebugPrintLevelMax ) ) + { + return; + } + + if( inErrorCode != 0 ) + { + DebugPrintF( + kDebugLevelAssert, + "\n" + "[ASSERT] error: %ld (%m)\n" + "[ASSERT] where: \"%s\", line %ld, \"%s\"\n" + "\n", + inErrorCode, inErrorCode, + inFilename ? inFilename : "", + inLineNumber, + inFunction ? inFunction : "" ); + } + else + { + DebugPrintF( + kDebugLevelAssert, + "\n" + "[ASSERT] assert: \"%s\" %s\n" + "[ASSERT] where: \"%s\", line %ld, \"%s\"\n" + "\n", + inAssertString ? inAssertString : "", + inMessage ? inMessage : "", + inFilename ? inFilename : "", + inLineNumber, + inFunction ? inFunction : "" ); + } + + // Break into the debugger if enabled. + + #if( TARGET_OS_WIN32 ) + if( gDebugBreakLevel <= kDebugLevelAssert ) + { + if( IsDebuggerPresent() ) + { + DebugBreak(); + } + } + #endif +} + +#if 0 +#pragma mark - +#endif + +#if( DEBUG_FPRINTF_ENABLED ) +//=========================================================================================================================== +// DebugFPrintFInit +//=========================================================================================================================== + +static OSStatus DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename ) +{ + OSStatus err; + DebugOutputTypeFlags typeFlags; + + typeFlags = inFlags & kDebugOutputTypeFlagsTypeMask; + if( typeFlags == kDebugOutputTypeFlagsStdOut ) + { + #if( TARGET_OS_WIN32 ) + DebugWinEnableConsole(); + #endif + + gDebugFPrintFFile = stdout; + } + else if( typeFlags == kDebugOutputTypeFlagsStdErr ) + { + #if( TARGET_OS_WIN32 ) + DebugWinEnableConsole(); + #endif + + gDebugFPrintFFile = stdout; + } + else if( typeFlags == kDebugOutputTypeFlagsFile ) + { + require_action_quiet( inFilename && ( *inFilename != '\0' ), exit, err = kOpenErr ); + + gDebugFPrintFFile = fopen( inFilename, "a" ); + require_action_quiet( gDebugFPrintFFile, exit, err = kOpenErr ); + } + else + { + err = kParamErr; + goto exit; + } + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// DebugFPrintFPrint +//=========================================================================================================================== + +static void DebugFPrintFPrint( char *inData, size_t inSize ) +{ + char * p; + char * q; + + // Convert \r to \n. fprintf will interpret \n and convert to whatever is appropriate for the platform. + + p = inData; + q = p + inSize; + while( p < q ) + { + if( *p == '\r' ) + { + *p = '\n'; + } + ++p; + } + + // Write the data and flush. + + if( gDebugFPrintFFile ) + { + fprintf( gDebugFPrintFFile, "%.*s", (int) inSize, inData ); + fflush( gDebugFPrintFFile ); + } +} +#endif // DEBUG_FPRINTF_ENABLED + +#if( DEBUG_IDEBUG_ENABLED ) +//=========================================================================================================================== +// DebugiDebugInit +//=========================================================================================================================== + +static OSStatus DebugiDebugInit( void ) +{ + OSStatus err; + + #if( TARGET_API_MAC_OSX_KERNEL ) + + extern uint32_t * _giDebugReserved1; + + // Emulate the iDebugSetOutputType macro in iDebugServices.h. + // Note: This is not thread safe, but neither is iDebugServices.h nor iDebugKext. + + if( !_giDebugReserved1 ) + { + _giDebugReserved1 = (uint32_t *) IOMalloc( sizeof( uint32_t ) ); + require_action_quiet( _giDebugReserved1, exit, err = kNoMemoryErr ); + } + *_giDebugReserved1 = 0x00010000U; + err = kNoErr; +exit: + #else + + __private_extern__ void iDebugSetOutputTypeInternal( uint32_t inType ); + + iDebugSetOutputTypeInternal( 0x00010000U ); + err = kNoErr; + + #endif + + return( err ); +} + +//=========================================================================================================================== +// DebugiDebugPrint +//=========================================================================================================================== + +static void DebugiDebugPrint( char *inData, size_t inSize ) +{ + #if( TARGET_API_MAC_OSX_KERNEL ) + + // Locally declared here so we do not need to include iDebugKext.h. + // Note: IOKit uses a global namespace for all code and only a partial link occurs at build time. When the + // KEXT is loaded, the runtime linker will link in this extern'd symbol (assuming iDebug is present). + // _giDebugLogInternal is actually part of IOKit proper so this should link even if iDebug is not present. + + typedef void ( *iDebugLogFunctionPtr )( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... ); + + extern iDebugLogFunctionPtr _giDebugLogInternal; + + if( _giDebugLogInternal ) + { + _giDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData ); + } + + #else + + __private_extern__ void iDebugLogInternal( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... ); + + iDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData ); + + #endif +} +#endif + +#if( DEBUG_KPRINTF_ENABLED ) +//=========================================================================================================================== +// DebugKPrintFPrint +//=========================================================================================================================== + +static void DebugKPrintFPrint( char *inData, size_t inSize ) +{ + extern void kprintf( const char *inFormat, ... ); + + kprintf( "%.*s", (int) inSize, inData ); +} +#endif + +#if( DEBUG_MAC_OS_X_IOLOG_ENABLED ) +//=========================================================================================================================== +// DebugMacOSXIOLogPrint +//=========================================================================================================================== + +static void DebugMacOSXIOLogPrint( char *inData, size_t inSize ) +{ + extern void IOLog( const char *inFormat, ... ); + + IOLog( "%.*s", (int) inSize, inData ); +} +#endif + +#if( TARGET_OS_MAC ) +//=========================================================================================================================== +// DebugMacOSXLogInit +//=========================================================================================================================== + +static OSStatus DebugMacOSXLogInit( void ) +{ + OSStatus err; + CFStringRef path; + CFURLRef url; + CFBundleRef bundle; + CFStringRef functionName; + void * functionPtr; + + bundle = NULL; + + // Create a bundle reference for System.framework. + + path = CFSTR( "/System/Library/Frameworks/System.framework" ); + url = CFURLCreateWithFileSystemPath( NULL, path, kCFURLPOSIXPathStyle, true ); + require_action_quiet( url, exit, err = memFullErr ); + + bundle = CFBundleCreate( NULL, url ); + CFRelease( url ); + require_action_quiet( bundle, exit, err = memFullErr ); + + // Get a ptr to the system's "printf" function from System.framework. + + functionName = CFSTR( "printf" ); + functionPtr = CFBundleGetFunctionPointerForName( bundle, functionName ); + require_action_quiet( functionPtr, exit, err = memFullErr ); + + // Success! Note: The bundle cannot be released because it would invalidate the function ptr. + + gDebugMacOSXLogFunction = (DebugMacOSXLogFunctionPtr) functionPtr; + bundle = NULL; + err = noErr; + +exit: + if( bundle ) + { + CFRelease( bundle ); + } + return( err ); +} + +//=========================================================================================================================== +// DebugMacOSXLogPrint +//=========================================================================================================================== + +static void DebugMacOSXLogPrint( char *inData, size_t inSize ) +{ + if( gDebugMacOSXLogFunction ) + { + gDebugMacOSXLogFunction( "%.*s", (int) inSize, inData ); + } +} +#endif + +#if( TARGET_OS_WIN32 ) +//=========================================================================================================================== +// DebugWindowsDebuggerPrint +//=========================================================================================================================== + +void DebugWindowsDebuggerPrint( char *inData, size_t inSize ) +{ + TCHAR buffer[ 512 ]; + const char * src; + const char * end; + TCHAR * dst; + char c; + + // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are + // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process. + + src = inData; + if( inSize >= sizeof_array( buffer ) ) + { + inSize = sizeof_array( buffer ) - 1; + } + end = src + inSize; + dst = buffer; + while( src < end ) + { + c = *src++; + if( c == '\r' ) + { + c = '\n'; + } + *dst++ = (TCHAR) c; + } + *dst = 0; + + // Print out the string to the debugger. + + OutputDebugString( buffer ); +} +#endif + +#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) +//=========================================================================================================================== +// DebugWindowsEventLogInit +//=========================================================================================================================== + +static OSStatus DebugWindowsEventLogInit( const char *inName, HMODULE inModule ) +{ + OSStatus err; + HKEY key; + TCHAR name[ 128 ]; + const char * src; + TCHAR path[ MAX_PATH ]; + size_t size; + DWORD typesSupported; + DWORD n; + + key = NULL; + + // Use a default name if needed then convert the name to TCHARs so it works on ANSI or Unicode builds. + + if( !inName || ( *inName == '\0' ) ) + { + inName = "DefaultApp"; + } + DebugWinCharToTCharString( inName, kSizeCString, name, sizeof( name ), NULL ); + + // Build the path string using the fixed registry path and app name. + + src = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"; + DebugWinCharToTCharString( src, kSizeCString, path, sizeof_array( path ), &size ); + DebugWinCharToTCharString( inName, kSizeCString, path + size, sizeof_array( path ) - size, NULL ); + + // Add/Open the source name as a sub-key under the Application key in the EventLog registry key. + + err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL ); + require_noerr_quiet( err, exit ); + + // Set the path in the EventMessageFile subkey. Add 1 to the TCHAR count to include the null terminator. + + n = GetModuleFileName( inModule, path, sizeof_array( path ) ); + err = translate_errno( n > 0, (OSStatus) GetLastError(), kParamErr ); + require_noerr_quiet( err, exit ); + n += 1; + n *= sizeof( TCHAR ); + + err = RegSetValueEx( key, TEXT( "EventMessageFile" ), 0, REG_EXPAND_SZ, (const LPBYTE) path, n ); + require_noerr_quiet( err, exit ); + + // Set the supported event types in the TypesSupported subkey. + + typesSupported = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE | + EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE; + err = RegSetValueEx( key, TEXT( "TypesSupported" ), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) ); + require_noerr_quiet( err, exit ); + + // Set up the event source. + + gDebugWindowsEventLogEventSource = RegisterEventSource( NULL, name ); + err = translate_errno( gDebugWindowsEventLogEventSource, (OSStatus) GetLastError(), kParamErr ); + require_noerr_quiet( err, exit ); + +exit: + if( key ) + { + RegCloseKey( key ); + } + return( err ); +} + +//=========================================================================================================================== +// DebugWindowsEventLogPrint +//=========================================================================================================================== + +static void DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize ) +{ + WORD type; + TCHAR buffer[ 512 ]; + const char * src; + const char * end; + TCHAR * dst; + char c; + const TCHAR * array[ 1 ]; + + // Map the debug level to a Windows EventLog type. + + if( inLevel <= kDebugLevelNotice ) + { + type = EVENTLOG_INFORMATION_TYPE; + } + else if( inLevel <= kDebugLevelWarning ) + { + type = EVENTLOG_WARNING_TYPE; + } + else + { + type = EVENTLOG_ERROR_TYPE; + } + + // Copy locally and null terminate the string. This also converts from char to TCHAR in case we are + // building with UNICODE enabled since the input is always char. Also convert \r to \n in the process. + + src = inData; + if( inSize >= sizeof_array( buffer ) ) + { + inSize = sizeof_array( buffer ) - 1; + } + end = src + inSize; + dst = buffer; + while( src < end ) + { + c = *src++; + if( c == '\r' ) + { + c = '\n'; + } + *dst++ = (TCHAR) c; + } + *dst = 0; + + // Add the the string to the event log. + + array[ 0 ] = buffer; + if( gDebugWindowsEventLogEventSource ) + { + ReportEvent( gDebugWindowsEventLogEventSource, type, 0, 0x20000001L, NULL, 1, 0, array, NULL ); + } +} +#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE + +#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) +//=========================================================================================================================== +// DebugAssertOutputHandler +//=========================================================================================================================== + +static pascal void + DebugAssertOutputHandler( + OSType inComponentSignature, + UInt32 inOptions, + const char * inAssertString, + const char * inExceptionString, + const char * inErrorString, + const char * inFileName, + long inLineNumber, + void * inValue, + ConstStr255Param inOutputMsg ) +{ + DEBUG_UNUSED( inComponentSignature ); + DEBUG_UNUSED( inOptions ); + DEBUG_UNUSED( inExceptionString ); + DEBUG_UNUSED( inValue ); + DEBUG_UNUSED( inOutputMsg ); + + DebugPrintAssert( 0, inAssertString, inErrorString, inFileName, (int_least32_t) inLineNumber, "" ); +} +#endif + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +//=========================================================================================================================== +// DebugSNPrintF +// +// Stolen from mDNS.c's mDNS_snprintf/mDNS_vsnprintf with the following changes: +// +// Changed names to avoid name collisions with the mDNS versions. +// Changed types to standard C types since mDNSClientAPI.h may not be available. +// Conditionalized mDNS stuff so it can be used with or with mDNSClientAPI.h. +// Added 64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb). +// Added %@ - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription. +// Added %.8a - FIbre Channel address. Arg=ptr to address. +// Added %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr. +// Added %b - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc. +// Added %C - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode. +// Added %H - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size. +// Added %#H - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size. +// Added %m - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code args are the same as %d, %x, etc. +// Added %S - UTF-16 string. Host order if no BOM. Precision is UTF-16 char count. BOM counts in any precision. Arg=ptr. +// Added %#S - Big Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S. +// Added %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S. +// Added %U - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID. +//=========================================================================================================================== + +DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...) + { + size_t length; + + va_list ptr; + va_start(ptr,fmt); + length = DebugSNPrintFVAList(sbuffer, buflen, fmt, ptr); + va_end(ptr); + + return(length); + } + +//=========================================================================================================================== +// DebugSNPrintFVAList - va_list version of DebugSNPrintF. See DebugSNPrintF for more info. +//=========================================================================================================================== + +DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg) + { + static const struct DebugSNPrintF_format + { + unsigned leftJustify : 1; + unsigned forceSign : 1; + unsigned zeroPad : 1; + unsigned havePrecision : 1; + unsigned hSize : 1; + char lSize; + char altForm; + char sign; // +, - or space + unsigned int fieldWidth; + unsigned int precision; + } DebugSNPrintF_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + size_t nwritten = 0; + int c; + if (buflen == 0) return(0); + buflen--; // Pre-reserve one space in the buffer for the terminating nul + if (buflen == 0) goto exit; + + for (c = *fmt; c != 0; c = *++fmt) + { + if (c != '%') + { + *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + } + else + { + size_t 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) ((size_t)(mDNS_VACB_Lim - s)) + char *s = mDNS_VACB_Lim; + const char *digits = "0123456789ABCDEF"; + struct DebugSNPrintF_format F = DebugSNPrintF_format_default; + + 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 + { + 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 + { + #if TYPE_LONGLONG_NATIVE + unsigned_long_long_compat n; + unsigned_long_long_compat base; + #else + unsigned long n; + unsigned long base; + #endif + case 'h' : F.hSize = 1; c = *++fmt; goto conv; + case 'l' : // fall through + case 'L' : F.lSize++; c = *++fmt; goto conv; + case 'd' : + case 'i' : base = 10; + goto canBeSigned; + case 'u' : base = 10; + goto notSigned; + case 'o' : base = 8; + goto notSigned; + case 'b' : base = 2; + goto notSigned; + case 'p' : n = va_arg(arg, uintptr_t); + F.havePrecision = 1; + F.precision = (sizeof(uintptr_t) == 4) ? 8 : 16; + F.sign = 0; + base = 16; + c = 'x'; + goto number; + case 'x' : digits = "0123456789abcdef"; + case 'X' : base = 16; + goto notSigned; + canBeSigned: + #if TYPE_LONGLONG_NATIVE + if (F.lSize == 1) n = (unsigned_long_long_compat)va_arg(arg, long); + else if (F.lSize == 2) n = (unsigned_long_long_compat)va_arg(arg, long_long_compat); + else n = (unsigned_long_long_compat)va_arg(arg, int); + #else + if (F.lSize == 1) n = (unsigned long)va_arg(arg, long); + else if (F.lSize == 2) goto exit; + else n = (unsigned long)va_arg(arg, int); + #endif + if (F.hSize) n = (short) n; + #if TYPE_LONGLONG_NATIVE + if ((long_long_compat) n < 0) { n = (unsigned_long_long_compat)-(long_long_compat)n; F.sign = '-'; } + #else + if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } + #endif + else if (F.forceSign) F.sign = '+'; + goto number; + + notSigned: if (F.lSize == 1) n = va_arg(arg, unsigned long); + else if (F.lSize == 2) + { + #if TYPE_LONGLONG_NATIVE + n = va_arg(arg, unsigned_long_long_compat); + #else + goto exit; + #endif + } + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + F.sign = 0; + goto number; + + number: if (!F.havePrecision) + { + if (F.zeroPad) + { + F.precision = F.fieldWidth; + if (F.altForm) F.precision -= 2; + 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 /= base, i++) *--s = (char)(digits[n % base]); + for (; i < F.precision; i++) *--s = '0'; + if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } + if (F.sign) { *--s = F.sign; i++; } + break; + + case 'a' : { + unsigned char *a = va_arg(arg, unsigned char *); + char pre[4] = ""; + char post[32] = ""; + if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else + { + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (F.altForm == 1) + { + #if(defined(MDNS_DEBUGMSGS)) + mDNSAddr *ip = (mDNSAddr*)a; + switch (ip->type) + { + case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; + case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; + default: F.precision = 0; break; + } + #else + F.precision = 0; // mDNSClientAPI.h not included so no mDNSAddr support + #endif + } + else if (F.altForm == 2) + { + #ifdef AF_INET + const struct sockaddr *sa; + unsigned char *port; + sa = (const struct sockaddr*)a; + switch (sa->sa_family) + { + case AF_INET: F.precision = 4; a = (unsigned char*)&((const struct sockaddr_in *)a)->sin_addr; + port = (unsigned char*)&((const struct sockaddr_in *)sa)->sin_port; + DebugSNPrintF(post, sizeof(post), ":%d", (port[0] << 8) | port[1]); break; + #ifdef AF_INET6 + case AF_INET6: F.precision = 16; a = (unsigned char*)&((const struct sockaddr_in6 *)a)->sin6_addr; + pre[0] = '['; pre[1] = '\0'; + port = (unsigned char*)&((const struct sockaddr_in6 *)sa)->sin6_port; + DebugSNPrintF(post, sizeof(post), "%%%d]:%d", + (int)((const struct sockaddr_in6 *)sa)->sin6_scope_id, + (port[0] << 8) | port[1]); break; + #endif + default: F.precision = 0; break; + } + #else + F.precision = 0; // socket interfaces not included so no sockaddr support + #endif + } + switch (F.precision) + { + case 4: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d%s", + a[0], a[1], a[2], a[3], post); break; + case 6: i = DebugSNPrintF(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 8: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", + a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); break; + case 16: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), + "%s%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%s", + pre, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], + a[9], a[10], a[11], a[12], a[13], a[14], a[15], post); break; + default: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size " + "(i.e. %.4a=IPv4, %.6a=Ethernet, %.8a=Fibre Channel %.16a=IPv6) >>"); break; + } + } + } + break; + + case 'U' : { + unsigned char *a = va_arg(arg, unsigned char *); + if (!a) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else + { + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + *((uint32_t*) &a[0]), *((uint16_t*) &a[4]), *((uint16_t*) &a[6]), + a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); break; + } + } + break; + + case 'c' : *--s = (char)va_arg(arg, int); i = 1; break; + + case 'C' : if (F.lSize) n = va_arg(arg, unsigned long); + else n = va_arg(arg, unsigned int); + if (F.hSize) n = (unsigned short) n; + c = (int)( n & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); + c = (int)((n >> 8) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); + c = (int)((n >> 16) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); + c = (int)((n >> 24) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^'); + i = 4; + break; + + case 's' : s = va_arg(arg, char *); + if (!s) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + else switch (F.altForm) + { + case 0: i=0; + if (F.havePrecision) // C string + { + while((i < F.precision) && s[i]) i++; + // Make sure we don't truncate in the middle of a UTF-8 character. + // If the last character is part of a multi-byte UTF-8 character, back up to the start of it. + j=0; + while((i > 0) && ((c = s[i-1]) & 0x80)) { j++; i--; if((c & 0xC0) != 0x80) break; } + // If the actual count of UTF-8 characters matches the encoded UTF-8 count, add it back. + if((j > 1) && (j <= 6)) + { + int test = (0xFF << (8-j)) & 0xFF; + int mask = test | (1 << ((8-j)-1)); + if((c & mask) == test) i += j; + } + } + else + while(s[i]) i++; + break; + 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 += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<>", *a); break; } + if (s + *a >= &mDNS_VACB[254]) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<>"); break; } + s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "%#s.", a); + a += 1 + *a; + } + i = (size_t)(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 'S': { // UTF-16 string + unsigned char *a = va_arg(arg, unsigned char *); + uint16_t *u = (uint16_t*)a; + if (!u) { static char emsg[] = "<>"; s = emsg; i = sizeof(emsg)-1; } + if ((!F.havePrecision || F.precision)) + { + if ((a[0] == 0xFE) && (a[1] == 0xFF)) { F.altForm = 1; u += 1; a += 2; F.precision--; } // Big Endian + else if ((a[0] == 0xFF) && (a[1] == 0xFE)) { F.altForm = 2; u += 1; a += 2; F.precision--; } // Little Endian + } + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + switch (F.altForm) + { + case 0: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Host Endian + { c = u[i]; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; } + break; + case 1: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Big Endian + { c = ((a[0] << 8) | a[1]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; } + break; + case 2: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s)) // Little Endian + { c = ((a[1] << 8) | a[0]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; } + break; + } + } + s = mDNS_VACB; // Reset s back to the start of the buffer + break; + + #if TARGET_OS_MAC + case '@': { // Cocoa/CoreFoundation object + CFTypeRef cfObj; + CFStringRef cfStr; + cfObj = (CFTypeRef) va_arg(arg, void *); + cfStr = (CFGetTypeID(cfObj) == CFStringGetTypeID()) ? (CFStringRef)CFRetain(cfObj) : CFCopyDescription(cfObj); + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + if (cfStr) + { + CFRange range; + CFIndex m; + range = CFRangeMake(0, CFStringGetLength(cfStr)); + m = 0; + CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, '^', false, (UInt8*)mDNS_VACB, (CFIndex)sizeof(mDNS_VACB), &m); + CFRelease(cfStr); + i = (size_t) m; + } + else + { + i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "ERROR: " ); + } + } + 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; + #endif + + case 'm' : { // Error Message + long err; + if (F.lSize) err = va_arg(arg, long); + else err = va_arg(arg, int); + if (F.hSize) err = (short)err; + DebugGetErrorString(err, mDNS_VACB, sizeof(mDNS_VACB)); + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + for(i=0;s[i];i++) {} + } + break; + + case 'H' : { // Hex Dump + void *a = va_arg(arg, void *); + size_t size = (size_t)va_arg(arg, int); + size_t max = (size_t)va_arg(arg, int); + DebugFlags flags = + kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine | + kDebugFlags8BitSeparator | kDebugFlagsNo32BitSeparator | + kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount; + if (F.altForm == 0) flags |= kDebugFlagsNoASCII; + size = (max < size) ? max : size; + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + i = DebugHexDump(kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, a, a, size, flags, mDNS_VACB, sizeof(mDNS_VACB)); + } + break; + + case 'v' : { // Version + uint32_t version; + version = va_arg(arg, unsigned int); + DebugNumVersionToString(version, mDNS_VACB); + s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end + for(i=0;s[i];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 = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "<>", c); + + case '%' : *sbuffer++ = (char)c; + if (++nwritten >= buflen) goto exit; + break; + } + + if (i < F.fieldWidth && !F.leftJustify) // Pad on the left + do { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } while (i < --F.fieldWidth); + + if (i > buflen - nwritten) // Make sure we don't truncate in the middle of a UTF-8 character + { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } + for (j=0; j= buflen) goto exit; + + for (; i < F.fieldWidth; i++) // Pad on the right + { + *sbuffer++ = ' '; + if (++nwritten >= buflen) goto exit; + } + } + } + exit: + *sbuffer++ = 0; + return(nwritten); + } + +//=========================================================================================================================== +// DebugGetErrorString +//=========================================================================================================================== + +DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize ) +{ + const char * s; + char * dst; + char * end; +#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + char buffer[ 256 ]; +#endif + + switch( inErrorCode ) + { + #define CaseErrorString( X, STR ) case X: s = STR; break + #define CaseErrorStringify( X ) case X: s = # X; break + #define CaseErrorStringifyHardCode( VALUE, X ) case VALUE: s = # X; break + + // General Errors + + CaseErrorString( 0, "no error" ); + CaseErrorString( 1, "in-progress/waiting" ); + CaseErrorString( -1, "catch-all unknown error" ); + + // ACP Errors + + CaseErrorStringifyHardCode( -2, kACPBadRequestErr ); + CaseErrorStringifyHardCode( -3, kACPNoMemoryErr ); + CaseErrorStringifyHardCode( -4, kACPBadParamErr ); + CaseErrorStringifyHardCode( -5, kACPNotFoundErr ); + CaseErrorStringifyHardCode( -6, kACPBadChecksumErr ); + CaseErrorStringifyHardCode( -7, kACPCommandNotHandledErr ); + CaseErrorStringifyHardCode( -8, kACPNetworkErr ); + CaseErrorStringifyHardCode( -9, kACPDuplicateCommandHandlerErr ); + CaseErrorStringifyHardCode( -10, kACPUnknownPropertyErr ); + CaseErrorStringifyHardCode( -11, kACPImmutablePropertyErr ); + CaseErrorStringifyHardCode( -12, kACPBadPropertyValueErr ); + CaseErrorStringifyHardCode( -13, kACPNoResourcesErr ); + CaseErrorStringifyHardCode( -14, kACPBadOptionErr ); + CaseErrorStringifyHardCode( -15, kACPBadSizeErr ); + CaseErrorStringifyHardCode( -16, kACPBadPasswordErr ); + CaseErrorStringifyHardCode( -17, kACPNotInitializedErr ); + CaseErrorStringifyHardCode( -18, kACPNonReadablePropertyErr ); + CaseErrorStringifyHardCode( -19, kACPBadVersionErr ); + CaseErrorStringifyHardCode( -20, kACPBadSignatureErr ); + CaseErrorStringifyHardCode( -21, kACPBadIndexErr ); + CaseErrorStringifyHardCode( -22, kACPUnsupportedErr ); + CaseErrorStringifyHardCode( -23, kACPInUseErr ); + CaseErrorStringifyHardCode( -24, kACPParamCountErr ); + CaseErrorStringifyHardCode( -25, kACPIDErr ); + CaseErrorStringifyHardCode( -26, kACPFormatErr ); + CaseErrorStringifyHardCode( -27, kACPUnknownUserErr ); + CaseErrorStringifyHardCode( -28, kACPAccessDeniedErr ); + CaseErrorStringifyHardCode( -29, kACPIncorrectFWErr ); + + // Common Services Errors + + CaseErrorStringify( kUnknownErr ); + CaseErrorStringify( kOptionErr ); + CaseErrorStringify( kSelectorErr ); + CaseErrorStringify( kExecutionStateErr ); + CaseErrorStringify( kPathErr ); + CaseErrorStringify( kParamErr ); + CaseErrorStringify( kParamCountErr ); + CaseErrorStringify( kCommandErr ); + CaseErrorStringify( kIDErr ); + CaseErrorStringify( kStateErr ); + CaseErrorStringify( kRangeErr ); + CaseErrorStringify( kRequestErr ); + CaseErrorStringify( kResponseErr ); + CaseErrorStringify( kChecksumErr ); + CaseErrorStringify( kNotHandledErr ); + CaseErrorStringify( kVersionErr ); + CaseErrorStringify( kSignatureErr ); + CaseErrorStringify( kFormatErr ); + CaseErrorStringify( kNotInitializedErr ); + CaseErrorStringify( kAlreadyInitializedErr ); + CaseErrorStringify( kNotInUseErr ); + CaseErrorStringify( kInUseErr ); + CaseErrorStringify( kTimeoutErr ); + CaseErrorStringify( kCanceledErr ); + CaseErrorStringify( kAlreadyCanceledErr ); + CaseErrorStringify( kCannotCancelErr ); + CaseErrorStringify( kDeletedErr ); + CaseErrorStringify( kNotFoundErr ); + CaseErrorStringify( kNoMemoryErr ); + CaseErrorStringify( kNoResourcesErr ); + CaseErrorStringify( kDuplicateErr ); + CaseErrorStringify( kImmutableErr ); + CaseErrorStringify( kUnsupportedDataErr ); + CaseErrorStringify( kIntegrityErr ); + CaseErrorStringify( kIncompatibleErr ); + CaseErrorStringify( kUnsupportedErr ); + CaseErrorStringify( kUnexpectedErr ); + CaseErrorStringify( kValueErr ); + CaseErrorStringify( kNotReadableErr ); + CaseErrorStringify( kNotWritableErr ); + CaseErrorStringify( kBadReferenceErr ); + CaseErrorStringify( kFlagErr ); + CaseErrorStringify( kMalformedErr ); + CaseErrorStringify( kSizeErr ); + CaseErrorStringify( kNameErr ); + CaseErrorStringify( kNotReadyErr ); + CaseErrorStringify( kReadErr ); + CaseErrorStringify( kWriteErr ); + CaseErrorStringify( kMismatchErr ); + CaseErrorStringify( kDateErr ); + CaseErrorStringify( kUnderrunErr ); + CaseErrorStringify( kOverrunErr ); + CaseErrorStringify( kEndingErr ); + CaseErrorStringify( kConnectionErr ); + CaseErrorStringify( kAuthenticationErr ); + CaseErrorStringify( kOpenErr ); + CaseErrorStringify( kTypeErr ); + CaseErrorStringify( kSkipErr ); + CaseErrorStringify( kNoAckErr ); + CaseErrorStringify( kCollisionErr ); + CaseErrorStringify( kBackoffErr ); + CaseErrorStringify( kNoAddressAckErr ); + CaseErrorStringify( kBusyErr ); + CaseErrorStringify( kNoSpaceErr ); + + // mDNS/DNS-SD Errors + + CaseErrorStringifyHardCode( -65537, mStatus_UnknownErr ); + CaseErrorStringifyHardCode( -65538, mStatus_NoSuchNameErr ); + CaseErrorStringifyHardCode( -65539, mStatus_NoMemoryErr ); + CaseErrorStringifyHardCode( -65540, mStatus_BadParamErr ); + CaseErrorStringifyHardCode( -65541, mStatus_BadReferenceErr ); + CaseErrorStringifyHardCode( -65542, mStatus_BadStateErr ); + CaseErrorStringifyHardCode( -65543, mStatus_BadFlagsErr ); + CaseErrorStringifyHardCode( -65544, mStatus_UnsupportedErr ); + CaseErrorStringifyHardCode( -65545, mStatus_NotInitializedErr ); + CaseErrorStringifyHardCode( -65546, mStatus_NoCache ); + CaseErrorStringifyHardCode( -65547, mStatus_AlreadyRegistered ); + CaseErrorStringifyHardCode( -65548, mStatus_NameConflict ); + CaseErrorStringifyHardCode( -65549, mStatus_Invalid ); + CaseErrorStringifyHardCode( -65550, mStatus_GrowCache ); + CaseErrorStringifyHardCode( -65551, mStatus_BadInterfaceErr ); + CaseErrorStringifyHardCode( -65552, mStatus_Incompatible ); + CaseErrorStringifyHardCode( -65791, mStatus_ConfigChanged ); + CaseErrorStringifyHardCode( -65792, mStatus_MemFree ); + + // RSP Errors + + CaseErrorStringifyHardCode( -400000, kRSPUnknownErr ); + CaseErrorStringifyHardCode( -400050, kRSPParamErr ); + CaseErrorStringifyHardCode( -400108, kRSPNoMemoryErr ); + CaseErrorStringifyHardCode( -405246, kRSPRangeErr ); + CaseErrorStringifyHardCode( -409057, kRSPSizeErr ); + CaseErrorStringifyHardCode( -400200, kRSPHardwareErr ); + CaseErrorStringifyHardCode( -401712, kRSPTimeoutErr ); + CaseErrorStringifyHardCode( -402053, kRSPUnsupportedErr ); + CaseErrorStringifyHardCode( -402419, kRSPIDErr ); + CaseErrorStringifyHardCode( -403165, kRSPFlagErr ); + CaseErrorString( -200000, "kRSPControllerStatusBase - 0x50" ); + CaseErrorString( -200080, "kRSPCommandSucceededErr - 0x50" ); + CaseErrorString( -200001, "kRSPCommandFailedErr - 0x01" ); + CaseErrorString( -200051, "kRSPChecksumErr - 0x33" ); + CaseErrorString( -200132, "kRSPCommandTimeoutErr - 0x84" ); + CaseErrorString( -200034, "kRSPPasswordRequiredErr - 0x22 OBSOLETE" ); + CaseErrorString( -200128, "kRSPCanceledErr - 0x02 Async" ); + + // XML Errors + + CaseErrorStringifyHardCode( -100043, kXMLNotFoundErr ); + CaseErrorStringifyHardCode( -100050, kXMLParamErr ); + CaseErrorStringifyHardCode( -100108, kXMLNoMemoryErr ); + CaseErrorStringifyHardCode( -100206, kXMLFormatErr ); + CaseErrorStringifyHardCode( -100586, kXMLNoRootElementErr ); + CaseErrorStringifyHardCode( -101703, kXMLWrongDataTypeErr ); + CaseErrorStringifyHardCode( -101726, kXMLKeyErr ); + CaseErrorStringifyHardCode( -102053, kXMLUnsupportedErr ); + CaseErrorStringifyHardCode( -102063, kXMLMissingElementErr ); + CaseErrorStringifyHardCode( -103026, kXMLParseErr ); + CaseErrorStringifyHardCode( -103159, kXMLBadDataErr ); + CaseErrorStringifyHardCode( -103170, kXMLBadNameErr ); + CaseErrorStringifyHardCode( -105246, kXMLRangeErr ); + CaseErrorStringifyHardCode( -105251, kXMLUnknownElementErr ); + CaseErrorStringifyHardCode( -108739, kXMLMalformedInputErr ); + CaseErrorStringifyHardCode( -109057, kXMLBadSizeErr ); + CaseErrorStringifyHardCode( -101730, kXMLMissingChildElementErr ); + CaseErrorStringifyHardCode( -102107, kXMLMissingParentElementErr ); + CaseErrorStringifyHardCode( -130587, kXMLNonRootElementErr ); + CaseErrorStringifyHardCode( -102015, kXMLDateErr ); + + #if( __MACH__ ) + + // Mach Errors + + CaseErrorStringifyHardCode( 0x00002000, MACH_MSG_IPC_SPACE ); + CaseErrorStringifyHardCode( 0x00001000, MACH_MSG_VM_SPACE ); + CaseErrorStringifyHardCode( 0x00000800, MACH_MSG_IPC_KERNEL ); + CaseErrorStringifyHardCode( 0x00000400, MACH_MSG_VM_KERNEL ); + CaseErrorStringifyHardCode( 0x10000001, MACH_SEND_IN_PROGRESS ); + CaseErrorStringifyHardCode( 0x10000002, MACH_SEND_INVALID_DATA ); + CaseErrorStringifyHardCode( 0x10000003, MACH_SEND_INVALID_DEST ); + CaseErrorStringifyHardCode( 0x10000004, MACH_SEND_TIMED_OUT ); + CaseErrorStringifyHardCode( 0x10000007, MACH_SEND_INTERRUPTED ); + CaseErrorStringifyHardCode( 0x10000008, MACH_SEND_MSG_TOO_SMALL ); + CaseErrorStringifyHardCode( 0x10000009, MACH_SEND_INVALID_REPLY ); + CaseErrorStringifyHardCode( 0x1000000A, MACH_SEND_INVALID_RIGHT ); + CaseErrorStringifyHardCode( 0x1000000B, MACH_SEND_INVALID_NOTIFY ); + CaseErrorStringifyHardCode( 0x1000000C, MACH_SEND_INVALID_MEMORY ); + CaseErrorStringifyHardCode( 0x1000000D, MACH_SEND_NO_BUFFER ); + CaseErrorStringifyHardCode( 0x1000000E, MACH_SEND_TOO_LARGE ); + CaseErrorStringifyHardCode( 0x1000000F, MACH_SEND_INVALID_TYPE ); + CaseErrorStringifyHardCode( 0x10000010, MACH_SEND_INVALID_HEADER ); + CaseErrorStringifyHardCode( 0x10000011, MACH_SEND_INVALID_TRAILER ); + CaseErrorStringifyHardCode( 0x10000015, MACH_SEND_INVALID_RT_OOL_SIZE ); + CaseErrorStringifyHardCode( 0x10004001, MACH_RCV_IN_PROGRESS ); + CaseErrorStringifyHardCode( 0x10004002, MACH_RCV_INVALID_NAME ); + CaseErrorStringifyHardCode( 0x10004003, MACH_RCV_TIMED_OUT ); + CaseErrorStringifyHardCode( 0x10004004, MACH_RCV_TOO_LARGE ); + CaseErrorStringifyHardCode( 0x10004005, MACH_RCV_INTERRUPTED ); + CaseErrorStringifyHardCode( 0x10004006, MACH_RCV_PORT_CHANGED ); + CaseErrorStringifyHardCode( 0x10004007, MACH_RCV_INVALID_NOTIFY ); + CaseErrorStringifyHardCode( 0x10004008, MACH_RCV_INVALID_DATA ); + CaseErrorStringifyHardCode( 0x10004009, MACH_RCV_PORT_DIED ); + CaseErrorStringifyHardCode( 0x1000400A, MACH_RCV_IN_SET ); + CaseErrorStringifyHardCode( 0x1000400B, MACH_RCV_HEADER_ERROR ); + CaseErrorStringifyHardCode( 0x1000400C, MACH_RCV_BODY_ERROR ); + CaseErrorStringifyHardCode( 0x1000400D, MACH_RCV_INVALID_TYPE ); + CaseErrorStringifyHardCode( 0x1000400E, MACH_RCV_SCATTER_SMALL ); + CaseErrorStringifyHardCode( 0x1000400F, MACH_RCV_INVALID_TRAILER ); + CaseErrorStringifyHardCode( 0x10004011, MACH_RCV_IN_PROGRESS_TIMED ); + + // Mach OSReturn Errors + + CaseErrorStringifyHardCode( 0xDC000001, kOSReturnError ); + CaseErrorStringifyHardCode( 0xDC004001, kOSMetaClassInternal ); + CaseErrorStringifyHardCode( 0xDC004002, kOSMetaClassHasInstances ); + CaseErrorStringifyHardCode( 0xDC004003, kOSMetaClassNoInit ); + CaseErrorStringifyHardCode( 0xDC004004, kOSMetaClassNoTempData ); + CaseErrorStringifyHardCode( 0xDC004005, kOSMetaClassNoDicts ); + CaseErrorStringifyHardCode( 0xDC004006, kOSMetaClassNoKModSet ); + CaseErrorStringifyHardCode( 0xDC004007, kOSMetaClassNoInsKModSet ); + CaseErrorStringifyHardCode( 0xDC004008, kOSMetaClassNoSuper ); + CaseErrorStringifyHardCode( 0xDC004009, kOSMetaClassInstNoSuper ); + CaseErrorStringifyHardCode( 0xDC00400A, kOSMetaClassDuplicateClass ); + + // IOKit Errors + + CaseErrorStringifyHardCode( 0xE00002BC, kIOReturnError ); + CaseErrorStringifyHardCode( 0xE00002BD, kIOReturnNoMemory ); + CaseErrorStringifyHardCode( 0xE00002BE, kIOReturnNoResources ); + CaseErrorStringifyHardCode( 0xE00002BF, kIOReturnIPCError ); + CaseErrorStringifyHardCode( 0xE00002C0, kIOReturnNoDevice ); + CaseErrorStringifyHardCode( 0xE00002C1, kIOReturnNotPrivileged ); + CaseErrorStringifyHardCode( 0xE00002C2, kIOReturnBadArgument ); + CaseErrorStringifyHardCode( 0xE00002C3, kIOReturnLockedRead ); + CaseErrorStringifyHardCode( 0xE00002C4, kIOReturnLockedWrite ); + CaseErrorStringifyHardCode( 0xE00002C5, kIOReturnExclusiveAccess ); + CaseErrorStringifyHardCode( 0xE00002C6, kIOReturnBadMessageID ); + CaseErrorStringifyHardCode( 0xE00002C7, kIOReturnUnsupported ); + CaseErrorStringifyHardCode( 0xE00002C8, kIOReturnVMError ); + CaseErrorStringifyHardCode( 0xE00002C9, kIOReturnInternalError ); + CaseErrorStringifyHardCode( 0xE00002CA, kIOReturnIOError ); + CaseErrorStringifyHardCode( 0xE00002CC, kIOReturnCannotLock ); + CaseErrorStringifyHardCode( 0xE00002CD, kIOReturnNotOpen ); + CaseErrorStringifyHardCode( 0xE00002CE, kIOReturnNotReadable ); + CaseErrorStringifyHardCode( 0xE00002CF, kIOReturnNotWritable ); + CaseErrorStringifyHardCode( 0xE00002D0, kIOReturnNotAligned ); + CaseErrorStringifyHardCode( 0xE00002D1, kIOReturnBadMedia ); + CaseErrorStringifyHardCode( 0xE00002D2, kIOReturnStillOpen ); + CaseErrorStringifyHardCode( 0xE00002D3, kIOReturnRLDError ); + CaseErrorStringifyHardCode( 0xE00002D4, kIOReturnDMAError ); + CaseErrorStringifyHardCode( 0xE00002D5, kIOReturnBusy ); + CaseErrorStringifyHardCode( 0xE00002D6, kIOReturnTimeout ); + CaseErrorStringifyHardCode( 0xE00002D7, kIOReturnOffline ); + CaseErrorStringifyHardCode( 0xE00002D8, kIOReturnNotReady ); + CaseErrorStringifyHardCode( 0xE00002D9, kIOReturnNotAttached ); + CaseErrorStringifyHardCode( 0xE00002DA, kIOReturnNoChannels ); + CaseErrorStringifyHardCode( 0xE00002DB, kIOReturnNoSpace ); + CaseErrorStringifyHardCode( 0xE00002DD, kIOReturnPortExists ); + CaseErrorStringifyHardCode( 0xE00002DE, kIOReturnCannotWire ); + CaseErrorStringifyHardCode( 0xE00002DF, kIOReturnNoInterrupt ); + CaseErrorStringifyHardCode( 0xE00002E0, kIOReturnNoFrames ); + CaseErrorStringifyHardCode( 0xE00002E1, kIOReturnMessageTooLarge ); + CaseErrorStringifyHardCode( 0xE00002E2, kIOReturnNotPermitted ); + CaseErrorStringifyHardCode( 0xE00002E3, kIOReturnNoPower ); + CaseErrorStringifyHardCode( 0xE00002E4, kIOReturnNoMedia ); + CaseErrorStringifyHardCode( 0xE00002E5, kIOReturnUnformattedMedia ); + CaseErrorStringifyHardCode( 0xE00002E6, kIOReturnUnsupportedMode ); + CaseErrorStringifyHardCode( 0xE00002E7, kIOReturnUnderrun ); + CaseErrorStringifyHardCode( 0xE00002E8, kIOReturnOverrun ); + CaseErrorStringifyHardCode( 0xE00002E9, kIOReturnDeviceError ); + CaseErrorStringifyHardCode( 0xE00002EA, kIOReturnNoCompletion ); + CaseErrorStringifyHardCode( 0xE00002EB, kIOReturnAborted ); + CaseErrorStringifyHardCode( 0xE00002EC, kIOReturnNoBandwidth ); + CaseErrorStringifyHardCode( 0xE00002ED, kIOReturnNotResponding ); + CaseErrorStringifyHardCode( 0xE00002EE, kIOReturnIsoTooOld ); + CaseErrorStringifyHardCode( 0xE00002EF, kIOReturnIsoTooNew ); + CaseErrorStringifyHardCode( 0xE00002F0, kIOReturnNotFound ); + CaseErrorStringifyHardCode( 0xE0000001, kIOReturnInvalid ); + + // IOKit FireWire Errors + + CaseErrorStringifyHardCode( 0xE0008010, kIOFireWireResponseBase ); + CaseErrorStringifyHardCode( 0xE0008020, kIOFireWireBusReset ); + CaseErrorStringifyHardCode( 0xE0008001, kIOConfigNoEntry ); + CaseErrorStringifyHardCode( 0xE0008002, kIOFireWirePending ); + CaseErrorStringifyHardCode( 0xE0008003, kIOFireWireLastDCLToken ); + CaseErrorStringifyHardCode( 0xE0008004, kIOFireWireConfigROMInvalid ); + CaseErrorStringifyHardCode( 0xE0008005, kIOFireWireAlreadyRegistered ); + CaseErrorStringifyHardCode( 0xE0008006, kIOFireWireMultipleTalkers ); + CaseErrorStringifyHardCode( 0xE0008007, kIOFireWireChannelActive ); + CaseErrorStringifyHardCode( 0xE0008008, kIOFireWireNoListenerOrTalker ); + CaseErrorStringifyHardCode( 0xE0008009, kIOFireWireNoChannels ); + CaseErrorStringifyHardCode( 0xE000800A, kIOFireWireChannelNotAvailable ); + CaseErrorStringifyHardCode( 0xE000800B, kIOFireWireSeparateBus ); + CaseErrorStringifyHardCode( 0xE000800C, kIOFireWireBadSelfIDs ); + CaseErrorStringifyHardCode( 0xE000800D, kIOFireWireLowCableVoltage ); + CaseErrorStringifyHardCode( 0xE000800E, kIOFireWireInsufficientPower ); + CaseErrorStringifyHardCode( 0xE000800F, kIOFireWireOutOfTLabels ); + CaseErrorStringifyHardCode( 0xE0008101, kIOFireWireBogusDCLProgram ); + CaseErrorStringifyHardCode( 0xE0008102, kIOFireWireTalkingAndListening ); + CaseErrorStringifyHardCode( 0xE0008103, kIOFireWireHardwareSlept ); + CaseErrorStringifyHardCode( 0xE00087D0, kIOFWMessageServiceIsRequestingClose ); + CaseErrorStringifyHardCode( 0xE00087D1, kIOFWMessagePowerStateChanged ); + CaseErrorStringifyHardCode( 0xE00087D2, kIOFWMessageTopologyChanged ); + + // IOKit USB Errors + + CaseErrorStringifyHardCode( 0xE0004061, kIOUSBUnknownPipeErr ); + CaseErrorStringifyHardCode( 0xE0004060, kIOUSBTooManyPipesErr ); + CaseErrorStringifyHardCode( 0xE000405F, kIOUSBNoAsyncPortErr ); + CaseErrorStringifyHardCode( 0xE000405E, kIOUSBNotEnoughPipesErr ); + CaseErrorStringifyHardCode( 0xE000405D, kIOUSBNotEnoughPowerErr ); + CaseErrorStringifyHardCode( 0xE0004057, kIOUSBEndpointNotFound ); + CaseErrorStringifyHardCode( 0xE0004056, kIOUSBConfigNotFound ); + CaseErrorStringifyHardCode( 0xE0004051, kIOUSBTransactionTimeout ); + CaseErrorStringifyHardCode( 0xE0004050, kIOUSBTransactionReturned ); + CaseErrorStringifyHardCode( 0xE000404F, kIOUSBPipeStalled ); + CaseErrorStringifyHardCode( 0xE000404E, kIOUSBInterfaceNotFound ); + CaseErrorStringifyHardCode( 0xE000404D, kIOUSBLowLatencyBufferNotPreviouslyAllocated ); + CaseErrorStringifyHardCode( 0xE000404C, kIOUSBLowLatencyFrameListNotPreviouslyAllocated ); + CaseErrorStringifyHardCode( 0xE000404B, kIOUSBHighSpeedSplitError ); + CaseErrorStringifyHardCode( 0xE0004010, kIOUSBLinkErr ); + CaseErrorStringifyHardCode( 0xE000400F, kIOUSBNotSent2Err ); + CaseErrorStringifyHardCode( 0xE000400E, kIOUSBNotSent1Err ); + CaseErrorStringifyHardCode( 0xE000400D, kIOUSBBufferUnderrunErr ); + CaseErrorStringifyHardCode( 0xE000400C, kIOUSBBufferOverrunErr ); + CaseErrorStringifyHardCode( 0xE000400B, kIOUSBReserved2Err ); + CaseErrorStringifyHardCode( 0xE000400A, kIOUSBReserved1Err ); + CaseErrorStringifyHardCode( 0xE0004007, kIOUSBWrongPIDErr ); + CaseErrorStringifyHardCode( 0xE0004006, kIOUSBPIDCheckErr ); + CaseErrorStringifyHardCode( 0xE0004003, kIOUSBDataToggleErr ); + CaseErrorStringifyHardCode( 0xE0004002, kIOUSBBitstufErr ); + CaseErrorStringifyHardCode( 0xE0004001, kIOUSBCRCErr ); + + #endif // __MACH__ + + // Other Errors + + default: + s = NULL; + #if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) + if( inBuffer && ( inBufferSize > 0 ) ) + { + DWORD n; + + n = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) inErrorCode, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), buffer, sizeof( buffer ), NULL ); + if( n > 0 ) + { + // Remove any trailing CR's or LF's since some messages have them. + + while( ( n > 0 ) && isspace( ( (unsigned char *) buffer )[ n - 1 ] ) ) + { + buffer[ --n ] = '\0'; + } + s = buffer; + } + } + #endif + + if( !s ) + { + #if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE ) + s = strerror( inErrorCode ); + #endif + if( !s ) + { + s = ""; + } + } + break; + } + + // Copy the string to the output buffer. If no buffer is supplied or it is empty, return an empty string. + + if( inBuffer && ( inBufferSize > 0 ) ) + { + dst = inBuffer; + end = dst + ( inBufferSize - 1 ); + while( ( ( end - dst ) > 0 ) && ( *s != '\0' ) ) + { + *dst++ = *s++; + } + *dst = '\0'; + s = inBuffer; + } + return( s ); +} + +//=========================================================================================================================== +// DebugHexDump +//=========================================================================================================================== + +DEBUG_EXPORT size_t + DebugHexDump( + DebugLevel inLevel, + int inIndent, + const char * inLabel, + size_t inLabelSize, + int inLabelMinWidth, + const char * inType, + size_t inTypeSize, + const void * inDataStart, + const void * inData, + size_t inDataSize, + DebugFlags inFlags, + char * outBuffer, + size_t inBufferSize ) +{ + static const char kHexChars[] = "0123456789ABCDEF"; + const uint8_t * start; + const uint8_t * src; + char * dst; + char * end; + size_t n; + int offset; + int width; + const char * newline; + char separator[ 8 ]; + char * s; + + DEBUG_UNUSED( inType ); + DEBUG_UNUSED( inTypeSize ); + + // Set up the function-wide variables. + + if( inLabelSize == kSizeCString ) + { + inLabelSize = strlen( inLabel ); + } + start = (const uint8_t *) inData; + src = start; + dst = outBuffer; + end = dst + inBufferSize; + offset = (int)( (intptr_t) inData - (intptr_t) inDataStart ); + width = ( (int) inLabelSize > inLabelMinWidth ) ? (int) inLabelSize : inLabelMinWidth; + newline = ( inFlags & kDebugFlagsNoNewLine ) ? "" : "\n"; + + // Set up the separator string. This is used to insert spaces on subsequent "lines" when not using newlines. + + s = separator; + if( inFlags & kDebugFlagsNoNewLine ) + { + if( inFlags & kDebugFlags8BitSeparator ) + { + *s++ = ' '; + } + if( inFlags & kDebugFlags16BitSeparator ) + { + *s++ = ' '; + } + if( !( inFlags & kDebugFlagsNo32BitSeparator ) ) + { + *s++ = ' '; + } + check( ( (size_t)( s - separator ) ) < sizeof( separator ) ); + } + *s = '\0'; + + for( ;; ) + { + char prefixString[ 32 ]; + char hexString[ 64 ]; + char asciiString[ 32 ]; + char byteCountString[ 32 ]; + int c; + size_t chunkSize; + size_t i; + + // If this is a label-only item (i.e. no data), print the label (accounting for prefix string spacing) and exit. + + if( inDataSize == 0 ) + { + if( inLabel && ( inLabelSize > 0 ) ) + { + width = 0; + if( !( inFlags & kDebugFlagsNoAddress ) ) + { + width += 8; // "00000000" + if( !( inFlags & kDebugFlagsNoOffset ) ) + { + width += 1; // "+" + } + } + if( inFlags & kDebugFlags32BitOffset ) + { + width += 8; // "00000000" + } + else if( !( inFlags & kDebugFlagsNoOffset ) ) + { + width += 4; // "0000" + } + + if( outBuffer ) + { + dst += DebugSNPrintF( dst, (size_t)( end - dst ), "%*s" "%-*.*s" "%.*s" "%s", + width, "", + ( width > 0 ) ? ": " : "", + width, (int) inLabelSize, inLabel, + newline ); + } + else + { + dst += DebugPrintF( inLevel, "%*s" "%-*.*s" "%.*s" "%s", + width, "", + ( width > 0 ) ? ": " : "", + width, (int) inLabelSize, inLabel, + newline ); + } + } + break; + } + + // Build the prefix string. It will be in one of the following formats: + // + // 1) "00000000+0000[0000]" (address and offset) + // 2) "00000000" (address only) + // 3) "0000[0000]" (offset only) + // 4) "" (no address or offset) + // + // Note: If we're printing multiple "lines", but not printing newlines, a space is used to separate. + + s = prefixString; + if( !( inFlags & kDebugFlagsNoAddress ) ) + { + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 28 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 24 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 20 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 16 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 12 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 8 ) & 0xF ]; + *s++ = kHexChars[ ( ( (uintptr_t) src ) >> 4 ) & 0xF ]; + *s++ = kHexChars[ ( (uintptr_t) src ) & 0xF ]; + + if( !( inFlags & kDebugFlagsNoOffset ) ) + { + *s++ = '+'; + } + } + if( !( inFlags & kDebugFlagsNoOffset ) ) + { + if( inFlags & kDebugFlags32BitOffset ) + { + *s++ = kHexChars[ ( offset >> 28 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 24 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 20 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 16 ) & 0xF ]; + } + *s++ = kHexChars[ ( offset >> 12 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 8 ) & 0xF ]; + *s++ = kHexChars[ ( offset >> 4 ) & 0xF ]; + *s++ = kHexChars[ offset & 0xF ]; + } + if( s != prefixString ) + { + *s++ = ':'; + *s++ = ' '; + } + check( ( (size_t)( s - prefixString ) ) < sizeof( prefixString ) ); + *s = '\0'; + + // Build a hex string with a optional spaces after every 1, 2, and/or 4 bytes to make it easier to read. + // Optionally pads the hex string with space to fill the full 16 byte range (so it lines up). + + s = hexString; + chunkSize = ( inDataSize < 16 ) ? inDataSize : 16; + n = ( inFlags & kDebugFlagsNo16ByteHexPad ) ? chunkSize : 16; + for( i = 0; i < n; ++i ) + { + if( ( inFlags & kDebugFlags8BitSeparator ) && ( i > 0 ) ) + { + *s++ = ' '; + } + if( ( inFlags & kDebugFlags16BitSeparator ) && ( i > 0 ) && ( ( i % 2 ) == 0 ) ) + { + *s++ = ' '; + } + if( !( inFlags & kDebugFlagsNo32BitSeparator ) && ( i > 0 ) && ( ( i % 4 ) == 0 ) ) + { + *s++ = ' '; + } + if( i < chunkSize ) + { + *s++ = kHexChars[ src[ i ] >> 4 ]; + *s++ = kHexChars[ src[ i ] & 0xF ]; + } + else + { + *s++ = ' '; + *s++ = ' '; + } + } + check( ( (size_t)( s - hexString ) ) < sizeof( hexString ) ); + *s = '\0'; + + // Build a string with the ASCII version of the data (replaces non-printable characters with '^'). + // Optionally pads the string with '`' to fill the full 16 byte range (so it lines up). + + s = asciiString; + if( !( inFlags & kDebugFlagsNoASCII ) ) + { + *s++ = ' '; + *s++ = '|'; + for( i = 0; i < n; ++i ) + { + if( i < chunkSize ) + { + c = src[ i ]; + if( !DebugIsPrint( c ) ) + { + c = '^'; + } + } + else + { + c = '`'; + } + *s++ = (char) c; + } + *s++ = '|'; + check( ( (size_t)( s - asciiString ) ) < sizeof( asciiString ) ); + } + *s = '\0'; + + // Build a string indicating how bytes are in the hex dump. Only printed on the first line. + + s = byteCountString; + if( !( inFlags & kDebugFlagsNoByteCount ) ) + { + if( src == start ) + { + s += DebugSNPrintF( s, sizeof( byteCountString ), " (%d bytes)", (int) inDataSize ); + } + } + check( ( (size_t)( s - byteCountString ) ) < sizeof( byteCountString ) ); + *s = '\0'; + + // Build the entire line from all the pieces we've previously built. + + if( outBuffer ) + { + if( src == start ) + { + dst += DebugSNPrintF( dst, (size_t)( end - dst ), + "%*s" // Indention + "%s" // Separator (only if needed) + "%s" // Prefix + "%-*.*s" // Label + "%s" // Separator + "%s" // Hex + "%s" // ASCII + "%s" // Byte Count + "%s", // Newline + inIndent, "", + ( src != start ) ? separator : "", + prefixString, + width, (int) inLabelSize, inLabel ? inLabel : "", + ( width > 0 ) ? " " : "", + hexString, + asciiString, + byteCountString, + newline ); + } + else + { + dst += DebugSNPrintF( dst, (size_t)( end - dst ), + "%*s" // Indention + "%s" // Separator (only if needed) + "%s" // Prefix + "%*s" // Label Spacing + "%s" // Separator + "%s" // Hex + "%s" // ASCII + "%s" // Byte Count + "%s", // Newline + inIndent, "", + ( src != start ) ? separator : "", + prefixString, + width, "", + ( width > 0 ) ? " " : "", + hexString, + asciiString, + byteCountString, + newline ); + } + } + else + { + if( src == start ) + { + dst += DebugPrintF( inLevel, + "%*s" // Indention + "%s" // Separator (only if needed) + "%s" // Prefix + "%-*.*s" // Label + "%s" // Separator + "%s" // Hex + "%s" // ASCII + "%s" // Byte Count + "%s", // Newline + inIndent, "", + ( src != start ) ? separator : "", + prefixString, + width, (int) inLabelSize, inLabel, + ( width > 0 ) ? " " : "", + hexString, + asciiString, + byteCountString, + newline ); + } + else + { + dst += DebugPrintF( inLevel, + "%*s" // Indention + "%s" // Separator (only if needed) + "%s" // Prefix + "%*s" // Label Spacing + "%s" // Separator + "%s" // Hex + "%s" // ASCII + "%s" // Byte Count + "%s", // Newline + inIndent, "", + ( src != start ) ? separator : "", + prefixString, + width, "", + ( width > 0 ) ? " " : "", + hexString, + asciiString, + byteCountString, + newline ); + } + } + + // Move to the next chunk. Exit if there is no more data. + + offset += (int) chunkSize; + src += chunkSize; + inDataSize -= chunkSize; + if( inDataSize == 0 ) + { + break; + } + } + + // Note: The "dst - outBuffer" size calculation works even if "outBuffer" is NULL because it's all relative. + + return( (size_t)( dst - outBuffer ) ); +} + +//=========================================================================================================================== +// DebugNumVersionToString +//=========================================================================================================================== + +static char * DebugNumVersionToString( uint32_t inVersion, char *inString ) +{ + char * s; + uint8_t majorRev; + uint8_t minor; + uint8_t bugFix; + uint8_t stage; + uint8_t revision; + + check( inString ); + + majorRev = (uint8_t)( ( inVersion >> 24 ) & 0xFF ); + minor = (uint8_t)( ( inVersion >> 20 ) & 0x0F ); + bugFix = (uint8_t)( ( inVersion >> 16 ) & 0x0F ); + stage = (uint8_t)( ( inVersion >> 8 ) & 0xFF ); + revision = (uint8_t)( inVersion & 0xFF ); + + // Convert the major, minor, and bugfix numbers. + + s = inString; + s += sprintf( s, "%u", majorRev ); + s += sprintf( s, ".%u", minor ); + if( bugFix != 0 ) + { + s += sprintf( s, ".%u", bugFix ); + } + + // Convert the version stage and non-release revision number. + + switch( stage ) + { + case kVersionStageDevelopment: + s += sprintf( s, "d%u", revision ); + break; + + case kVersionStageAlpha: + s += sprintf( s, "a%u", revision ); + break; + + case kVersionStageBeta: + s += sprintf( s, "b%u", revision ); + break; + + case kVersionStageFinal: + + // A non-release revision of zero is a special case indicating the software is GM (at the golden master + // stage) and therefore, the non-release revision should not be added to the string. + + if( revision != 0 ) + { + s += sprintf( s, "f%u", revision ); + } + break; + + default: + dlog( kDebugLevelError, "invalid NumVersion stage (0x%02X)\n", stage ); + break; + } + return( inString ); +} + +//=========================================================================================================================== +// DebugTaskLevel +//=========================================================================================================================== + +DEBUG_EXPORT uint32_t DebugTaskLevel( void ) +{ + uint32_t level; + + level = 0; + +#if( TARGET_OS_VXWORKS ) + if( intContext() ) + { + level |= ( ( 1 << kDebugInterruptLevelShift ) & kDebugInterruptLevelMask ); + } +#endif + + return( level ); +} + +#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE ) +//=========================================================================================================================== +// DebugWinEnableConsole +//=========================================================================================================================== + +#pragma warning( disable:4311 ) + +static void DebugWinEnableConsole( void ) +{ + static bool sConsoleEnabled = false; + BOOL result; + int fileHandle; + FILE * file; + int err; + + if( sConsoleEnabled ) + { + goto exit; + } + + // Create console window. + + result = AllocConsole(); + require_quiet( result, exit ); + + // Redirect stdin to the console stdin. + + fileHandle = _open_osfhandle( (long) GetStdHandle( STD_INPUT_HANDLE ), _O_TEXT ); + + #if( defined( __MWERKS__ ) ) + file = __handle_reopen( (unsigned long) fileHandle, "r", stdin ); + require_quiet( file, exit ); + #else + file = _fdopen( fileHandle, "r" ); + require_quiet( file, exit ); + + *stdin = *file; + #endif + + err = setvbuf( stdin, NULL, _IONBF, 0 ); + require_noerr_quiet( err, exit ); + + // Redirect stdout to the console stdout. + + fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT ); + + #if( defined( __MWERKS__ ) ) + file = __handle_reopen( (unsigned long) fileHandle, "w", stdout ); + require_quiet( file, exit ); + #else + file = _fdopen( fileHandle, "w" ); + require_quiet( file, exit ); + + *stdout = *file; + #endif + + err = setvbuf( stdout, NULL, _IONBF, 0 ); + require_noerr_quiet( err, exit ); + + // Redirect stderr to the console stdout. + + fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT ); + + #if( defined( __MWERKS__ ) ) + file = __handle_reopen( (unsigned long) fileHandle, "w", stderr ); + require_quiet( file, exit ); + #else + file = _fdopen( fileHandle, "w" ); + require_quiet( file, exit ); + + *stderr = *file; + #endif + + err = setvbuf( stderr, NULL, _IONBF, 0 ); + require_noerr_quiet( err, exit ); + + sConsoleEnabled = true; + +exit: + return; +} + +#pragma warning( default:4311 ) + +#endif // TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE + +#if( TARGET_OS_WIN32 ) +//=========================================================================================================================== +// DebugWinCharToTCharString +//=========================================================================================================================== + +static TCHAR * + DebugWinCharToTCharString( + const char * inCharString, + size_t inCharCount, + TCHAR * outTCharString, + size_t inTCharCountMax, + size_t * outTCharCount ) +{ + const char * src; + TCHAR * dst; + TCHAR * end; + + if( inCharCount == kSizeCString ) + { + inCharCount = strlen( inCharString ); + } + src = inCharString; + dst = outTCharString; + if( inTCharCountMax > 0 ) + { + inTCharCountMax -= 1; + if( inTCharCountMax > inCharCount ) + { + inTCharCountMax = inCharCount; + } + + end = dst + inTCharCountMax; + while( dst < end ) + { + *dst++ = (TCHAR) *src++; + } + *dst = 0; + } + if( outTCharCount ) + { + *outTCharCount = (size_t)( dst - outTCharString ); + } + return( outTCharString ); +} +#endif + +#if 0 +#pragma mark - +#pragma mark == Debugging == +#endif + +//=========================================================================================================================== +// DebugServicesTest +//=========================================================================================================================== + +DEBUG_EXPORT OSStatus DebugServicesTest( void ) +{ + OSStatus err; + char s[ 512 ]; + uint8_t * p; + uint8_t data[] = + { + 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, + 0x77, 0x88, 0x99, 0xAA, + 0xBB, 0xCC, 0xDD, + 0xEE, + 0xFF, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, + 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1 + }; + + debug_initialize( kDebugOutputTypeMetaConsole ); + + // check's + + check( 0 && "SHOULD SEE: check" ); + check( 1 && "SHOULD *NOT* SEE: check (valid)" ); + check_string( 0, "SHOULD SEE: check_string" ); + check_string( 1, "SHOULD *NOT* SEE: check_string (valid)" ); + check_noerr( -123 ); + check_noerr( 10038 ); + check_noerr( 22 ); + check_noerr( 0 ); + check_noerr_string( -6712, "SHOULD SEE: check_noerr_string" ); + check_noerr_string( 0, "SHOULD *NOT* SEE: check_noerr_string (valid)" ); + check_translated_errno( 0 >= 0 && "SHOULD *NOT* SEE", -384, -999 ); + check_translated_errno( -1 >= 0 && "SHOULD SEE", -384, -999 ); + check_translated_errno( -1 >= 0 && "SHOULD SEE", 0, -999 ); + check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 22, 10 ); + check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 5, 10 ); + check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 12, 6 ); + check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 6, 10, 10 ); + check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 10, 10, 10 ); + check_ptr_overlap( "SHOULD *NOT* SEE" ? 22 : 0, 10, 10, 10 ); + check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 20, 10 ); + check_ptr_overlap( "SHOULD *NOT* SEE" ? 20 : 0, 10, 10, 10 ); + + // require's + + require( 0 && "SHOULD SEE", require1 ); + { err = kResponseErr; goto exit; } +require1: + require( 1 && "SHOULD *NOT* SEE", require2 ); + goto require2Good; +require2: + { err = kResponseErr; goto exit; } +require2Good: + require_string( 0 && "SHOULD SEE", require3, "SHOULD SEE: require_string" ); + { err = kResponseErr; goto exit; } +require3: + require_string( 1 && "SHOULD *NOT* SEE", require4, "SHOULD *NOT* SEE: require_string (valid)" ); + goto require4Good; +require4: + { err = kResponseErr; goto exit; } +require4Good: + require_quiet( 0 && "SHOULD SEE", require5 ); + { err = kResponseErr; goto exit; } +require5: + require_quiet( 1 && "SHOULD *NOT* SEE", require6 ); + goto require6Good; +require6: + { err = kResponseErr; goto exit; } +require6Good: + require_noerr( -1, require7 ); + { err = kResponseErr; goto exit; } +require7: + require_noerr( 0, require8 ); + goto require8Good; +require8: + { err = kResponseErr; goto exit; } +require8Good: + require_noerr_string( -2, require9, "SHOULD SEE: require_noerr_string"); + { err = kResponseErr; goto exit; } +require9: + require_noerr_string( 0, require10, "SHOULD *NOT* SEE: require_noerr_string (valid)" ); + goto require10Good; +require10: + { err = kResponseErr; goto exit; } +require10Good: + require_noerr_action_string( -3, require11, dlog( kDebugLevelMax, "action 1 (expected)\n" ), "require_noerr_action_string" ); + { err = kResponseErr; goto exit; } +require11: + require_noerr_action_string( 0, require12, dlog( kDebugLevelMax, "action 2\n" ), "require_noerr_action_string (valid)" ); + goto require12Good; +require12: + { err = kResponseErr; goto exit; } +require12Good: + require_noerr_quiet( -4, require13 ); + { err = kResponseErr; goto exit; } +require13: + require_noerr_quiet( 0, require14 ); + goto require14Good; +require14: + { err = kResponseErr; goto exit; } +require14Good: + require_noerr_action( -5, require15, dlog( kDebugLevelMax, "SHOULD SEE: action 3 (expected)\n" ) ); + { err = kResponseErr; goto exit; } +require15: + require_noerr_action( 0, require16, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 4\n" ) ); + goto require16Good; +require16: + { err = kResponseErr; goto exit; } +require16Good: + require_noerr_action_quiet( -4, require17, dlog( kDebugLevelMax, "SHOULD SEE: action 5 (expected)\n" ) ); + { err = kResponseErr; goto exit; } +require17: + require_noerr_action_quiet( 0, require18, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 6\n" ) ); + goto require18Good; +require18: + { err = kResponseErr; goto exit; } +require18Good: + require_action( 0 && "SHOULD SEE", require19, dlog( kDebugLevelMax, "SHOULD SEE: action 7 (expected)\n" ) ); + { err = kResponseErr; goto exit; } +require19: + require_action( 1 && "SHOULD *NOT* SEE", require20, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 8\n" ) ); + goto require20Good; +require20: + { err = kResponseErr; goto exit; } +require20Good: + require_action_quiet( 0, require21, dlog( kDebugLevelMax, "SHOULD SEE: action 9 (expected)\n" ) ); + { err = kResponseErr; goto exit; } +require21: + require_action_quiet( 1, require22, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 10\n" ) ); + goto require22Good; +require22: + { err = kResponseErr; goto exit; } +require22Good: + require_action_string( 0, require23, dlog( kDebugLevelMax, "SHOULD SEE: action 11 (expected)\n" ), "SHOULD SEE: require_action_string" ); + { err = kResponseErr; goto exit; } +require23: + require_action_string( 1, require24, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 12\n" ), "SHOULD *NOT* SEE: require_action_string" ); + goto require24Good; +require24: + { err = kResponseErr; goto exit; } +require24Good: + +#if( defined( __MWERKS__ ) ) + #if( defined( __cplusplus ) && __option( exceptions ) ) + #define COMPILER_HAS_EXCEPTIONS 1 + #else + #define COMPILER_HAS_EXCEPTIONS 0 + #endif +#else + #if( defined( __cplusplus ) ) + #define COMPILER_HAS_EXCEPTIONS 1 + #else + #define COMPILER_HAS_EXCEPTIONS 0 + #endif +#endif + +#if( COMPILER_HAS_EXCEPTIONS ) + try + { + require_throw( 1 && "SHOULD *NOT* SEE" ); + require_throw( 0 && "SHOULD SEE" ); + } + catch( ... ) + { + goto require26Good; + } + { err = kResponseErr; goto exit; } +require26Good: +#endif + + // translate_errno + + err = translate_errno( 1 != -1, -123, -567 ); + require( ( err == 0 ) && "SHOULD *NOT* SEE", exit ); + + err = translate_errno( -1 != -1, -123, -567 ); + require( ( err == -123 ) && "SHOULD *NOT* SEE", exit ); + + err = translate_errno( -1 != -1, 0, -567 ); + require( ( err == -567 ) && "SHOULD *NOT* SEE", exit ); + + // debug_string + + debug_string( "debug_string" ); + + // DebugSNPrintF + + DebugSNPrintF( s, sizeof( s ), "%d", 1234 ); + require_action( strcmp( s, "1234" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%X", 0x2345 ); + require_action( strcmp( s, "2345" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%#s", "\05test" ); + require_action( strcmp( s, "test" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%##s", "\03www\05apple\03com" ); + require_action( strcmp( s, "www.apple.com." ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%ld", (long) INT32_C( 2147483647 ) ); + require_action( strcmp( s, "2147483647" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%lu", (unsigned long) UINT32_C( 4294967295 ) ); + require_action( strcmp( s, "4294967295" ) == 0, exit, err = -1 ); + + #if( TYPE_LONGLONG_NATIVE ) + DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( 9223372036854775807 ) ); + require_action( strcmp( s, "9223372036854775807" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( -9223372036854775807 ) ); + require_action( strcmp( s, "-9223372036854775807" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%llu", (unsigned_long_long_compat) UINT64_C( 18446744073709551615 ) ); + require_action( strcmp( s, "18446744073709551615" ) == 0, exit, err = -1 ); + #endif + + DebugSNPrintF( s, sizeof( s ), "%lb", (unsigned long) binary_32( 01111011, 01111011, 01111011, 01111011 ) ); + require_action( strcmp( s, "1111011011110110111101101111011" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%C", 0x41624364 ); // 'AbCd' + require_action( strcmp( s, "AbCd" ) == 0, exit, err = -1 ); + + #if( defined( MDNS_DEBUGMSGS ) ) + { + mDNSAddr maddr; + + memset( &maddr, 0, sizeof( maddr ) ); + maddr.type = mDNSAddrType_IPv4; + maddr.ip.v4.b[ 0 ] = 127; + maddr.ip.v4.b[ 1 ] = 0; + maddr.ip.v4.b[ 2 ] = 0; + maddr.ip.v4.b[ 3 ] = 1; + DebugSNPrintF( s, sizeof( s ), "%#a", &maddr ); + require_action( strcmp( s, "127.0.0.1" ) == 0, exit, err = -1 ); + + memset( &maddr, 0, sizeof( maddr ) ); + maddr.type = mDNSAddrType_IPv6; + maddr.ip.v6.b[ 0 ] = 0xFE; + maddr.ip.v6.b[ 1 ] = 0x80; + maddr.ip.v6.b[ 15 ] = 0x01; + DebugSNPrintF( s, sizeof( s ), "%#a", &maddr ); + require_action( strcmp( s, "FE80:0000:0000:0000:0000:0000:0000:0001" ) == 0, exit, err = -1 ); + } + #endif + + #if( AF_INET ) + { + struct sockaddr_in sa4; + + memset( &sa4, 0, sizeof( sa4 ) ); + sa4.sin_family = AF_INET; + p = (uint8_t *) &sa4.sin_port; + p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF ); + p[ 1 ] = (uint8_t)( 80 & 0xFF ); + p = (uint8_t *) &sa4.sin_addr.s_addr; + p[ 0 ] = (uint8_t)( ( INADDR_LOOPBACK >> 24 ) & 0xFF ); + p[ 1 ] = (uint8_t)( ( INADDR_LOOPBACK >> 16 ) & 0xFF ); + p[ 2 ] = (uint8_t)( ( INADDR_LOOPBACK >> 8 ) & 0xFF ); + p[ 3 ] = (uint8_t)( INADDR_LOOPBACK & 0xFF ); + DebugSNPrintF( s, sizeof( s ), "%##a", &sa4 ); + require_action( strcmp( s, "127.0.0.1:80" ) == 0, exit, err = -1 ); + } + #endif + + #if( AF_INET6 ) + { + struct sockaddr_in6 sa6; + + memset( &sa6, 0, sizeof( sa6 ) ); + sa6.sin6_family = AF_INET6; + p = (uint8_t *) &sa6.sin6_port; + p[ 0 ] = (uint8_t)( ( 80 >> 8 ) & 0xFF ); + p[ 1 ] = (uint8_t)( 80 & 0xFF ); + sa6.sin6_addr.s6_addr[ 0 ] = 0xFE; + sa6.sin6_addr.s6_addr[ 1 ] = 0x80; + sa6.sin6_addr.s6_addr[ 15 ] = 0x01; + sa6.sin6_scope_id = 2; + DebugSNPrintF( s, sizeof( s ), "%##a", &sa6 ); + require_action( strcmp( s, "[FE80:0000:0000:0000:0000:0000:0000:0001%2]:80" ) == 0, exit, err = -1 ); + } + #endif + + // Unicode + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes" ); + require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "test" ); + require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "testing" ); + require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9" ); + require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9ing" ); + require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes\xC3\xA9ing" ); + require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbf" ); + require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbfing" ); + require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbf" ); + require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbfing" ); + require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 7, "te\xC3\xA9\xed\x9f\xbfing" ); + require_action( strcmp( s, "te\xC3\xA9\xed\x9f\xbf" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 6, "te\xC3\xA9\xed\x9f\xbfing" ); + require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); + + DebugSNPrintF(s, sizeof(s), "%.*s", 5, "te\xC3\xA9\xed\x9f\xbfing" ); + require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr ); + + #if( TARGET_RT_BIG_ENDIAN ) + DebugSNPrintF( s, sizeof( s ), "%S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + #else + DebugSNPrintF( s, sizeof( s ), "%S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + #endif + + DebugSNPrintF( s, sizeof( s ), "%S", + "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian BOM + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%S", + "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian BOM + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%#S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" ); // Big Endian + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%##S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" ); // Little Endian + require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%.*S", + 4, "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian BOM + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%.*S", + 4, "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian BOM + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + + #if( TARGET_RT_BIG_ENDIAN ) + DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + #else + DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + #endif + + DebugSNPrintF( s, sizeof( s ), "%#.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" ); // Big Endian + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%##.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" ); // Little Endian + require_action( strcmp( s, "abc" ) == 0, exit, err = -1 ); + + // Misc + + DebugSNPrintF( s, sizeof( s ), "%U", "\x10\xb8\xa7\x6b" "\xad\x9d" "\xd1\x11" "\x80\xb4" "\x00\xc0\x4f\xd4\x30\xc8" ); + require_action( strcmp( s, "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%m", 0 ); + require_action( strcmp( s, "no error" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "%lm", (long) 0 ); + require_action( strcmp( s, "no error" ) == 0, exit, err = -1 ); + + DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 16, 16 ); + DebugPrintF( kDebugLevelMax, "%s\n\n", s ); + + DebugSNPrintF( s, sizeof( s ), "\"%H\"", + "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8" + "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", + 32, 32 ); + DebugPrintF( kDebugLevelMax, "%s\n\n", s ); + + DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7", 2, 2 ); + DebugPrintF( kDebugLevelMax, "%s\n\n", s ); + + // Hex Dumps + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNone, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoOffset, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoOffset, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoByteCount, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, "\x41\x62\x43\x64", "\x41\x62\x43\x64", 4, // 'AbCd' + kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine | + kDebugFlagsNo32BitSeparator | kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, + s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), + kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoASCII | kDebugFlagsNoNewLine | + kDebugFlags16BitSeparator | kDebugFlagsNo32BitSeparator | + kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + s[ 0 ] = '\0'; + DebugHexDump( kDebugLevelMax, 8, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), kDebugFlagsNone, s, sizeof( s ) ); + DebugPrintF( kDebugLevelMax, "%s\n", s ); + + // dlog's + + dlog( kDebugLevelNotice, "dlog\n" ); + dlog( kDebugLevelNotice, "dlog integer: %d\n", 123 ); + dlog( kDebugLevelNotice, "dlog string: \"%s\"\n", "test string" ); + dlogmem( kDebugLevelNotice, data, sizeof( data ) ); + + // Done + + DebugPrintF( kDebugLevelMax, "\n\nALL TESTS DONE\n\n" ); + err = kNoErr; + +exit: + if( err ) + { + DebugPrintF( kDebugLevelMax, "\n\n### TEST FAILED ###\n\n" ); + } + return( err ); +} + +#endif // DEBUG diff --git a/mDNSWindows/DebugServices.h b/mDNSWindows/DebugServices.h new file mode 100644 index 0000000..fd914ee --- /dev/null +++ b/mDNSWindows/DebugServices.h @@ -0,0 +1,1633 @@ +/* + * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: DebugServices.h,v $ +Revision 1.4 2004/04/15 08:59:08 bradley +Removed deprecated debug and log levels and replaced them with modern equivalents. + +Revision 1.3 2004/04/08 09:29:55 bradley +Manually do host->network byte order conversion to avoid needing libraries for htons/htonl. Changed +hex dumps to better separate hex and ASCII. Added support for %.8a syntax in DebugSNPrintF for Fibre +Channel addresses (00:11:22:33:44:55:66:77). Fixed a few places where HeaderDoc was incorrect. + +Revision 1.2 2004/03/07 05:59:34 bradley +Sync'd with internal version: Added expect macros, error codes, and CoreServices exclusion. + +Revision 1.1 2004/01/30 02:27:30 bradley +Debugging support for various platforms. + +*/ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header DebugServices + + Debugging Library +*/ + +#ifndef __DEBUG_SERVICES__ +#define __DEBUG_SERVICES__ + +#include + +#include "CommonServices.h" + +#if( TARGET_OS_VXWORKS ) + #include "logLib.h" +#endif + +#if 0 +#pragma mark == Settings == +#endif + +//=========================================================================================================================== +// Settings +//=========================================================================================================================== + +// General + +#if( !defined( DEBUG ) ) + #define DEBUG 0 +#endif + +#if( defined( NDEBUG ) && DEBUG ) + #error NDEBUG defined and DEBUG is also enabled...they need to be in-sync +#endif + +// AssertMacros.h/Debugging.h overrides. + +#if( !defined( DEBUG_OVERRIDE_APPLE_MACROS ) ) + #define DEBUG_OVERRIDE_APPLE_MACROS 1 +#endif + +// Routine name. Uses ISO __func__ where possible. Otherwise, uses the best thing that is available (if anything). + +#if( defined( __MWERKS__ ) || ( __GNUC__ > 2 ) || ( ( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 9 ) ) ) + #define __ROUTINE__ __func__ +#elif( defined( __GNUC__ ) ) + #define __ROUTINE__ __PRETTY_FUNCTION__ +#elif( defined( _MSC_VER ) && !defined( _WIN32_WCE ) ) + #define __ROUTINE__ __FUNCTION__ +#else + #define __ROUTINE__ "" +#endif + +// Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing. + +#if( defined( __GNUC__ ) ) + #if( ( __GNUC__ > 3 ) || ( ( __GNUC__ == 3 ) && ( __GNUC_MINOR__ >= 3) ) ) + #define DEBUG_C99_VA_ARGS 1 + #define DEBUG_GNU_VA_ARGS 0 + #else + #define DEBUG_C99_VA_ARGS 0 + #define DEBUG_GNU_VA_ARGS 1 + #endif +#elif( defined( __MWERKS__ ) ) + #define DEBUG_C99_VA_ARGS 1 + #define DEBUG_GNU_VA_ARGS 0 +#else + #define DEBUG_C99_VA_ARGS 0 + #define DEBUG_GNU_VA_ARGS 0 +#endif + +#if 0 +#pragma mark == Output == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_FPRINTF_ENABLED + + @abstract Enables ANSI C fprintf output. +*/ + +#if( !defined( DEBUG_FPRINTF_ENABLED ) ) + #if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE ) + #define DEBUG_FPRINTF_ENABLED 1 + #else + #define DEBUG_FPRINTF_ENABLED 0 + #endif +#else + #if( TARGET_API_MAC_OSX_KERNEL || TARGET_OS_WINDOWS_CE ) + #error fprintf enabled, but not supported on Mac OS X kernel or Windows CE + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_MAC_OS_X_IOLOG_ENABLED + + @abstract Enables IOLog (Mac OS X Kernel) output. +*/ + +#if( !defined( DEBUG_MAC_OS_X_IOLOG_ENABLED ) ) + #define DEBUG_MAC_OS_X_IOLOG_ENABLED TARGET_API_MAC_OSX_KERNEL +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_KPRINTF_ENABLED + + @abstract Enables kprintf (Mac OS X Kernel) output. +*/ + +#if( !defined( DEBUG_KPRINTF_ENABLED ) ) + #define DEBUG_KPRINTF_ENABLED TARGET_API_MAC_OSX_KERNEL +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_IDEBUG_ENABLED + + @abstract Enables iDebug (Mac OS X user and Kernel) output. + + @discussion + + For Mac OS X kernel development, iDebug is enabled by default because we can dynamically check for the presence + of iDebug via some exported IOKit symbols. Mac OS X app usage doesn't allow dynamic detection because it relies + on statically linking to the iDebugServices.cp file so for Mac OS X app usage, you have to manually enable iDebug. +*/ + +#if( !defined( DEBUG_IDEBUG_ENABLED ) ) + #define DEBUG_IDEBUG_ENABLED TARGET_API_MAC_OSX_KERNEL +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_CORE_SERVICE_ASSERTS_ENABLED + + @abstract Controls whether Core Services assert handling is enabled. Enabling requires CoreServices framework. +*/ + +#if( !defined( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) ) + #if( defined( __DEBUGGING__ ) ) + #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 1 + #else + #define DEBUG_CORE_SERVICE_ASSERTS_ENABLED 0 + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugOutputType + + @abstract Type of debug output (i.e. where the output goes). +*/ + +typedef uint32_t DebugOutputType; + +#define kDebugOutputTypeNone 0x6E6F6E65U // 'none' - no params +#define kDebugOutputTypeCustom 0x63757374U // 'cust' - 1st param = function ptr, 2nd param = context +#define kDebugOutputTypeFPrintF 0x66707269U // 'fpri' - 1st param = DebugOutputTypeFlags [, 2nd param = filename] +#define kDebugOutputTypeiDebug 0x69646267U // 'idbg' - no params +#define kDebugOutputTypeKPrintF 0x6B707266U // 'kprf' - no params +#define kDebugOutputTypeMacOSXIOLog 0x696C6F67U // 'ilog' - no params +#define kDebugOutputTypeMacOSXLog 0x786C6F67U // 'xlog' - no params +#define kDebugOutputTypeWindowsDebugger 0x77696E64U // 'wind' - no params +#define kDebugOutputTypeWindowsEventLog 0x7765766CU // 'wevl' - 1st param = C-string name, 2nd param = HMODULE or NULL. + +// Console meta output kind - Any kind of Console output (in horizontal order of preference): +// +// Mac OS X = ANSI printf (viewable in Console.app) +// Mac OS X Kernel = IOLog (/var/log/system.log) or kprintf (serial). +// Windows = ANSI printf (Console window) or OutputDebugString (debugger). +// Other = ANSI printf (viewer varies). + +#define kDebugOutputTypeMetaConsole 0x434F4E53U // 'CONS' - no params + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugOutputTypeFlags + + @abstract Flags controlling how the output type is configured. + + @constant kDebugOutputTypeFlagsTypeMask Bit mask for the output type (e.g. stdout, stderr, file, etc.). + @constant kDebugOutputTypeFlagsStdOut fprintf should go to stdout. + @constant kDebugOutputTypeFlagsStdErr fprintf should go to stderr. + @constant kDebugOutputTypeFlagsFile fprintf should go to a specific file (filename passed as va_arg). +*/ + +typedef unsigned int DebugOutputTypeFlags; + +#define kDebugOutputTypeFlagsTypeMask 0xF +#define kDebugOutputTypeFlagsStdOut 1 +#define kDebugOutputTypeFlagsStdErr 2 +#define kDebugOutputTypeFlagsFile 10 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugOutputFunctionPtr + + @abstract Function ptr for a custom callback to print debug output. +*/ + +typedef void ( *DebugOutputFunctionPtr )( char *inData, size_t inSize, void *inContext ); + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#if 0 +#pragma mark == Flags == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugFlags + + @abstract Flags controlling how output is printed. +*/ + +typedef uint32_t DebugFlags; + +#define kDebugFlagsNone 0 +#define kDebugFlagsNoAddress ( 1 << 0 ) +#define kDebugFlagsNoOffset ( 1 << 1 ) +#define kDebugFlags32BitOffset ( 1 << 2 ) +#define kDebugFlagsNoASCII ( 1 << 3 ) +#define kDebugFlagsNoNewLine ( 1 << 4 ) +#define kDebugFlags8BitSeparator ( 1 << 5 ) +#define kDebugFlags16BitSeparator ( 1 << 6 ) +#define kDebugFlagsNo32BitSeparator ( 1 << 7 ) +#define kDebugFlagsNo16ByteHexPad ( 1 << 8 ) +#define kDebugFlagsNoByteCount ( 1 << 9 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum DebugTaskLevelFlags + + @abstract Flags indicating the task level. +*/ + +enum +{ + kDebugInterruptLevelShift = 0, + kDebugInterruptLevelMask = 0x00000007, + kDebugInVBLTaskMask = 0x00000010, + kDebugInDeferredTaskMask = 0x00000020, + kDebugInSecondaryInterruptHandlerMask = 0x00000040, + kDebugPageFaultFatalMask = 0x00000100, // There should be a "kPageFaultFatalMask" in Debugging.h. + kDebugMPTaskLevelMask = 0x00000200, // There should be a "kMPTaskLevelMask" in Debugging.h. + kDebugInterruptDepthShift = 16, + kDebugInterruptDepthMask = 0x00FF0000 +}; + +#define DebugExtractTaskLevelInterruptLevel( LEVEL ) \ + ( ( ( LEVEL ) & kDebugInterruptLevelMask ) >> kDebugInterruptLevelShift ) + +#define DebugExtractTaskLevelInterruptDepth( LEVEL ) \ + ( ( ( LEVEL ) & kDebugInterruptDepthMask ) >> kDebugInterruptDepthShift ) + +#if 0 +#pragma mark == Levels == +#endif + +//=========================================================================================================================== +// Constants & Types - Levels +//=========================================================================================================================== + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugLevel + + @abstract Level used to control debug logging. +*/ + +typedef int32_t DebugLevel; + +// Levels + +#define kDebugLevelMask 0x0000FFFF +#define kDebugLevelChatty 100 +#define kDebugLevelVerbose 500 +#define kDebugLevelTrace 800 +#define kDebugLevelInfo 1000 +#define kDebugLevelNotice 3000 +#define kDebugLevelWarning 5000 +#define kDebugLevelAssert 6000 +#define kDebugLevelRequire 7000 +#define kDebugLevelError 8000 +#define kDebugLevelCritical 9000 +#define kDebugLevelAlert 10000 +#define kDebugLevelEmergency 11000 +#define kDebugLevelTragic 12000 +#define kDebugLevelMax 0x0000FFFF + +// Level Flags + +#define kDebugLevelFlagMask 0xFFFF0000 +#define kDebugLevelFlagStackTrace 0x00010000 +#define kDebugLevelFlagDebugBreak 0x00020000 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef LogLevel + + @abstract Level used to control which events are logged. +*/ + +typedef int32_t LogLevel; + +#define kLogLevelUninitialized -1L +#define kLogLevelAll 0L +#define kLogLevelChatty 100L +#define kLogLevelVerbose 500L +#define kLogLevelTrace 800L +#define kLogLevelInfo 1000L +#define kLogLevelNotice 3000L +#define kLogLevelWarning 4000L +#define kLogLevelAssert 6000L +#define kLogLevelRequire 7000L +#define kLogLevelError 8000L +#define kLogLevelCritical 9000L +#define kLogLevelAlert 10000L +#define kLogLevelEmergency 11000L +#define kLogLevelTragic 12000L +#define kLogLevelOff 0x0000FFFEL + +#if 0 +#pragma mark == Properties == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef DebugPropertyTag + + @abstract Tag for properties. +*/ + +typedef uint32_t DebugPropertyTag; + +#define kDebugPropertyTagPrintLevelMin 0x6D696E70U // 'minp' Get: 1st param = DebugLevel * + // Set: 1st param = DebugLevel + +#define kDebugPropertyTagPrintLevel kDebugPropertyTagPrintLevelMin + +#define kDebugPropertyTagPrintLevelMax 0x706D786CU // 'maxp' Get: 1st param = DebugLevel * + // Set: 1st param = DebugLevel + +#define kDebugPropertyTagBreakLevel 0x62726B6CU // 'brkl' Get: 1st param = DebugLevel * + // Set: 1st param = DebugLevel +#if 0 +#pragma mark == General macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_UNUSED + + @abstract Macro to mark a paramter as unused to avoid unused parameter warnings. + + @discussion + + There is no universally supported pragma/attribute for indicating a variable is unused. DEBUG_UNUSED lets us + indicate a variable is unused in a manner that is supported by most compilers. +*/ + +#define DEBUG_UNUSED( X ) (void)( X ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_USE_ONLY + + @abstract Macro to mark a variable as used only when debugging is enabled. + + @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 these macros to indicate variables that + are only used for debugging. +*/ + +#if( DEBUG ) + #define DEBUG_USE_ONLY( X ) +#else + #define DEBUG_USE_ONLY( X ) (void)( X ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_LOCAL + + @abstract Macros to make variables and functions static when debugging is off, but extern 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 DEBUG_LOCAL +#else + #define DEBUG_LOCAL static +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_STATIC + + @abstract Macros to make variables and functions static when debugging is off, but extern 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 DEBUG_STATIC +#else + #define DEBUG_STATIC static +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DEBUG_EXPORT + + @abstract Macros to export variables. + + @discussion + + "__private_extern__" is a hack for IOKit to allow symbols to be exported from compilation units, but + // not exported outside a driver (IOKit uses a lame global namespace for symbols). This still does not + // solve the problem of multiple drivers in the same dependency chain since they share symbols. +*/ + +#if( TARGET_API_MAC_OSX_KERNEL ) + #define DEBUG_EXPORT __private_extern__ +#else + #define DEBUG_EXPORT extern +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined debug_add + + @abstract Macro to add (or subtract if negative) a value when debugging is on. Does nothing if debugging is off. +*/ + +#if( DEBUG ) + #define debug_add( A, B ) ( A ) += ( B ) +#else + #define debug_add( A, B ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined debug_perform + + @abstract Macro to perform something in debug-only builds. +*/ + +#if( DEBUG ) + #define debug_perform( X ) do { X; } while( 0 ) +#else + #define debug_perform( X ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function translate_errno + + @abstract Returns 0 if the test success. If the test fails, returns errno if non-zero and othewise the alternate error. +*/ + +#define translate_errno( TEST, ERRNO, ALTERNATE_ERROR ) ( ( TEST ) ? 0 : ( ERRNO ) ? ( ERRNO ) : ( ALTERNATE_ERROR ) ) + +#if 0 +#pragma mark == Compile Time macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_compile_time + + @abstract Performs a compile-time check of something such as the size of an int. + + @discussion + + This declares an array with a size that is determined by a compile-time expression. If the expression evaluates + to 0, the array has a size of -1, which is illegal and generates a compile-time error. + + For example: + + check_compile_time( sizeof( int ) == 4 ); + + Note: This only works with compile-time expressions. + Note: This only works in places where extern declarations are allowed (e.g. global scope). + + References: + + + + + Note: The following macros differ from the macros on the www.jaggersoft.com web site because those versions do not + work with GCC due to GCC allow a zero-length array. Using a -1 condition turned out to be more portable. +*/ + +#define check_compile_time( X ) extern int debug_compile_time_name[ ( X ) ? 1 : -1 ] + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined 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: + + check_compile_time_code( sizeof( int ) == 4 ); + + Note: This only works with compile-time expressions. + Note: This does not work in a global scope so it must be inside a function. + + References: + + + +*/ + +#define check_compile_time_code( X ) switch( 0 ) { case 0: case X:; } + +#if 0 +#pragma mark == check macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check + + @abstract Check that an expression is true (non-zero). + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method. + + Code inside check() statements is not compiled into production builds. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check +#endif +#if( !defined( check ) ) + #if( DEBUG ) + #define check( X ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + } while( 0 ) + #else + #define check( X ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_string + + @abstract Check that an expression is true (non-zero) with an explanation. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method. + + Code inside check_string() statements is not compiled into production builds. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check_string +#endif +#if( !defined( check_string ) ) + #if( DEBUG ) + #define check_string( X, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_string( X, STR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_noerr + + @abstract Check that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method. + + Code inside check_noerr() statements is not compiled into production builds. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check_noerr +#endif +#if( !defined( check_noerr ) ) + #if( DEBUG ) + #define check_noerr( ERR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_noerr( ERR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_noerr_string + + @abstract Check that an error code is noErr (0) with an explanation. + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method. + + Code inside check_noerr_string() statements is not compiled into production builds. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef check_noerr_string +#endif +#if( !defined( check_noerr_string ) ) + #if( DEBUG ) + #define check_noerr_string( ERR, STR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_noerr_string( ERR, STR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_translated_errno + + @abstract Check a condition and prints errno (if non-zero) to the log. + + @discussion + + Code inside check_translated_errno() statements is not compiled into production builds. +*/ + +#if( !defined( check_translated_errno ) ) + #if( DEBUG ) + #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) \ + do \ + { \ + if( !( TEST ) ) \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERRNO ); \ + localErr = ( localErr != 0 ) ? localErr : (int_least32_t)( ALTERNATE_ERROR ); \ + debug_print_assert( localErr, #TEST, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + } \ + \ + } while( 0 ) + #else + #define check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined check_ptr_overlap + + @abstract Checks that two ptrs do not overlap. +*/ + +#define check_ptr_overlap( P1, P1_SIZE, P2, P2_SIZE ) \ + do \ + { \ + check( !( ( (uintptr_t)( P1 ) >= (uintptr_t)( P2 ) ) && \ + ( (uintptr_t)( P1 ) < ( ( (uintptr_t)( P2 ) ) + ( P2_SIZE ) ) ) ) ); \ + check( !( ( (uintptr_t)( P2 ) >= (uintptr_t)( P1 ) ) && \ + ( (uintptr_t)( P2 ) < ( ( (uintptr_t)( P1 ) ) + ( P1_SIZE ) ) ) ) ); \ + \ + } while( 0 ) + +#if 0 +#pragma mark == require macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require + + @abstract Requires that an expression evaluate to true. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then jumps to a label. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require +#endif +#if( !defined( require ) ) + #define require( X, LABEL ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_string + + @abstract Requires that an expression evaluate to true with an explanation. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method then jumps to a label. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_string +#endif +#if( !defined( require_string ) ) + #define require_string( X, LABEL, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_quiet + + @abstract Requires that an expression evaluate to true. + + @discussion + + If expression evalulates to false, this jumps to a label. No debugging information is printed. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_quiet +#endif +#if( !defined( require_quiet ) ) + #define require_quiet( X, LABEL ) \ + do \ + { \ + if( !( X ) ) \ + { \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then jumps to a label. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr +#endif +#if( !defined( require_noerr ) ) + #define require_noerr( ERR, LABEL ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_string + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.), and a custom explanation string using the default debugging output method using the + default debugging output method then jumps to a label. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_string +#endif +#if( !defined( require_noerr_string ) ) + #define require_noerr_string( ERR, LABEL, STR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_action_string + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.), and a custom explanation string using the default debugging output method using the + default debugging output method then executes an action and jumps to a label. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_action_string +#endif +#if( !defined( require_noerr_action_string ) ) + #define require_noerr_action_string( ERR, LABEL, ACTION, STR ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_quiet + + @abstract Require that an error code is noErr (0). + + @discussion + + If the error code is non-0, this jumps to a label. No debugging information is printed. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_quiet +#endif +#if( !defined( require_noerr_quiet ) ) + #define require_noerr_quiet( ERR, LABEL ) \ + do \ + { \ + if( ( ERR ) != 0 ) \ + { \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_action + + @abstract Require that an error code is noErr (0) with an action to execute otherwise. + + @discussion + + If the error code is non-0, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then executes an action and jumps to a label. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_action +#endif +#if( !defined( require_noerr_action ) ) + #define require_noerr_action( ERR, LABEL, ACTION ) \ + do \ + { \ + int_least32_t localErr; \ + \ + localErr = (int_least32_t)( ERR ); \ + if( localErr != 0 ) \ + { \ + debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_noerr_action_quiet + + @abstract Require that an error code is noErr (0) with an action to execute otherwise. + + @discussion + + If the error code is non-0, this executes an action and jumps to a label. No debugging information is printed. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_noerr_action_quiet +#endif +#if( !defined( require_noerr_action_quiet ) ) + #define require_noerr_action_quiet( ERR, LABEL, ACTION ) \ + do \ + { \ + if( ( ERR ) != 0 ) \ + { \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_action + + @abstract Requires that an expression evaluate to true with an action to execute otherwise. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) using the default debugging output method then executes an action and jumps to a label. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_action +#endif +#if( !defined( require_action ) ) + #define require_action( X, LABEL, ACTION ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_action_quiet + + @abstract Requires that an expression evaluate to true with an action to execute otherwise. + + @discussion + + If expression evalulates to false, this executes an action and jumps to a label. No debugging information is printed. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_action_quiet +#endif +#if( !defined( require_action_quiet ) ) + #define require_action_quiet( X, LABEL, ACTION ) \ + do \ + { \ + if( !( X ) ) \ + { \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_action_string + + @abstract Requires that an expression evaluate to true with an explanation and action to execute otherwise. + + @discussion + + If expression evalulates to false, this prints debugging information (actual expression string, file, line number, + function name, etc.) and a custom explanation string using the default debugging output method then executes an + action and jumps to a label. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef require_action_string +#endif +#if( !defined( require_action_string ) ) + #define require_action_string( X, LABEL, ACTION, STR ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + { ACTION; } \ + goto LABEL; \ + } \ + \ + } while( 0 ) + +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined require_throw + + @abstract Requires that an expression evaluates to true or an exception is thrown. + + @discussion + + If the expression evaluates to false, this prints debugging information (actual expression string, file, + line number, function name, etc.) using the default debugging output method then throws an exception. +*/ + +#if( defined( __cplusplus ) ) + #define require_throw( X ) \ + do \ + { \ + if( !( X ) ) \ + { \ + debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ ); \ + throw kUnknownErr; \ + } \ + \ + } while( 0 ) +#endif + +#if 0 +#pragma mark == Design-By-Contract macros == +#endif + +//=========================================================================================================================== +// Design-By-Contract macros +//=========================================================================================================================== + +#define ensure( X ) check( X ) +#define ensure_string( X, STR ) check_string( X, STR ) +#define ensure_noerr( ERR ) check_noerr( ERR ) +#define ensure_noerr_string( ERR, STR ) check_noerr_string( ERR, STR ) +#define ensure_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR ) + +// Note: Design-By-Contract "require" macros are already defined elsewhere. + +#if 0 +#pragma mark == Expect macros == +#endif + +//=========================================================================================================================== +// Expect macros +//=========================================================================================================================== + +// Expect macros allow code to include runtime checking of things that should not happen in shipping code (e.g. internal +// programmer errors, such as a NULL parameter where it is not allowed). Once the code has been verified to work correctly +// without asserting, the DEBUG_EXPECT_VERIFIED conditional can be set to eliminate the error checking entirely. It can +// also be useful to measure the cost of error checking code by profiling with it enable and with it disabled. + +#if( DEBUG_EXPECT_VERIFIED ) + #define require_expect + #define require_string_expect + #define require_quiet_expect + #define require_noerr_expect + #define require_noerr_string_expect + #define require_noerr_action_string_expect + #define require_noerr_quiet_expect + #define require_noerr_action_expect + #define require_noerr_action_quiet_expect + #define require_action_expect + #define require_action_quiet_expect + #define require_action_string_expect +#else + #define require_expect require + #define require_string_expect require_string + #define require_quiet_expect require_quiet + #define require_noerr_expect require_noerr + #define require_noerr_string_expect require_noerr_string + #define require_noerr_action_string_expect require_noerr_action_string + #define require_noerr_quiet_expect require_noerr_quiet + #define require_noerr_action_expect require_noerr_action + #define require_noerr_action_quiet_expect require_noerr_action_quiet + #define require_action_expect require_action + #define require_action_quiet_expect require_action_quiet + #define require_action_string_expect require_action_string +#endif + +#if 0 +#pragma mark == Output macros == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined debug_string + + @abstract Prints a debugging C string. +*/ + +#if( DEBUG_OVERRIDE_APPLE_MACROS ) + #undef debug_string +#endif +#if( !defined( debug_string ) ) + #if( DEBUG ) + #define debug_string( STR ) \ + do \ + { \ + debug_print_assert( 0, NULL, STR, __FILE__, __LINE__, __ROUTINE__ ); \ + \ + } while( 0 ) + #else + #define debug_string( STR ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined debug_print_assert + + @abstract Prints an assertion. +*/ + +#if( DEBUG ) + #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) \ + DebugPrintAssert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) +#else + #define debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined dlog + + @abstract Prints a debug-only message. +*/ + +#if( DEBUG ) + #if( DEBUG_C99_VA_ARGS ) + #define dlog( ... ) DebugPrintF( __VA_ARGS__ ) + #elif( DEBUG_GNU_VA_ARGS ) + #define dlog( ARGS... ) DebugPrintF( ## ARGS ) + #else + #define dlog DebugPrintF + #endif +#else + #if( DEBUG_C99_VA_ARGS ) + #define dlog( ... ) + #elif( DEBUG_GNU_VA_ARGS ) + #define dlog( ARGS... ) + #else + #define dlog while( 0 ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined dlogv + + @abstract Prints a debug-only message. +*/ + +#if( DEBUG ) + #define dlogv( LEVEL, FORMAT, LIST ) DebugPrintFVAList( ( LEVEL ), ( FORMAT ), ( LIST ) ) +#else + #define dlogv( LEVEL, FORMAT, LIST ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined dlogmem + + @abstract Prints a debug-only dump of memory. +*/ + +#if( DEBUG ) + #define dlogmem( LEVEL, PTR, SIZE ) \ + DebugHexDump( ( LEVEL ), 0, NULL, 0, 0, NULL, 0, ( PTR ), ( PTR ), ( SIZE ), kDebugFlagsNone, NULL, 0 ) +#else + #define dlogmem( LEVEL, PTR, SIZE ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DebugNSLog + + @abstract Debug-only macro for the Cocoa NSLog function. +*/ + +#if( DEBUG ) + #if( DEBUG_C99_VA_ARGS ) + #define DebugNSLog( ... ) NSLog( __VA_ARGS__ ) + #elif( DEBUG_GNU_VA_ARGS ) + #define DebugNSLog( ARGS... ) NSLog( ## ARGS ) + #else + #define DebugNSLog NSLog + #endif +#else + #if( DEBUG_C99_VA_ARGS ) + #define DebugNSLog( ... ) + #elif( DEBUG_GNU_VA_ARGS ) + #define DebugNSLog( ARGS... ) + #else + #define DebugNSLog while( 0 ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined DebugLogMsg + + @abstract Debug-only macro for the VxWorks logMsg function. +*/ + +#if( TARGET_OS_VXWORKS ) + #if( DEBUG ) + #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) \ + do \ + { \ + if( ( inLevel >= gDebugPrintLevelMin ) || ( inLevel <= gDebugPrintLevelMax ) ) \ + { \ + logMsg( ( FORMAT ), ( P1 ), ( P2 ), ( P3 ), ( P4 ), ( P5 ), ( P6 ) ); \ + } \ + \ + } while( 0 ) + #else + #define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 ) + #endif +#else + #define DebugLogMsg dlog +#endif + +#if 0 +#pragma mark == Routines - General == +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugInitialize + + @abstract Initializes the debugging library for a specific kind of output. + + @param inType + @param varArg Variable number parameters, controlled by the "inType" parameter. +*/ + +#if( DEBUG ) + DEBUG_EXPORT OSStatus DebugInitialize( DebugOutputType inType, ... ); +#endif + +#if( DEBUG ) + #if( DEBUG_C99_VA_ARGS ) + #define debug_initialize( ... ) DebugInitialize( __VA_ARGS__ ) + #elif( DEBUG_GNU_VA_ARGS ) + #define debug_initialize( ARGS... ) DebugInitialize( ## ARGS ) + #else + #define debug_initialize DebugInitialize + #endif +#else + #if( DEBUG_C99_VA_ARGS ) + #define debug_initialize( ... ) + #elif( DEBUG_GNU_VA_ARGS ) + #define debug_initialize( ARGS... ) + #else + #define debug_initialize while( 0 ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugFinalize + + @abstract Releases any resources used by the debugging library +*/ + +#if( DEBUG ) + DEBUG_EXPORT void DebugFinalize( void ); +#endif + +#if( DEBUG ) + #define debug_terminate() DebugFinalize() +#else + #define debug_terminate() +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugGetProperty + + @abstract Gets the specified property from the debugging library. +*/ + +#if( DEBUG ) + DEBUG_EXPORT OSStatus DebugGetProperty( DebugPropertyTag inTag, ... ); +#endif + +#if( DEBUG ) + #if( DEBUG_C99_VA_ARGS ) + #define debug_get_property( ... ) DebugGetProperty( __VA_ARGS__ ) + #elif( DEBUG_GNU_VA_ARGS ) + #define debug_get_property( ARGS... ) DebugGetProperty( ## ARGS ) + #else + #define debug_get_property DebugGetProperty + #endif +#else + #if( DEBUG_C99_VA_ARGS ) + #define debug_get_property( ... ) + #elif( DEBUG_GNU_VA_ARGS ) + #define debug_get_property( ARGS... ) + #else + #define debug_get_property while( 0 ) + #endif +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugSetProperty + + @abstract Sets the specified property from the debugging library. +*/ + +#if( DEBUG ) + DEBUG_EXPORT OSStatus DebugSetProperty( DebugPropertyTag inTag, ... ); +#endif + +#if( DEBUG ) + #if( DEBUG_C99_VA_ARGS ) + #define debug_set_property( ... ) DebugSetProperty( __VA_ARGS__ ) + #elif( DEBUG_GNU_VA_ARGS ) + #define debug_set_property( ARGS... ) DebugSetProperty( ## ARGS ) + #else + #define debug_set_property DebugSetProperty + #endif +#else + #if( DEBUG_C99_VA_ARGS ) + #define debug_set_property( ... ) + #elif( DEBUG_GNU_VA_ARGS ) + #define debug_set_property( ARGS... ) + #else + #define debug_set_property while( 0 ) + #endif +#endif + +#if 0 +#pragma mark == Routines - Debugging Output == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugPrintF + + @abstract Prints a debug message with printf-style formatting. + + @param inLevel Error that generated this assert or noErr. + + @param inFormatString + C string containing assertion text. + + @param VAR_ARG + Variable number of arguments depending on the format string. + + @result Number of bytes printed or -1 on error. +*/ + +#if( DEBUG ) + DEBUG_EXPORT size_t DebugPrintF( DebugLevel inLevel, const char *inFormat, ... ); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugPrintFVAList + + @abstract va_list version of DebugPrintF. See DebugPrintF for more info. +*/ + +#if( DEBUG ) + DEBUG_EXPORT size_t DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs ); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugPrintAssert + + @abstract Prints a message describing the reason the (e.g. an assert failed), an optional error message, + an optional source filename, an optional source line number. + + @param inErrorCode Error that generated this assert or noErr. + @param inAssertString C string containing assertion text. + @param inMessage C string containing a message about the assert. + @param inFileName C string containing path of file where the error occurred. + @param inLineNumber Line number in source file where the error occurred. + @param inFunction C string containing name of function where assert occurred. + + @discussion + + Example output: + + [ASSERT] assert: "dataPtr != NULL" allocate memory for object failed + [ASSERT] where: "MyFile.c", line 123, ("MyFunction") + + OR + + [ASSERT] error: -6728 (kNoMemoryErr) + [ASSERT] where: "MyFile.c", line 123, ("MyFunction") +*/ + +#if( DEBUG ) + DEBUG_EXPORT void + DebugPrintAssert( + int_least32_t inErrorCode, + const char * inAssertString, + const char * inMessage, + const char * inFilename, + int_least32_t inLineNumber, + const char * inFunction ); +#endif + +#if 0 +#pragma mark == Routines - Utilities == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugSNPrintF + + @abstract Debugging versions of standard C snprintf with extra features. + + @param sbuffer Buffer to receive result. Null terminated unless the buffer size is 0. + @param buflen Size of the buffer including space for the null terminator. + @param fmt printf-style format string. + @param VAR_ARG Variable number of arguments depending on the format string. + + @result Number of characters written (minus the null terminator). + + @discussion + + Extra features over the standard C snprintf: +

+		64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+		%@   - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+		%a   - Network Address: %.4a=IPv4, %.6a=Ethernet, %.8a Fibre Channel, %.16a=IPv6. Arg=ptr to network address.
+		%#a  - IPv4 or IPv6 mDNSAddr. Arg=ptr to mDNSAddr.
+		%##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+		%b   - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+		%C   - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+		%H   - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+		%#H  - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+		%m   - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code arg=the same as %d, %x, etc.
+		%#s  - Pascal-style length-prefixed string. Arg=ptr to string.
+		%##s - DNS label-sequence name. Arg=ptr to name.
+		%S   - UTF-16 string, 0x0000 terminated. Host order if no BOM. Precision is UTF-16 count. Precision includes BOM.
+		%#S  - Big Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+		%##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+		%U   - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+	
+*/ + +#if( DEBUG ) + DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugSNPrintFVAList + + @abstract va_list version of DebugSNPrintF. See DebugSNPrintF for more info. +*/ + +#if( DEBUG ) + DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugGetErrorString + + @abstract Gets an error string from an error code. + + @param inStatus Error code to get the string for. + @param inBuffer Optional buffer to copy the string to for non-static strings. May be null. + @param inBufferSize Size of optional buffer. May be 0. + + @result C string containing error string for the error code. Guaranteed to be a valid, static string. If a + buffer is supplied, the return value will always be a pointer to the supplied buffer, which will + contain the best available description of the error code. If a buffer is not supplied, the return + value will be the best available description of the error code that can be represented as a static + string. This allows code that cannot use a temporary buffer to hold the result to still get a useful + error string in most cases, but also allows code that can use a temporary buffer to get the best + available description. +*/ + +#if( DEBUG ) + DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize ); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugHexDump + + @abstract Hex dumps data to a string or to the output device. +*/ + +#if( DEBUG ) + DEBUG_EXPORT size_t + DebugHexDump( + DebugLevel inLevel, + int inIndent, + const char * inLabel, + size_t inLabelSize, + int inLabelMinWidth, + const char * inType, + size_t inTypeSize, + const void * inDataStart, + const void * inData, + size_t inDataSize, + DebugFlags inFlags, + char * outBuffer, + size_t inBufferSize ); +#endif + +#if( DEBUG ) + #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) \ + DebugHexDump( ( LEVEL ), (INDENT), ( LABEL ), ( LABEL_SIZE ), ( LABEL_MIN_SIZE ), ( TYPE ), ( TYPE_SIZE ), \ + ( DATA_START ), ( DATA ), ( DATA_SIZE ), ( FLAGS ), ( BUFFER ), ( BUFFER_SIZE ) ) +#else + #define dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE ) +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugTaskLevel + + @abstract Returns the current task level. + + @result Current task level + + @discussion + + Bit masks to isolate portions of the result (note that some masks may also need bit shifts to right justify): +
+		kDebugInterruptLevelMask				- Indicates the current interrupt level (> 0 means interrupt time).
+		kDebugInVBLTaskMask						- Indicates if a VBL task is currently being executed.
+		kDebugInDeferredTaskMask				- Indicates if a Deferred Task is currently being executed.
+		kDebugInSecondaryInterruptHandlerMask	- Indicates if a Secondary Interrupt Handler is currently being executed.
+		kDebugPageFaultFatalMask				- Indicates if it is unsafe to cause a page fault (worse than interrupt time).
+		kDebugMPTaskLevelMask					- Indicates if being called from an MP task.
+		kDebugInterruptDepthMask				- 0 means task level, 1 means in interrupt, > 1 means in nested interrupt.
+	
+ + Helpers: +
+		DebugExtractTaskLevelInterruptDepth()   - Macro to extract interrupt depth from task level value.
+	
+*/ + +#if( DEBUG ) + DEBUG_EXPORT uint32_t DebugTaskLevel( void ); +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DebugServicesTest + + @abstract Unit test. +*/ + +#if( DEBUG ) + DEBUG_EXPORT OSStatus DebugServicesTest( void ); +#endif + +#ifdef __cplusplus + } +#endif + +#endif // __DEBUG_SERVICES__ diff --git a/mDNSWindows/Installer.vct b/mDNSWindows/Installer.vct new file mode 100644 index 0000000000000000000000000000000000000000..c31317d785551748d31e184eb5b2c6e6330568f4 GIT binary patch literal 40028 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

QQoOuHy#!M_?7)m1Ppl<{ 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 a8`m#iyUJzHny56S%2bP5qco}b^Zx)F5s_8^ literal 0 HcmV?d00001 diff --git a/mDNSWindows/README.txt b/mDNSWindows/README.txt index 52c48b1..5a7b375 100644 --- a/mDNSWindows/README.txt +++ b/mDNSWindows/README.txt @@ -1,19 +1,59 @@ This directory contains support files for running mDNS on Microsoft Windows -and Windows CE/PocketPC. +and Windows CE/PocketPC. Building this code requires the Windows SDK 2003 +or later. The CodeWarrior builds require CodeWarrior 8 or later and if using +CodeWarrior 8, the newer Windows headers from the Mac CodeWarrior 9 need to +be used. -mDNSWin32.c & mDNSWin32.h are the Platform Support files that go below -mDNS Core. These work on both Windows and Windows CE/PocketPC. +mDNSWin32.c/.h + + 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. +DNSSD.c/.h -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. + High-level implementation of the DNS-SD API. This supports both "direct" + (compiled-in mDNSCore) and "client" (IPC to service) usage. Conditionals + can exclude either "direct" or "client" to reduce code size. + +DNSSDDirect.c/.h + + Portable implementation of the DNS-SD API. This interacts with mDNSCore + to perform all the real work of the DNS-SD API. This code does not rely + on any platform-specifics so it should run on any platform with an mDNS + platform plugin available. Software that cannot or does not want to use + the IPC mechanism (e.g. Windows CE, VxWorks, etc.) can use this code + directly without any of the IPC pieces. + +RMxClient.c/.h + + Client-side implementation of the DNS-SD IPC API. This handles sending + and receiving messages from the service to perform DNS-SD operations + and get DNS-SD responses. + +RMxCommon.c/.h + + Common code between the RMxClient and RMxServer. This handles establishing + and accepting connections, the underying message sending and receiving, + portable data packing and unpacking, and shared utility routines. + +RMxServer.c/.h + + Server-side implementation of the DNS-SD IPC API. This listens for + and accepts connections from IPC clients, starts server sessions, and + acts as a mediator between the "direct" (compiled-in mDNSCore) code + and the IPC client. + +DNSServices is an obsolete higher-level API for using mDNS. New code should +use the DNS-SD APIs. + +DNSServiceDiscovery is an obsolete emulation layer that sits on top of +DNSServices and provides the Mac OS X DNS Service Discovery API's on any +platform. New code should use the DNS-SD APIs. 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 +ToolWin32.vcproj is a Visual Studio .NET 7 project. These projects build 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: @@ -42,3 +82,4 @@ 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/RMxClient.c b/mDNSWindows/RMxClient.c new file mode 100644 index 0000000..540d4ef --- /dev/null +++ b/mDNSWindows/RMxClient.c @@ -0,0 +1,1302 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: RMxClient.c,v $ +Revision 1.3 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.2 2004/04/09 21:03:14 bradley +Changed port numbers to use network byte order for consistency with other platforms. + +Revision 1.1 2004/01/30 02:35:13 bradley +Rendezvous Message Exchange implementation for DNS-SD IPC on Windows. + +*/ + +#include +#include +#include + +#include "CommonServices.h" +#include "DebugServices.h" + +#include "DNSSD.h" +#include "RMxCommon.h" + +#include "RMxClient.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[RMxClient] " + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +typedef void ( *DNSServiceRefReleaseCallBack )( DNSServiceRef inRef ); + +// DNSServiceRef + +typedef struct _DNSServiceRef_t _DNSServiceRef_t; +struct _DNSServiceRef_t +{ + RMxSessionRef session; + void * context; + DNSRecordRef records; + + union + { + struct // EnumerateDomains + { + DNSServiceDomainEnumReply callback; + + } domain; + + struct // Register + { + DNSServiceRegisterReply callback; + uint32_t lastID; + + } reg; + + struct // Browse + { + DNSServiceBrowseReply callback; + + } browse; + + struct // Resolve + { + DNSServiceFlags flags; + DNSServiceResolveReply callback; + + } resolve; + + struct // CreateConnection + { + DNSServiceRegisterRecordReply callback; + uint32_t lastID; + + } connection; + + struct // QueryRecord + { + DNSServiceQueryRecordReply callback; + + } query; + + } u; +}; + +// DNSRecordRef + +typedef struct _DNSRecordRef_t _DNSRecordRef_t; +struct _DNSRecordRef_t +{ + DNSRecordRef next; + uint32_t id; + DNSServiceRegisterRecordReply callback; + void * context; +}; + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// RMx + +DEBUG_LOCAL void RMxClientMessageCallBack( RMxMessage *inMessage ); + +// Properties + +DEBUG_LOCAL DNSServiceErrorType + DNSServiceCopyPropertyDecodeData_client( + DNSPropertyCode inCode, + const void * inData, + size_t inSize, + DNSPropertyData * outData ); + +// Domain Enumeration + +DEBUG_LOCAL void DNSServiceEnumerateDomainsReply_client( RMxMessage *inMessage ); + +// Service Discovery + +DEBUG_LOCAL void DNSServiceBrowseReply_client( RMxMessage *inMessage ); + +DEBUG_LOCAL void DNSServiceResolveReply_client( RMxMessage *inMessage ); + +// Service Registration + +DEBUG_LOCAL void DNSServiceRegisterReply_client( RMxMessage *inMessage ); + +// Special Purpose + +DEBUG_LOCAL DNSRecordRef DNSServiceConnectionRecordRemove_client( DNSServiceRef inRef, DNSRecordRef inRecordRef ); +DEBUG_LOCAL void DNSServiceRegisterRecordReply_client( RMxMessage *inMessage ); +DEBUG_LOCAL void DNSServiceQueryRecordReply_client( RMxMessage *inMessage ); + +#if 0 +#pragma mark - +#pragma mark == RMx == +#endif + +//=========================================================================================================================== +// RMxClientInitialize +//=========================================================================================================================== + +OSStatus RMxClientInitialize( void ) +{ + OSStatus err; + + // Initialize the lower-level layer and indicate it is running. + + err = RMxInitialize(); + require_noerr( err, exit ); + + gRMxState = kRMxStateRun; + +exit: + if( err != kNoErr ) + { + RMxClientFinalize(); + } + return( err ); +} + +//=========================================================================================================================== +// RMxClientFinalize +//=========================================================================================================================== + +void RMxClientFinalize( void ) +{ + RMxFinalize(); +} + +//=========================================================================================================================== +// RMxClientMessageCallBack +//=========================================================================================================================== + +DEBUG_LOCAL void RMxClientMessageCallBack( RMxMessage *inMessage ) +{ + check( inMessage ); + + switch( inMessage->opcode ) + { + case kRMxOpCodeInvalid: + // The session is closing. We don't delete the DNS-SD object here because the client deletes DNS-SD objects. + break; + + case kRMxOpCodeEnumerateDomains: + DNSServiceEnumerateDomainsReply_client( inMessage ); + break; + + case kRMxOpCodeRegister: + DNSServiceRegisterReply_client( inMessage ); + break; + + case kRMxOpCodeBrowse: + DNSServiceBrowseReply_client( inMessage ); + break; + + case kRMxOpCodeResolve: + DNSServiceResolveReply_client( inMessage ); + break; + + case kRMxOpCodeRegisterRecord: + DNSServiceRegisterRecordReply_client( inMessage ); + break; + + case kRMxOpCodeQueryRecord: + DNSServiceQueryRecordReply_client( inMessage ); + break; + + default: + dlog( kDebugLevelWarning, DEBUG_NAME "message with unknown opcode received (%d)\n", inMessage->opcode ); + break; + } +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD General == +#endif + +//=========================================================================================================================== +// DNSServiceRefDeallocate_client +//=========================================================================================================================== + +void DNSServiceRefDeallocate_client( DNSServiceRef inRef ) +{ + OSStatus err; + + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "Deallocate ref=%#p\n", inRef ); + require_action( inRef, exit, err = kDNSServiceErr_BadReference ); + + // Close the session (if not already closed). + + if( inRef->session ) + { + inRef->session->callback = NULL; + inRef->session->message.context = NULL; + + err = RMxSessionClose( inRef->session, kEndingErr ); + check_noerr( err ); + } + + // Release any outstanding individual records. + + while( inRef->records ) + { + DNSRecordRef record; + + record = inRef->records; + inRef->records = record->next; + + free( record ); + } + + // Release the object itself. + + free( inRef ); + +exit: + return; +} + +//=========================================================================================================================== +// DNSServiceCheckVersion_client +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceCheckVersion_client( const char *inServer ) +{ + DNSServiceErrorType err; + RMxSessionRef session; + OSStatus errorCode; + uint32_t serverCurrentVersion; + uint32_t serverOldestClientVersion; + uint32_t serverOldestServerVersion; + + session = NULL; + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "CheckVersion\n" ); + + // Open a session to the server and send the request. Specify no thread since we are going to manually read the message. + + err = RMxSessionOpen( inServer, kRMxSessionFlagsNoThread, kInvalidSocketRef, NULL, NULL, &session, + kRMxOpCodeCheckVersion, "www", kRMxCurrentVersion, kRMxOldestClientVersion, kRMxOldestServerVersion ); + require_noerr( err, exit ); + + // Receive the respons, parse it, and check the versions. + + err = RMxSessionRecvMessage( session, kRMxClientTimeout ); + require_noerr( err, exit ); + check( session->message.recvData || ( session->message.recvSize == 0 ) ); + + err = RMxUnpack( session->message.recvData, session->message.recvSize, "wwww", + &errorCode, &serverCurrentVersion, &serverOldestClientVersion, &serverOldestServerVersion ); + require_noerr( err, exit ); + + err = RMxCheckVersion( kRMxCurrentVersion, kRMxOldestClientVersion, kRMxOldestServerVersion, + serverCurrentVersion, serverOldestClientVersion, serverOldestServerVersion ); + check( err == errorCode ); + if( ( err == kNoErr ) && ( errorCode != kNoErr ) ) + { + dlog( kDebugLevelWarning, DEBUG_NAME "client/server disagree on versions\n" ); + err = errorCode; + } + +exit: + if( session ) + { + RMxSessionClose( session, kEndingErr ); + } + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Properties == +#endif + +//=========================================================================================================================== +// DNSServiceCopyProperty_client +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceCopyProperty_client( const char *inServer, DNSPropertyCode inCode, DNSPropertyData *outData ) +{ + DNSServiceErrorType err; + RMxSessionRef session; + OSStatus errorCode; + DNSPropertyCode code; + const void * data; + size_t size; + + session = NULL; + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "CopyProperty server=%s, code='%C'\n", inServer ? inServer : "", inCode ); + require_action( outData, exit, err = kDNSServiceErr_BadParam ); + + // Open a session to the server and send the request. Specify no thread since we are going to manually read the message. + + err = RMxSessionOpen( inServer, kRMxSessionFlagsNoThread, kInvalidSocketRef, NULL, NULL, &session, + kRMxOpCodeCopyProperty, "w", inCode ); + require_noerr( err, exit ); + + // Receive the response, parse it, and check the versions. + + err = RMxSessionRecvMessage( session, kRMxClientTimeout ); + require_noerr( err, exit ); + check( session->message.recvData || ( session->message.recvSize == 0 ) ); + + err = RMxUnpack( session->message.recvData, session->message.recvSize, "wwn", &errorCode, &code, &data, &size ); + require_noerr( err, exit ); + err = errorCode; + require_noerr_quiet( err, exit ); + + // Decode the data and fill in the results. + + err = DNSServiceCopyPropertyDecodeData_client( code, data, size, outData ); + require_noerr( err, exit ); + +exit: + if( session ) + { + RMxSessionClose( session, kEndingErr ); + } + return( err ); +} + +//=========================================================================================================================== +// DNSServiceCopyProperty_client +//=========================================================================================================================== + +DEBUG_LOCAL DNSServiceErrorType + DNSServiceCopyPropertyDecodeData_client( + DNSPropertyCode inCode, + const void * inData, + size_t inSize, + DNSPropertyData * outData ) +{ + OSStatus err; + + check( outData ); + + switch( inCode ) + { + case kDNSPropertyCodeVersion: + outData->u.version.clientCurrentVersion = kRMxCurrentVersion; + outData->u.version.clientOldestServerVersion = kRMxOldestServerVersion; + + err = RMxUnpack( inData, inSize, "www", + &outData->u.version.serverCurrentVersion, &outData->u.version.serverOldestClientVersion, NULL ); + require_noerr( err, exit ); + break; + + default: + dlog( kDebugLevelError, DEBUG_NAME "CopyPropertyDecodeData unknown property code (%C)\n", inCode ); + err = kDNSServiceErr_Unsupported; + goto exit; + } + err = kDNSServiceErr_NoError; + +exit: + return( err ); +} + +//=========================================================================================================================== +// DNSServiceReleaseProperty_client +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceReleaseProperty_client( DNSPropertyData *inData ) +{ + OSStatus err; + + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "ReleaseProperty\n" ); + require_action( inData, exit, err = kDNSServiceErr_BadParam ); + + switch( inData->code ) + { + case kDNSPropertyCodeVersion: + // Data is embedded directly in the structure so there is nothing to release. + break; + + default: + dlog( kDebugLevelError, DEBUG_NAME "ReleaseProperty unknown property code (%C)\n", inData->code ); + err = kDNSServiceErr_Unsupported; + goto exit; + } + err = kDNSServiceErr_NoError; + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Domain Enumeration == +#endif + +//=========================================================================================================================== +// DNSServiceEnumerateDomains_client +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceEnumerateDomains_client( + DNSServiceRef * outRef, + const char * inServer, + const DNSServiceFlags inFlags, + const uint32_t inInterfaceIndex, + const DNSServiceDomainEnumReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + + obj = NULL; + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "EnumerateDomains flags=0x%08X, ifi=%d, server=%s\n", + inFlags, inInterfaceIndex, inServer ? inServer : "" ); + require_action( outRef, exit, err = kDNSServiceErr_BadParam ); + require_action( ( inFlags == kDNSServiceFlagsBrowseDomains ) || + ( inFlags == kDNSServiceFlagsRegistrationDomains ), + exit, err = kDNSServiceErr_BadFlags ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadParam ); + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->context = inContext; + obj->u.domain.callback = inCallBack; + + // Open a session to the server and send the request. + + err = RMxSessionOpen( inServer, kRMxSessionFlagsNoClose, kInvalidSocketRef, RMxClientMessageCallBack, obj, + &obj->session, kRMxOpCodeEnumerateDomains, "ww", inFlags, inInterfaceIndex ); + require_noerr( err, exit ); + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_client( obj ); + } + return( err ); +} + +//=========================================================================================================================== +// DNSServiceEnumerateDomainsReply_client +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceEnumerateDomainsReply_client( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + const char * domain; + + check( inMessage ); + obj = (DNSServiceRef) inMessage->context; + check( obj ); + + err = inMessage->status; + if( err == kNoErr ) + { + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwws", &flags, &interfaceIndex, &errorCode, &domain, NULL ); + check_noerr( err ); + if( err == kNoErr ) + { + dlog( kDebugLevelTrace, DEBUG_NAME "EnumerateDomains reply flags=0x%08X, ifi=%d, err=%d, domain=\"%s\"\n", + flags, interfaceIndex, errorCode, domain ); + + obj->u.domain.callback( obj, flags, interfaceIndex, errorCode, domain, obj->context ); + } + } + if( err != kNoErr ) + { + obj->u.domain.callback( obj, 0, 0, err, "", obj->context ); + } +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Service Registration == +#endif + +//=========================================================================================================================== +// DNSServiceRegister_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceRegister_client( + DNSServiceRef * outRef, + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + const char * inHost, + uint16_t inPort, + uint16_t inTXTSize, + const void * inTXT, + DNSServiceRegisterReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + + obj = NULL; + dlog( kDebugLevelTrace, "\n" DEBUG_NAME + "Resolve flags=0x%08X, ifi=%d, name=\"%s\", type=\"%s\", domain=\"%s\", host=\"%s\", port=%d, txtSize=%d\n", + inFlags, inInterfaceIndex, inName ? inName : "", inType, inDomain ? inDomain : "", inHost, + ntohs( inPort ), inTXTSize ); + require_action( outRef, exit, err = kDNSServiceErr_BadReference ); + require_action( ( inFlags == 0 ) || ( inFlags == kDNSServiceFlagsNoAutoRename ), exit, err = kDNSServiceErr_BadFlags ); + require_action( inType, exit, err = kDNSServiceErr_BadParam ); + require_action( inTXT || ( inTXTSize == 0 ), exit, err = kDNSServiceErr_BadParam ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadParam ); + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->context = inContext; + obj->u.reg.callback = inCallBack; + + // Open a session to the server and send the request. + + err = RMxSessionOpen( inServer, kRMxSessionFlagsNoClose, kInvalidSocketRef, RMxClientMessageCallBack, obj, + &obj->session, kRMxOpCodeRegister, "wwsssshn", inFlags, inInterfaceIndex, inName ? inName : "", inType, + inDomain ? inDomain : "", inHost, inPort, inTXTSize, inTXT ); + require_noerr( err, exit ); + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_client( obj ); + } + return( err ); +} + +//=========================================================================================================================== +// DNSServiceRegisterReply_client +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRegisterReply_client( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + DNSServiceErrorType errorCode; + const char * name; + const char * type; + const char * domain; + + check( inMessage ); + obj = (DNSServiceRef) inMessage->context; + check( obj ); + + err = inMessage->status; + if( err == kNoErr ) + { + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwsss", + &flags, &errorCode, &name, NULL, &type, NULL, &domain, NULL ); + check_noerr( err ); + if( err == kNoErr ) + { + dlog( kDebugLevelTrace, DEBUG_NAME "Register reply flags=0x%08X, err=%d, name=\"%s\", type=\"%s\", domain=\"%s\"\n", + flags, errorCode, name, type, domain ); + + obj->u.reg.callback( obj, flags, errorCode, name, type, domain, obj->context ); + } + } + if( err != kNoErr ) + { + obj->u.reg.callback( obj, 0, err, "", "", "", obj->context ); + } +} + +//=========================================================================================================================== +// DNSServiceAddRecord_client +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceAddRecord_client( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint16_t inRRType, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ) +{ + DNSServiceErrorType err; + DNSRecordRef obj; + + obj = NULL; + RMxLock(); + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "AddRecord flags=0x%08X, rrType=%d, rrDataSize=%d, ttl=%d\n", + inFlags, inRRType, inRDataSize, inTTL ); + require_action( inRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inRef->session, exit, err = kDNSServiceErr_NotInitialized ); + require_action( outRecordRef, exit, err = kDNSServiceErr_BadParam ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inRData && ( inRDataSize > 0 ), exit, err = kDNSServiceErr_BadParam ); + + // Allocate and initialize the object. + + obj = (DNSRecordRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + // Send the message. Use an ID that should be unique for the session and avoid the reserved ID. + + obj->id = ++inRef->u.reg.lastID; + if( obj->id == kDNSRecordIndexDefaultTXT ) + { + obj->id = ++inRef->u.reg.lastID; + } + + err = RMxSessionSendMessage( inRef->session, kRMxOpCodeAddRecord, kNoErr, "wwhnw", + obj->id, inFlags, inRRType, inRDataSize, inRData, inTTL ); + require_noerr( err, exit ); + + // Success! + + *outRecordRef = obj; + obj = NULL; + +exit: + if( obj ) + { + free( obj ); + } + RMxUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceUpdateRecord_client +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceUpdateRecord_client( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ) +{ + DNSServiceErrorType err; + + RMxLock(); + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "UpdateRecord flags=0x%08X, rrDataSize=%d, ttl=%d\n", inFlags, inRDataSize, inTTL ); + require_action( inRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inRef->session, exit, err = kDNSServiceErr_NotInitialized ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inRData && ( inRDataSize > 0 ), exit, err = kDNSServiceErr_BadParam ); + + err = RMxSessionSendMessage( inRef->session, kRMxOpCodeUpdateRecord, kNoErr, "wwnw", + inRecordRef ? inRecordRef->id : kDNSRecordIndexDefaultTXT, inFlags, inRDataSize, inRData, inTTL ); + require_noerr( err, exit ); + +exit: + RMxUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceRemoveRecord_client +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceRemoveRecord_client( DNSServiceRef inRef, DNSRecordRef inRecordRef, DNSServiceFlags inFlags ) +{ + DNSServiceErrorType err; + + RMxLock(); + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "RemoveRecord flags=0x%08X\n", inFlags ); + require_action( inRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inRef->session, exit, err = kDNSServiceErr_NotInitialized ); + require_action( inRecordRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + + err = RMxSessionSendMessage( inRef->session, kRMxOpCodeRemoveRecord, kNoErr, "ww", inRecordRef->id, inFlags ); + DNSServiceConnectionRecordRemove_client( inRef, inRecordRef ); + free( inRecordRef ); + require_noerr( err, exit ); + +exit: + RMxUnlock(); + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Service Discovery == +#endif + +//=========================================================================================================================== +// DNSServiceBrowse_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceBrowse_client( + DNSServiceRef * outRef, + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inType, + const char * inDomain, + DNSServiceBrowseReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + + obj = NULL; + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "Browse flags=0x%08X, ifi=%d, type=\"%s\", domain=\"%s\"\n", + inFlags, inInterfaceIndex, inType, inDomain ? inDomain : "" ); + require_action( outRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inType, exit, err = kDNSServiceErr_BadParam ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadParam ); + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->context = inContext; + obj->u.browse.callback = inCallBack; + + // Open a session to the server and send the request. + + err = RMxSessionOpen( inServer, kRMxSessionFlagsNoClose, kInvalidSocketRef, RMxClientMessageCallBack, obj, + &obj->session, kRMxOpCodeBrowse, "wwss", inFlags, inInterfaceIndex, inType, inDomain ? inDomain : "" ); + require_noerr( err, exit ); + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_client( obj ); + } + return( err ); +} + +//=========================================================================================================================== +// DNSServiceBrowseReply_client +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceBrowseReply_client( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + const char * name; + const char * type; + const char * domain; + + check( inMessage ); + obj = (DNSServiceRef) inMessage->context; + check( obj ); + + err = inMessage->status; + if( err == kNoErr ) + { + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwwsss", + &flags, &interfaceIndex, &errorCode, &name, NULL, &type, NULL, &domain, NULL ); + check_noerr( err ); + if( err == kNoErr ) + { + dlog( kDebugLevelTrace, DEBUG_NAME + "Browse reply flags=0x%08X, ifi=%d, err=%d, name=\"%s\", type=\"%s\", domain=\"%s\"\n", + flags, interfaceIndex, errorCode, name, type, domain ); + + obj->u.browse.callback( obj, flags, interfaceIndex, errorCode, name, type, domain, obj->context ); + } + } + if( err != kNoErr ) + { + obj->u.browse.callback( obj, 0, 0, err, "", "", "", obj->context ); + } +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// DNSServiceResolve_direct +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceResolve_client( + DNSServiceRef * outRef, + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + DNSServiceResolveReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + + obj = NULL; + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "Resolve flags=0x%08X, ifi=%d, name=\"%s\", type=\"%s\", domain=\"%s\"\n", + inFlags, inInterfaceIndex, inName, inType, inDomain ? inDomain : "" ); + require_action( outRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inName, exit, err = kDNSServiceErr_BadParam ); + require_action( inType, exit, err = kDNSServiceErr_BadParam ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadParam ); + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->context = inContext; + obj->u.resolve.flags = inFlags; + obj->u.resolve.callback = inCallBack; + + // Open a session to the server and send the request. + + err = RMxSessionOpen( inServer, kRMxSessionFlagsNoClose, kInvalidSocketRef, RMxClientMessageCallBack, obj, + &obj->session, kRMxOpCodeResolve, "wwsss", inFlags, inInterfaceIndex, inName, inType, inDomain ? inDomain : "" ); + require_noerr( err, exit ); + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_client( obj ); + } + return( err ); +} + +//=========================================================================================================================== +// DNSServiceResolveReply_client +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceResolveReply_client( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + const char * name; + const char * host; + uint16_t port; + const char * txt; + size_t txtSize; + + check( inMessage ); + obj = (DNSServiceRef) inMessage->context; + check( obj ); + + err = inMessage->status; + if( err == kNoErr ) + { + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwwsshn", + &flags, &interfaceIndex, &errorCode, &name, NULL, &host, NULL, &port, &txt, &txtSize ); + check_noerr( err ); + if( err == kNoErr ) + { + dlog( kDebugLevelTrace, DEBUG_NAME + "Resolve reply flags=0x%08X, ifi=%d, err=%d, name=\"%s\", host=\"%s\", port=%d, txtSize=%d\n", + flags, interfaceIndex, errorCode, name, host, ntohs( port ), (int) txtSize ); + + obj->u.resolve.callback( obj, flags, interfaceIndex, errorCode, name, host, port, (uint16_t) txtSize, txt, + obj->context ); + } + } + if( err != kNoErr ) + { + obj->u.resolve.callback( obj, 0, 0, err, "", "", 0, 0, NULL, obj->context ); + } +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Special Purpose == +#endif + +//=========================================================================================================================== +// DNSServiceCreateConnection_client +//=========================================================================================================================== + +DNSServiceErrorType DNSServiceCreateConnection_client( DNSServiceRef *outRef, const char *inServer ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + + obj = NULL; + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "CreateConnection (server=\"%s\")\n", inServer ? inServer : "" ); + require_action( outRef, exit, err = kDNSServiceErr_BadReference ); + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + err = RMxSessionOpen( inServer, kRMxSessionFlagsNoClose, kInvalidSocketRef, RMxClientMessageCallBack, obj, + &obj->session, kRMxOpCodeCreateConnection, "" ); + require_noerr( err, exit ); + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_client( obj ); + } + return( err ); +} + +//=========================================================================================================================== +// DNSServiceConnectionRecordRemove_client +// +// Warning: Assumes the RMx lock is held (or is not needed due to a single thread having exclusive access). +//=========================================================================================================================== + +DEBUG_LOCAL DNSRecordRef DNSServiceConnectionRecordRemove_client( DNSServiceRef inRef, DNSRecordRef inRecordRef ) +{ + DNSRecordRef * p; + + for( p = &inRef->records; *p; p = &( *p )->next ) + { + if( *p == inRecordRef ) + { + break; + } + } + inRecordRef = *p; + if( inRecordRef ) + { + *p = inRecordRef->next; + } + return( inRecordRef ); +} + +//=========================================================================================================================== +// DNSServiceRegisterRecord_client +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceRegisterRecord_client( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + DNSServiceRegisterRecordReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSRecordRef obj; + + DEBUG_UNUSED( inContext ); + + obj = NULL; + RMxLock(); + dlog( kDebugLevelTrace, "\n" DEBUG_NAME + "RegisterRecord flags=0x%08X, ifi=%d, name=\"%s\" rrType=0x%04X, rrClass=0x%04X, rDataSize=%d, ttl=%d\n", + inFlags, inInterfaceIndex, inName, inRRType, inRRClass, inRDataSize, inTTL ); + require_action( inRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inRef->session, exit, err = kDNSServiceErr_NotInitialized ); + require_action( outRecordRef, exit, err = kDNSServiceErr_BadParam ); + require_action( ( inFlags == kDNSServiceFlagsShared ) || ( inFlags == kDNSServiceFlagsUnique ), + exit, err = kDNSServiceErr_BadFlags ); + require_action( inName, exit, err = kDNSServiceErr_BadParam ); + require_action( inRData && ( inRDataSize > 0 ), exit, err = kDNSServiceErr_BadParam ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadFlags ); + + // Allocate and initialize the object. + + obj = (DNSRecordRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->callback = inCallBack; + obj->context = inContext; + + // Set up a unique ID for the session, avoiding the reserved ID 0, add the record, then send the message. + + obj->id = ++inRef->u.connection.lastID; + if( obj->id == kDNSRecordIndexDefaultTXT ) + { + obj->id = ++inRef->u.connection.lastID; + } + obj->next = inRef->records; + inRef->records = obj; + + err = RMxSessionSendMessage( inRef->session, kRMxOpCodeRegisterRecord, kNoErr, "wwwshhnw", + obj->id, inFlags, inInterfaceIndex, inName, inRRType, inRRClass, inRDataSize, inRData, inTTL ); + require_noerr( err, exit ); + + // Success! + + *outRecordRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceConnectionRecordRemove_client( inRef, obj ); + free( obj ); + } + RMxUnlock(); + return( err ); +} + +//=========================================================================================================================== +// DNSServiceRegisterRecordReply_client +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRegisterRecordReply_client( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + DNSServiceErrorType errorCode; + uint32_t id; + DNSRecordRef record; + + check( inMessage ); + obj = (DNSServiceRef) inMessage->context; + check( obj ); + + record = NULL; + err = inMessage->status; + if( err == kNoErr ) + { + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "www", &flags, &errorCode, &id ); + check_noerr( err ); + if( err == kNoErr ) + { + RMxLock(); + for( record = obj->records; record; record = record->next ) + { + if( record->id == id ) + { + break; + } + } + RMxUnlock(); + if( !record ) + { + dlog( kDebugLevelError, DEBUG_NAME "RegisterRecord reply with unknown record ID (%d)\n", id ); + err = kNotFoundErr; + } + if( err == kNoErr ) + { + dlog( kDebugLevelTrace, DEBUG_NAME "RegisterRecord reply id=%d, flags=0x%08X, err=%d\n", id, flags, errorCode ); + + record->callback( obj, record, flags, errorCode, record->context ); + } + } + } + if( err != kNoErr ) + { + check( record ); + if( record ) + { + record->callback( obj, NULL, 0, err, NULL ); + } + } +} + +//=========================================================================================================================== +// DNSServiceQueryRecord_client +//=========================================================================================================================== + +DNSServiceErrorType + DNSServiceQueryRecord_client( + DNSServiceRef * outRef, + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + DNSServiceQueryRecordReply inCallBack, + void * inContext ) +{ + DNSServiceErrorType err; + DNSServiceRef obj; + + obj = NULL; + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "QueryRecord flags=0x%08X, ifi=%d, name=\"%s\", rrType=%d, rrClass=%d\n", + inFlags, inInterfaceIndex, inName, inRRType, inRRClass ); + require_action( outRef, exit, err = kDNSServiceErr_BadReference ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inName, exit, err = kDNSServiceErr_BadParam ); + require_action( inCallBack, exit, err = kDNSServiceErr_BadFlags ); + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kDNSServiceErr_NoMemory ); + + obj->context = inContext; + obj->u.query.callback = inCallBack; + + // Open a session to the server and send the request. + + err = RMxSessionOpen( inServer, kRMxSessionFlagsNoClose, kInvalidSocketRef, RMxClientMessageCallBack, obj, + &obj->session, kRMxOpCodeQueryRecord, "wwshh", inFlags, inInterfaceIndex, inName, inRRType, inRRClass ); + require_noerr( err, exit ); + + // Success! + + *outRef = obj; + obj = NULL; + +exit: + if( obj ) + { + DNSServiceRefDeallocate_client( obj ); + } + return( err ); +} + +//=========================================================================================================================== +// DNSServiceQueryRecordReply_client +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceQueryRecordReply_client( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + const char * name; + uint16_t rrType; + uint16_t rrClass; + uint8_t * rData; + size_t rDataSize; + uint32_t ttl; + + check( inMessage ); + obj = (DNSServiceRef) inMessage->context; + check( obj ); + + err = inMessage->status; + if( err == kNoErr ) + { + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwwshhnw", + &flags, &interfaceIndex, &errorCode, &name, NULL, &rrType, &rrClass, &rData, &rDataSize, &ttl ); + check_noerr( err ); + if( err == kNoErr ) + { + dlog( kDebugLevelTrace, DEBUG_NAME + "QueryRecord reply flags=0x%08X, ifi=%d, err=%d, name=\"%s\", rrType=%d, rrClass=%d, rDataSize=%d, ttl=%d\n", + flags, interfaceIndex, errorCode, name, rrType, rrClass, rDataSize, ttl ); + + obj->u.query.callback( obj, flags, interfaceIndex, errorCode, name, rrType, rrClass, (uint16_t) rDataSize, rData, + ttl, obj->context ); + } + } + if( err != kNoErr ) + { + obj->u.query.callback( obj, 0, 0, err, "", 0, 0, 0, NULL, 0, obj->context ); + } +} + +//=========================================================================================================================== +// DNSServiceReconfirmRecord_client +//=========================================================================================================================== + +void + DNSServiceReconfirmRecord_client( + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData ) +{ + DNSServiceErrorType err; + + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "ReconfirmRecord flags=0x%08X, ifi=%d, name=\"%s\", rrType=%d, rrClass=%d\n", + inFlags, inInterfaceIndex, inName, inRRType, inRRClass ); + require_action( inFlags == 0, exit, err = kDNSServiceErr_BadFlags ); + require_action( inName, exit, err = kDNSServiceErr_BadParam ); + require_action( inRData && ( inRDataSize > 0 ), exit, err = kDNSServiceErr_BadParam ); + + err = RMxSessionOpen( inServer, kRMxSessionFlagsNoClose, kInvalidSocketRef, NULL, NULL, NULL, + kRMxOpCodeReconfirmRecord, "wwshhn", inFlags, inInterfaceIndex, inName, inRRType, inRRClass, inRDataSize, inRData ); + require_noerr( err, exit ); + +exit: + return; +} + +#ifdef __cplusplus + } +#endif diff --git a/mDNSWindows/RMxClient.h b/mDNSWindows/RMxClient.h new file mode 100644 index 0000000..798f9e9 --- /dev/null +++ b/mDNSWindows/RMxClient.h @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: RMxClient.h,v $ +Revision 1.1 2004/01/30 02:35:13 bradley +Rendezvous Message Exchange implementation for DNS-SD IPC on Windows. + +*/ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header RMxClient.h + + @abstract Client-side implementation of the DNS-SD IPC API. + + @discussion + + This handles sending and receiving messages from the service to perform DNS-SD operations and get DNS-SD responses. +*/ + +#ifndef __RMx_CLIENT__ +#define __RMx_CLIENT__ + +#include "DNSSD.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == RMx == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxClientInitialize + + @abstract Initializes RMx for client usage. This must be called before any RMx functions are called. +*/ + +OSStatus RMxClientInitialize( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxClientFinalize + + @abstract Finalizes client usage of RMx. No RMx calls should be made after this call is made. +*/ + +void RMxClientFinalize( void ); + +#if 0 +#pragma mark == DNS-SD General == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRefDeallocate_client + + @abstract Client-side version of DNSServiceRefDeallocate. +*/ + +void DNSServiceRefDeallocate_client( DNSServiceRef inRef ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceCheckVersion_client + + @abstract Client-side version of DNSServiceCheckVersion. +*/ + +DNSServiceErrorType DNSServiceCheckVersion_client( const char *inServer ); + +#if 0 +#pragma mark == DNS-SD Properties == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceCopyProperty_client + + @abstract Client-side version of DNSServiceCopyProperty. +*/ + +DNSServiceErrorType DNSServiceCopyProperty_client( const char *inServer, DNSPropertyCode inCode, DNSPropertyData *outData ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceReleaseProperty_client + + @abstract Client-side version of DNSServiceReleaseProperty. +*/ + +DNSServiceErrorType DNSServiceReleaseProperty_client( DNSPropertyData *inData ); + +#if 0 +#pragma mark == DNS-SD Domain Enumeration == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceEnumerateDomains_client + + @abstract Client-side version of DNSServiceEnumerateDomains. +*/ + +DNSServiceErrorType + DNSServiceEnumerateDomains_client( + DNSServiceRef * outRef, + const char * inServer, + const DNSServiceFlags inFlags, + const uint32_t inInterfaceIndex, + const DNSServiceDomainEnumReply inCallBack, + void * inContext ); + +#if 0 +#pragma mark == DNS-SD Service Registration == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRegister_client + + @abstract Client-side version of DNSServiceRegister. +*/ + +DNSServiceErrorType + DNSServiceRegister_client( + DNSServiceRef * outRef, + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + const char * inHost, + uint16_t inPort, + uint16_t inTXTSize, + const void * inTXT, + DNSServiceRegisterReply inCallBack, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceAddRecord_client + + @abstract Client-side version of DNSServiceAddRecord. +*/ + +DNSServiceErrorType + DNSServiceAddRecord_client( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint16_t inRRType, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceUpdateRecord_client + + @abstract Client-side version of DNSServiceUpdateRecord. +*/ + +DNSServiceErrorType + DNSServiceUpdateRecord_client( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRemoveRecord_client + + @abstract Client-side version of DNSServiceRemoveRecord. +*/ + +DNSServiceErrorType DNSServiceRemoveRecord_client( DNSServiceRef inRef, DNSRecordRef inRecordRef, DNSServiceFlags inFlags ); + +#if 0 +#pragma mark == DNS-SD Service Discovery == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceBrowse_client + + @abstract Client-side version of DNSServiceBrowse. +*/ + +DNSServiceErrorType + DNSServiceBrowse_client( + DNSServiceRef * outRef, + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inType, + const char * inDomain, + DNSServiceBrowseReply inCallBack, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceResolve_client + + @abstract Client-side version of DNSServiceResolve. +*/ + +DNSServiceErrorType + DNSServiceResolve_client( + DNSServiceRef * outRef, + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + const char * inType, + const char * inDomain, + DNSServiceResolveReply inCallBack, + void * inContext ); + +#if 0 +#pragma mark == DNS-SD Special Purpose == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceCreateConnection_client + + @abstract Client-side version of DNSServiceCreateConnection. +*/ + +DNSServiceErrorType DNSServiceCreateConnection_client( DNSServiceRef *outRef, const char *inServer ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceRegisterRecord_client + + @abstract Client-side version of DNSServiceRegisterRecord. +*/ + +DNSServiceErrorType + DNSServiceRegisterRecord_client( + DNSServiceRef inRef, + DNSRecordRef * outRecordRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + DNSServiceRegisterRecordReply inCallBack, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceQueryRecord_client + + @abstract Client-side version of DNSServiceQueryRecord. +*/ + +DNSServiceErrorType + DNSServiceQueryRecord_client( + DNSServiceRef * outRef, + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + DNSServiceQueryRecordReply inCallBack, + void * inContext ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function DNSServiceReconfirmRecord_client + + @abstract Client-side version of DNSServiceReconfirmRecord. +*/ + +void + DNSServiceReconfirmRecord_client( + const char * inServer, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData ); + +#ifdef __cplusplus + } +#endif + +#endif // __RMx_CLIENT__ diff --git a/mDNSWindows/RMxCommon.c b/mDNSWindows/RMxCommon.c new file mode 100644 index 0000000..686ee93 --- /dev/null +++ b/mDNSWindows/RMxCommon.c @@ -0,0 +1,1501 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: RMxCommon.c,v $ +Revision 1.2 2004/03/16 22:09:03 bradley +Skip socket creation failures to handle local IPv6 addresses being returned by Windows even when +they are not actually supported by the OS; Log a message and only fail if no sockets can be created. + +Revision 1.1 2004/01/30 02:35:13 bradley +Rendezvous Message Exchange implementation for DNS-SD IPC on Windows. + +*/ + +#include +#include +#include + +#include "CommonServices.h" + +#include + +#include "RMxCommon.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[RMxCommon] " + +#define kRMxSessionOpenValidFlags ( kRMxSessionFlagsNoThread | kRMxSessionFlagsNoClose ) + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// Session + +DEBUG_LOCAL unsigned WINAPI RMxSessionThread( LPVOID inParam ); +DEBUG_LOCAL OSStatus RMxSessionInitServer( RMxSessionRef inSession ); +DEBUG_LOCAL OSStatus RMxSessionInitClient( RMxSessionRef inSession, const char *inServer ); +DEBUG_LOCAL OSStatus RMxSessionConnect( RMxSessionRef inSession, const struct sockaddr *inAddr, size_t inAddrSize ); +DEBUG_LOCAL OSStatus + RMxSessionSendMessageVAList( + RMxSessionRef inSession, + RMxOpCode inOpCode, + OSStatus inStatus, + const char * inFormat, + va_list inArgs1, + va_list inArgs2 ); + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +// General + +DEBUG_LOCAL CRITICAL_SECTION gRMxLock; +DEBUG_LOCAL bool gRMxLockInitialized = false; +DEBUG_LOCAL RMxSessionRef gRMxSessionList = NULL; +RMxState gRMxState = kRMxStateInvalid; +HANDLE gRMxStateChangeEvent = NULL; + +#if 0 +#pragma mark - +#pragma mark == General == +#endif + +//=========================================================================================================================== +// RMxInitialize +//=========================================================================================================================== + +OSStatus RMxInitialize( void ) +{ + OSStatus err; + WSADATA wsaData; + + // Set up WinSock. + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsaData ); + require_noerr( err, exit ); + require_action( ( LOBYTE( wsaData.wVersion ) == 2 ) && ( HIBYTE( wsaData.wVersion ) == 2 ), exit, err = kUnsupportedErr ); + + // Set up the global locked used to protect things like the session list. + + InitializeCriticalSection( &gRMxLock ); + gRMxLockInitialized = true; + + // Set up the state and state changed event. A manual-reset event is used so all threads wake up when it is signaled. + + gRMxState = kRMxStateInvalid; + gRMxStateChangeEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + err = translate_errno( gRMxStateChangeEvent, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// RMxFinalize +//=========================================================================================================================== + +void RMxFinalize( void ) +{ + BOOL ok; + OSStatus err; + + // Signal a state changed event to trigger everything to stop. Note: This is a manual-reset event so it will + // remain signaled to allow all running threads to wake up and exit cleanly. + + gRMxState = kRMxStateStop; + if( gRMxStateChangeEvent ) + { + ok = SetEvent( gRMxStateChangeEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + } + + // Close any open sessions. + + while( gRMxSessionList ) + { + err = RMxSessionClose( gRMxSessionList, kEndingErr ); + check( ( err == kNoErr ) || ( err == kNotFoundErr ) ); + } + + // Release the state changed event. + + if( gRMxStateChangeEvent ) + { + ok = CloseHandle( gRMxStateChangeEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + gRMxStateChangeEvent = NULL; + } + + // Release the lock. + + if( gRMxLockInitialized ) + { + gRMxLockInitialized = false; + DeleteCriticalSection( &gRMxLock ); + } + + // Tear down WinSock. + + WSACleanup(); +} + +//=========================================================================================================================== +// RMxLock +//=========================================================================================================================== + +void RMxLock( void ) +{ + check( gRMxLockInitialized ); + if( gRMxLockInitialized ) + { + EnterCriticalSection( &gRMxLock ); + } +} + +//=========================================================================================================================== +// RMxUnlock +//=========================================================================================================================== + +void RMxUnlock( void ) +{ + check( gRMxLockInitialized ); + if( gRMxLockInitialized ) + { + LeaveCriticalSection( &gRMxLock ); + } +} + +#if 0 +#pragma mark - +#pragma mark == Messages == +#endif + +//=========================================================================================================================== +// RMxMessageInitialize +//=========================================================================================================================== + +void RMxMessageInitialize( RMxMessage *inMessage ) +{ + check( inMessage ); + if( inMessage ) + { + inMessage->sendSize = 0; + inMessage->sendData = NULL; + inMessage->recvSize = 0; + inMessage->recvData = NULL; + inMessage->bufferSize = 0; + inMessage->buffer = NULL; + } +} + +//=========================================================================================================================== +// RMxMessageRelease +//=========================================================================================================================== + +void RMxMessageRelease( RMxMessage *inMessage ) +{ + check( inMessage ); + if( inMessage ) + { + // Assert if a malloc'd buffer was used for a small message that could have used the inline message buffer. + + check( !inMessage->recvData || + ( inMessage->recvSize > sizeof( inMessage->storage ) ) || + ( inMessage->recvData == inMessage->storage ) ); + + // Free the memory if it was malloc'd (non-null and not the inline message buffer). + + if( inMessage->recvData && ( inMessage->recvData != inMessage->storage ) ) + { + free( inMessage->recvData ); + } + inMessage->recvData = NULL; + } +} + +#if 0 +#pragma mark - +#pragma mark == Sessions == +#endif + +//=========================================================================================================================== +// RMxSessionOpen +//=========================================================================================================================== + +OSStatus + RMxSessionOpen( + const char * inServer, + RMxSessionFlags inFlags, + SocketRef inSock, + RMxMessageCallBack inCallBack, + void * inContext, + RMxSessionRef * outSession, + RMxOpCode inMessageOpCode, + const char * inMessageFormat, + ... ) +{ + OSStatus err; + RMxSessionRef session; + DWORD result; + bool locked; + + session = NULL; + locked = false; + dlog( kDebugLevelNotice, DEBUG_NAME "opening %s session to %s\n", IsValidSocket( inSock ) ? "server" : "client", + inServer ? inServer : "" ); + require_action( gRMxState == kRMxStateRun, exit, err = kStateErr ); + require_action( ( inFlags & ~kRMxSessionOpenValidFlags ) == 0, exit, err = kFlagErr ); + + // Allocate and initialize the object and add it to the session list. + + session = (RMxSessionRef) calloc( 1, sizeof( *session ) ); + require_action( session, exit, err = kNoMemoryErr ); + + session->flags = inFlags; + session->sock = kInvalidSocketRef; + session->callback = inCallBack; + RMxMessageInitialize( &session->message ); + session->message.context = inContext; + session->message.session = session; + session->messageRecvBuffer = session->message.storage; + session->messageRecvBufferPtr = session->messageRecvBuffer; + session->messageRecvRemaining = kRMxMessageHeaderSize; + session->messageRecvHeaderDone = false; + + RMxLock(); + session->next = gRMxSessionList; + gRMxSessionList = session; + RMxUnlock(); + + session->closeEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + err = translate_errno( session->closeEvent, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + // Set up the sockets and socket events. + + if( IsValidSocket( inSock ) ) + { + // Server: accepted a connection from a client. + + session->flags |= kRMxSessionFlagsServer; + session->sock = inSock; + inSock = kInvalidSocketRef; + + err = RMxSessionInitServer( session ); + require_noerr( err, exit ); + } + else + { + // Client: initiate a connection to the server. + + err = RMxSessionInitClient( session, inServer ); + require_noerr( err, exit ); + } + + // Create thread with _beginthreadex() instead of CreateThread() to avoid memory leaks when using static run-time + // libraries. See . + // Create the thread suspended then resume it so the thread handle and ID are valid before the thread starts running. + + RMxLock(); + locked = true; + if( !( inFlags & kRMxSessionFlagsNoThread ) ) + { + session->thread = (HANDLE) _beginthreadex_compat( NULL, 0, RMxSessionThread, session, CREATE_SUSPENDED, &session->threadID ); + err = translate_errno( session->thread, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + result = ResumeThread( session->thread ); + err = translate_errno( result != (DWORD) -1, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + } + + // Send the optional message. Note: we have to do 2 va_start/va_end because va_copy is not widely supported yet. + + if( inMessageFormat ) + { + va_list args1; + va_list args2; + + va_start( args1, inMessageFormat ); + va_start( args2, inMessageFormat ); + err = RMxSessionSendMessageVAList( session, inMessageOpCode, kNoErr, inMessageFormat, args1, args2 ); + va_end( args1 ); + va_end( args2 ); + require_noerr( err, exit ); + } + + // Success! + + if( outSession ) + { + *outSession = session; + } + session = NULL; + +exit: + if( locked ) + { + RMxUnlock(); + } + if( session ) + { + RMxSessionClose( session, err ); + } + if( IsValidSocket( inSock ) ) + { + close_compat( inSock ); + } + return( err ); +} + +//=========================================================================================================================== +// RMxSessionClose +//=========================================================================================================================== + +OSStatus RMxSessionClose( RMxSessionRef inSession, OSStatus inReason ) +{ + OSStatus err; + bool locked; + RMxSessionRef * p; + bool sameThread; + bool deferClose; + BOOL ok; + DWORD threadID; + DWORD result; + + DEBUG_USE_ONLY( inReason ); + + check( inSession ); + + // Find the session in the list. + + RMxLock(); + locked = true; + + for( p = &gRMxSessionList; *p; p = &( *p )->next ) + { + if( *p == inSession ) + { + break; + } + } + require_action( *p, exit, err = kNotFoundErr ); + + // If we're being called from the same thread as the session (e.g. message callback is closing the session) then + // we must defer the close until the thread is done because the thread is still using the session object. + + deferClose = false; + threadID = GetCurrentThreadId(); + sameThread = inSession->thread && ( threadID == inSession->threadID ); + if( sameThread && !( inSession->flags & kRMxSessionFlagsThreadDone ) ) + { + inSession->flags &= ~kRMxSessionFlagsNoClose; + deferClose = true; + } + + // If the thread we're not being called from the session thread, but the thread has already marked itself as + // as done (e.g. session closed from something like a peer disconnect and at the same time the client also + // tried to close) then we only want to continue with the close if the thread is not going to close itself. + + if( !sameThread && ( inSession->flags & kRMxSessionFlagsThreadDone ) && !( inSession->flags & kRMxSessionFlagsNoClose ) ) + { + deferClose = true; + } + + // Signal a close so the thread exits. + + inSession->quit = true; + if( inSession->closeEvent ) + { + ok = SetEvent( inSession->closeEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + } + if( deferClose ) + { + err = kNoErr; + goto exit; + } + inSession->flags |= kRMxSessionFlagsNoClose; + + // Remove the session from the list. + + *p = inSession->next; + + RMxUnlock(); + locked = false; + + // Wait for the thread to exit. Give up after 10 seconds to handle a hung thread. + + if( inSession->thread && ( threadID != inSession->threadID ) ) + { + result = WaitForSingleObject( inSession->thread, 10 * 1000 ); + check_translated_errno( result == WAIT_OBJECT_0, (OSStatus) GetLastError(), result ); + } + + // Release the thread. + + if( inSession->thread ) + { + ok = CloseHandle( inSession->thread ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + inSession->thread = NULL; + } + + // Release the socket event. + + if( inSession->sockEvent ) + { + ok = CloseHandle( inSession->sockEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + inSession->sockEvent = NULL; + } + + // Close the socket. + + if( IsValidSocket( inSession->sock ) ) + { + err = close_compat( inSession->sock ); + err = translate_errno( err == 0, errno_compat(), kUnknownErr ); + check_noerr( err ); + inSession->sock = kInvalidSocketRef; + } + + // Release the close event. + + if( inSession->closeEvent ) + { + ok = CloseHandle( inSession->closeEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + inSession->closeEvent = NULL; + } + + // Release the memory used by the object. + + RMxMessageRelease( &inSession->message ); + free( inSession ); + err = kNoErr; + + dlog( kDebugLevelNotice, DEBUG_NAME "session closed (%d %m)\n", inReason, inReason ); + +exit: + if( locked ) + { + RMxUnlock(); + } + return( err ); +} + +//=========================================================================================================================== +// RMxSessionThread +//=========================================================================================================================== + +DEBUG_LOCAL unsigned WINAPI RMxSessionThread( LPVOID inParam ) +{ + OSStatus err; + RMxSessionRef session; + bool safeToClose; + + session = (RMxSessionRef) inParam; + check( session ); + + // Process messages until the session is told to close or the remote site disconnects. + + for( ;; ) + { + if( session->quit || ( gRMxState != kRMxStateRun ) ) + { + dlog( kDebugLevelNotice, DEBUG_NAME "session state exit (quit=%d, state=%d)\n", session->quit, gRMxState ); + err = kEndingErr; + break; + } + + err = RMxSessionRecvMessage( session, INFINITE ); + if( err == kNoErr ) + { + if( session->callback ) + { + session->callback( &session->message ); + } + RMxMessageRelease( &session->message ); + } + else + { + dlog( kDebugLevelNotice, DEBUG_NAME "session closing (%d %m)\n", err, err ); + break; + } + } + + // Tell the callback the session is closing. + + if( session->callback ) + { + session->message.opcode = kRMxOpCodeInvalid; + session->message.status = kNoErr; + session->message.sendSize = 0; + session->message.sendData = NULL; + session->message.recvSize = 0; + session->message.recvData = NULL; + session->callback( &session->message ); + } + + // Mark the thread as done and close the session (unless asked not to). + + RMxLock(); + session->flags |= kRMxSessionFlagsThreadDone; + safeToClose = !( session->flags & kRMxSessionFlagsNoClose ); + RMxUnlock(); + if( safeToClose ) + { + RMxSessionClose( session, err ); + } + + // Call _endthreadex() explicitly instead of just exiting normally to avoid memory leaks when using static run-time + // libraries. See . + + _endthreadex_compat( (unsigned) err ); + return( (unsigned) err ); +} + +//=========================================================================================================================== +// RMxSessionAccept +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus RMxSessionInitServer( RMxSessionRef inSession ) +{ + OSStatus err; + + inSession->sockEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + err = translate_errno( inSession->sockEvent, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + err = WSAEventSelect( inSession->sock, inSession->sockEvent, FD_READ | FD_CLOSE ); + err = translate_errno( err == 0, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + inSession->waitCount = 0; + inSession->waitHandles[ inSession->waitCount++ ] = inSession->sockEvent; + inSession->waitHandles[ inSession->waitCount++ ] = inSession->closeEvent; + inSession->waitHandles[ inSession->waitCount++ ] = gRMxStateChangeEvent; + check( inSession->waitCount == sizeof_array( inSession->waitHandles ) ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// RMxSessionInitClient +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus RMxSessionInitClient( RMxSessionRef inSession, const char *inServer ) +{ + OSStatus err; + struct addrinfo hints; + struct addrinfo * addrList; + struct addrinfo * addr; + BOOL ok; + + addrList = NULL; + + // Initiate a connection to the server (if we're the client). + + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + err = getaddrinfo( inServer, kRMxServerPortString, &hints, &addrList ); + require_noerr( err, exit ); + + for( addr = addrList; addr; addr = addr->ai_next ) + { + check( addr->ai_addr && ( addr->ai_addrlen > 0 ) ); + + if( inSession->quit || ( gRMxState != kRMxStateRun ) ) + { + dlog( kDebugLevelNotice, DEBUG_NAME "session state exit while connecting (quit=%d, state=%d)\n", + inSession->quit, gRMxState ); + err = kEndingErr; + break; + } + + // Clean up for any previous iteration that failed. Normal cleanup will occur when closing the session. + + if( IsValidSocket( inSession->sock ) ) + { + err = close_compat( inSession->sock ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + inSession->sock = kInvalidSocketRef; + } + + if( inSession->sockEvent ) + { + ok = CloseHandle( inSession->sockEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + inSession->sockEvent = NULL; + } + + // Set up the socket and try to connect. + + dlog( kDebugLevelTrace, DEBUG_NAME "connecting %s socket to %s/%##a\n", + ( addr->ai_family == AF_INET ) ? "AF_INET" : ( addr->ai_family == AF_INET6 ) ? "AF_INET6" : "", + inServer ? inServer : "", addr->ai_addr ); + + inSession->sock = socket( addr->ai_family, addr->ai_socktype, addr->ai_protocol ); + err = translate_errno( IsValidSocket( inSession->sock ), errno_compat(), kNoResourcesErr ); + if( err != kNoErr ) + { + dlog( kDebugLevelNotice, DEBUG_NAME "%s socket not supported...skipping (%d %m)\n", + ( addr->ai_family == AF_INET ) ? "AF_INET" : ( addr->ai_family == AF_INET6 ) ? "AF_INET6" : "", + err, err ); + continue; + } + + inSession->sockEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + err = translate_errno( inSession->sockEvent, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + err = WSAEventSelect( inSession->sock, inSession->sockEvent, FD_READ | FD_CONNECT | FD_CLOSE ); + err = translate_errno( err == 0, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + inSession->waitCount = 0; + inSession->waitHandles[ inSession->waitCount++ ] = inSession->sockEvent; + inSession->waitHandles[ inSession->waitCount++ ] = inSession->closeEvent; + inSession->waitHandles[ inSession->waitCount++ ] = gRMxStateChangeEvent; + check( inSession->waitCount == sizeof_array( inSession->waitHandles ) ); + + err = RMxSessionConnect( inSession, addr->ai_addr, addr->ai_addrlen ); + if( err == kNoErr ) + { + break; + } + } + require_action( addr, exit, err = kConnectionErr ); + +exit: + if( addrList ) + { + freeaddrinfo( addrList ); + } + return( err ); +} + +//=========================================================================================================================== +// RMxSessionConnect +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus RMxSessionConnect( RMxSessionRef inSession, const struct sockaddr *inAddr, size_t inAddrSize ) +{ + OSStatus err; + DWORD result; + + check( inSession ); + check( inSession->sock ); + check( inSession->waitCount > 0 ); + check( inAddr && ( inAddrSize > 0 ) ); + + // Start the connection process. This returns immediately. If the error is 0, it means the connection has + // successfully completed (usually only if the host is local). Otherwise, it returns an error to indicate + // that the connection process has started and we must use wait for it to complete. + + err = connect( inSession->sock, inAddr, (int) inAddrSize ); + if( err == 0 ) goto exit; + + // Wait for the connection to complete, timeout, or be canceled. + + result = WaitForMultipleObjects( inSession->waitCount, inSession->waitHandles, FALSE, kRMxClientTimeout ); + if( result == WAIT_OBJECT_0 ) // Socket Event + { + WSANETWORKEVENTS events; + + // Check the result of the FD_CONNECT event to see if the connection was successful or not. + + err = WSAEnumNetworkEvents( inSession->sock, NULL, &events ); + require_noerr( err, exit ); + require_action( events.lNetworkEvents & FD_CONNECT, exit, err = kSelectorErr ); + err = events.iErrorCode[ FD_CONNECT_BIT ]; + } + else if( result == WAIT_TIMEOUT ) + { + err = kTimeoutErr; + } + else + { + err = kEndingErr; + } + +exit: + return( err ); +} + +//=========================================================================================================================== +// RMxSessionSendMessage +// +// Warning: Assumes the RMx lock is held. +//=========================================================================================================================== + +OSStatus RMxSessionSendMessage( RMxSessionRef inSession, RMxOpCode inOpCode, OSStatus inStatus, const char *inFormat, ... ) +{ + OSStatus err; + va_list args1; + va_list args2; + + // Note: 2 va_list's need to be provided as the va_list needs to be processed twice (once to preflight, once for + // real) and this is illegal without C99 va_copy, but some environments do not yet support va_copy (e.g. Visual C++). + + va_start( args1, inFormat ); + va_start( args2, inFormat ); + err = RMxSessionSendMessageVAList( inSession, inOpCode, inStatus, inFormat, args1, args2 ); + va_end( args1 ); + va_end( args2 ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +//=========================================================================================================================== +// RMxSessionSendMessageVAList +// +// Warning: Assumes the RMx lock is held. +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus + RMxSessionSendMessageVAList( + RMxSessionRef inSession, + RMxOpCode inOpCode, + OSStatus inStatus, + const char * inFormat, + va_list inArgs1, + va_list inArgs2 ) +{ + OSStatus err; + RMxMessage msg; + uint8_t * bufferStorage; + uint8_t * buffer; + size_t bodySize; + size_t headerSize; + size_t size; + int n; + + bufferStorage = NULL; + check( inSession ); + check( IsValidSocket( inSession->sock ) ); + check( inFormat ); + + // Calculate the size of the data section of the message and set up the buffer for the entire message. + // If the entire message will fit in the inline message buffer, use it. Otherwise, allocate a buffer for it. + + err = RMxPackedSizeVAList( &bodySize, inFormat, inArgs1 ); + require_noerr( err, exit ); + + size = kRMxMessageHeaderSize + bodySize; + if( size <= sizeof( msg.storage ) ) + { + buffer = msg.storage; + } + else + { + bufferStorage = (uint8_t *) malloc( size ); + require_action( bufferStorage, exit, err = kNoMemoryErr ); + buffer = bufferStorage; + } + + // Build the message header. + + err = RMxPack( buffer, kRMxMessageHeaderSize, &headerSize, "wwwwww", + kRMxSignatureVersion1, // signature + inOpCode, // opcode + kRMxFlagsNone, // flags + 0, // xid + inStatus, // status + (uint32_t) bodySize ); // size + require_noerr( err, exit ); + check( headerSize == kRMxMessageHeaderSize ); + + // Build the message body. + + err = RMxPackVAList( buffer + kRMxMessageHeaderSize, size - kRMxMessageHeaderSize, &size, inFormat, inArgs2 ); + require_noerr( err, exit ); + check( size == bodySize ); + + // Send the entire message. + + size = headerSize + size; + n = send( inSession->sock, (const char *) buffer, (int) size, 0 ); + err = translate_errno( n == (int) size, errno_compat(), kWriteErr ); + require_noerr( err, exit ); + +exit: + if( bufferStorage ) + { + free( bufferStorage ); + } + return( err ); +} + +//=========================================================================================================================== +// RMxSessionRecvMessage +// +// Note: This routine maintains state within the session data structure and can resume partial message reception. +//=========================================================================================================================== + +OSStatus RMxSessionRecvMessage( RMxSessionRef inSession, DWORD inTimeout ) +{ + OSStatus err; + DWORD result; + int n; + + for( ;; ) + { + if( inSession->quit || ( gRMxState != kRMxStateRun ) ) + { + dlog( kDebugLevelNotice, DEBUG_NAME "session recv state exit (quit=%d, state=%d)\n", inSession->quit, gRMxState ); + err = kEndingErr; + goto exit; + } + + // Wait for data to become available or another event to occur. + + result = WaitForMultipleObjects( inSession->waitCount, inSession->waitHandles, FALSE, inTimeout ); + if( result == WAIT_OBJECT_0 ) // Socket Event (i.e. data available to read) + { + n = recv( inSession->sock, (char *) inSession->messageRecvBufferPtr, inSession->messageRecvRemaining, 0 ); + if( n > 0 ) + { + inSession->messageRecvBufferPtr += n; + inSession->messageRecvRemaining -= n; + if( inSession->messageRecvRemaining == 0 ) + { + // Complete chunk ready. If we haven't read the header yet, it's the header. Otherwise, it's the data. + + if( !inSession->messageRecvHeaderDone ) + { + // Parse the buffer into the message header structure and mark the header as complete. + + err = RMxUnpack( inSession->messageRecvBuffer, kRMxMessageHeaderSize, + "wwwwww", + &inSession->message.signature, + &inSession->message.opcode, + &inSession->message.flags, + &inSession->message.xid, + &inSession->message.status, + &inSession->message.recvSize ); + require_noerr( err, exit ); + require_action( inSession->message.signature == kRMxSignatureVersion1, exit, err = kMismatchErr ); + require_action( inSession->message.opcode != kRMxOpCodeInvalid, exit, err = kMismatchErr ); + inSession->messageRecvHeaderDone = true; + + // Set up to read the data section (if any). Use the inline message buffer if the data will fit. + + if( inSession->message.recvSize > 0 ) + { + if( inSession->message.recvSize <= sizeof( inSession->message.storage ) ) + { + inSession->message.recvData = inSession->message.storage; + } + else + { + inSession->message.recvData = (uint8_t *) malloc( inSession->message.recvSize ); + require_action( inSession->message.recvData, exit, err = kNoMemoryErr ); + } + inSession->messageRecvBufferPtr = inSession->message.recvData; + inSession->messageRecvRemaining = (int) inSession->message.recvSize; + } + } + } + if( inSession->messageRecvHeaderDone && ( inSession->messageRecvRemaining == 0 ) ) + { + // Complete message ready. Reset state for next message. Exit to allow message to be processed. + + inSession->messageRecvBufferPtr = inSession->messageRecvBuffer; + inSession->messageRecvRemaining = kRMxMessageHeaderSize; + inSession->messageRecvHeaderDone = false; + break; + } + } + else + { + err = errno_compat(); + dlog( kDebugLevelNotice, DEBUG_NAME "session recv peer disconnected (%d/%d %m)\n", n, err, err ); + err = kConnectionErr; + goto exit; + } + } + else if( result == ( WAIT_OBJECT_0 + 1 ) ) // Close Event + { + dlog( kDebugLevelNotice, DEBUG_NAME "session recv close signaled\n" ); + err = kEndingErr; + goto exit; + } + else if( result == ( WAIT_OBJECT_0 + 2 ) ) // State Change Event + { + dlog( kDebugLevelNotice, DEBUG_NAME "session recv state change (%d)\n", gRMxState ); + err = kEndingErr; + goto exit; + } + else if( result == WAIT_TIMEOUT ) // Timeout + { + dlog( kDebugLevelNotice, DEBUG_NAME "session recv timeout\n" ); + err = kTimeoutErr; + goto exit; + } + else + { + err = errno_compat(); + dlog( kDebugLevelAlert, DEBUG_NAME "session recv message wait error: 0x%08X, %d (%m)\n", result, err, err ); + err = (OSStatus) result; + goto exit; + } + } + err = kNoErr; + +exit: + return( err ); +} + +#if 0 +#pragma mark - +#pragma mark == Utilities == +#endif + +//=========================================================================================================================== +// RMxCheckVersion +//=========================================================================================================================== + +OSStatus + RMxCheckVersion( + uint32_t inClientCurrentVersion, uint32_t inClientOldestClientVersion, uint32_t inClientOldestServerVersion, + uint32_t inServerCurrentVersion, uint32_t inServerOldestClientVersion, uint32_t inServerOldestServerVersion ) +{ + OSStatus err; + const char * message; + + DEBUG_USE_ONLY( inClientOldestClientVersion ); + DEBUG_USE_ONLY( inServerOldestServerVersion ); + DEBUG_USE_ONLY( message ); + + // Determine if the version information on both sides is compatible. + + if( inClientCurrentVersion == inServerCurrentVersion ) + { + message = "versions exactly match"; + err = kNoErr; + } + else if( inClientCurrentVersion > inServerCurrentVersion ) + { + if( inClientOldestServerVersion <= inServerCurrentVersion ) + { + message = "client newer, but compatible"; + err = kNoErr; + } + else + { + message = "server too old for client"; + err = kIncompatibleErr; + } + } + else + { + if( inServerOldestClientVersion <= inClientCurrentVersion ) + { + message = "server newer, but compatible"; + err = kNoErr; + } + else + { + message = "client too old for server"; + err = kIncompatibleErr; + } + } + dlog( kDebugLevelNotice, DEBUG_NAME "%s (client=%v/%v/%v vs server=%v/%v/%v)\n", message, + inClientCurrentVersion, inClientOldestClientVersion, inClientOldestServerVersion, + inServerCurrentVersion, inServerOldestClientVersion, inServerOldestServerVersion ); + + return( err ); +} + +//=========================================================================================================================== +// RMxPackedSize +//=========================================================================================================================== + +OSStatus RMxPackedSize( size_t *outSize, const char *inFormat, ... ) +{ + OSStatus err; + va_list args; + + va_start( args, inFormat ); + err = RMxPackedSizeVAList( outSize, inFormat, args ); + va_end( args ); + + return( err ); +} + +//=========================================================================================================================== +// RMxPackedSizeVAList +//=========================================================================================================================== + +OSStatus RMxPackedSizeVAList( size_t *outSize, const char *inFormat, va_list inArgs ) +{ + OSStatus err; + size_t size; + char c; + const uint8_t * src; + uint32_t tempU32; + const uint8_t * p; + + check_compile_time_code( sizeof( unsigned int ) >= 4 ); + + size = 0; + + // Loop thru each character in the format string, decode it, and add the size required to pack it. + + for( c = *inFormat; c != '\0'; c = *( ++inFormat ) ) + { + switch( c ) + { + case 'b': // Byte (8-bit) + + va_arg( inArgs, unsigned int ); + size += 1; + break; + + case 'h': // Half-word (16-bit) + + va_arg( inArgs, unsigned int ); + size += 2; + break; + + case 'w': // Word (32-bit) + + va_arg( inArgs, unsigned int ); + size += 4; + break; + + case 's': // UTF-8 String, null terminated + + src = va_arg( inArgs, const uint8_t * ); + check( src ); + + p = src; + while( *p++ != 0 ) {} + size += ( p - src ); + break; + + case 'n': // N bytes of raw data; 1st arg is size, 2nd arg is ptr; stored with 32-bit length prefix. + + tempU32 = (uint32_t) va_arg( inArgs, unsigned int ); + src = va_arg( inArgs, const uint8_t * ); + check( src || ( tempU32 == 0 ) ); + + size += ( 4 + tempU32 ); + break; + + case ' ': // Ignore spaces (they're just for format string readability). + break; + + default: // Unknown format specifier + + err = kUnsupportedErr; + goto exit; + } + } + + // Success! + + if( outSize ) + { + *outSize = size; + } + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// RMxPack +//=========================================================================================================================== + +OSStatus RMxPack( void *inBuffer, size_t inMaxSize, size_t *outSize, const char *inFormat, ... ) +{ + OSStatus err; + va_list args; + + va_start( args, inFormat ); + err = RMxPackVAList( inBuffer, inMaxSize, outSize, inFormat, args ); + va_end( args ); + + return( err ); +} + +//=========================================================================================================================== +// RMxPackVAList +//=========================================================================================================================== + +OSStatus RMxPackVAList( void *inBuffer, size_t inMaxSize, size_t *outSize, const char *inFormat, va_list inArgs ) +{ + OSStatus err; + char c; + const uint8_t * src; + uint8_t * dst; + uint8_t * end; + uint8_t tempU8; + uint16_t tempU16; + uint32_t tempU32; + const uint8_t * p; + size_t size; + + check_compile_time_code( sizeof( unsigned int ) >= 4 ); + + dst = (uint8_t *) inBuffer; + end = dst + inMaxSize; + + // Loop thru each character in the format string, decode it, and pack the data appropriately. + + for( c = *inFormat; c != '\0'; c = *( ++inFormat ) ) + { + switch( c ) + { + case 'b': // Byte (8-bit) + + check( ( end - dst ) >= 1 ); + tempU8 = (uint8_t) va_arg( inArgs, unsigned int ); + *dst++ = tempU8; + break; + + case 'h': // Half-word (16-bit) + + check( ( end - dst ) >= 2 ); + tempU16 = (uint16_t) va_arg( inArgs, unsigned int ); + *dst++ = (uint8_t)( ( tempU16 >> 8 ) & 0xFF ); + *dst++ = (uint8_t)( tempU16 & 0xFF ); + break; + + case 'w': // Word (32-bit) + + check( ( end - dst ) >= 4 ); + tempU32 = (uint32_t) va_arg( inArgs, unsigned int ); + *dst++ = (uint8_t)( ( tempU32 >> 24 ) & 0xFF ); + *dst++ = (uint8_t)( ( tempU32 >> 16 ) & 0xFF ); + *dst++ = (uint8_t)( ( tempU32 >> 8 ) & 0xFF ); + *dst++ = (uint8_t)( tempU32 & 0xFF ); + break; + + case 's': // UTF-8 String, null terminated + + src = va_arg( inArgs, const uint8_t * ); + check( src ); + + p = src; + while( *p++ != 0 ) {} + size = (size_t)( p - src ); + check( ( end - dst ) >= (ptrdiff_t) size ); + + while( size-- > 0 ) + { + *dst++ = *src++; + } + break; + + case 'n': // N bytes of raw data; 1st arg is size, 2nd arg is ptr; stored with 32-bit length prefix. + + tempU32 = (uint32_t) va_arg( inArgs, unsigned int ); + check( ( end - dst ) >= (ptrdiff_t)( 4 + tempU32 ) ); + + src = va_arg( inArgs, const uint8_t * ); + check( src || ( tempU32 == 0 ) ); + + *dst++ = (uint8_t)( ( tempU32 >> 24 ) & 0xFF ); + *dst++ = (uint8_t)( ( tempU32 >> 16 ) & 0xFF ); + *dst++ = (uint8_t)( ( tempU32 >> 8 ) & 0xFF ); + *dst++ = (uint8_t)( tempU32 & 0xFF ); + while( tempU32-- > 0 ) + { + *dst++ = *src++; + } + break; + + case ' ': // Ignore spaces (they're just for format string readability). + break; + + default: // Unknown format specifier + + err = kUnsupportedErr; + goto exit; + } + } + + // Success! + + if( outSize ) + { + *outSize = (size_t)( dst - ( (uint8_t *) inBuffer ) ); + } + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// RMxUnpack +//=========================================================================================================================== + +OSStatus RMxUnpack( const void *inData, size_t inSize, const char *inFormat, ... ) +{ + OSStatus err; + va_list args; + + va_start( args, inFormat ); + err = RMxUnpackVAList( inData, inSize, inFormat, args ); + va_end( args ); + + return( err ); +} + +//=========================================================================================================================== +// DNSUnpackVAList +//=========================================================================================================================== + +OSStatus RMxUnpackVAList( const void *inData, size_t inSize, const char *inFormat, va_list inArgs ) +{ + OSStatus err; + char c; + const uint8_t * src; + const uint8_t * end; + uint8_t * b; + uint16_t * h; + uint32_t * w; + uint16_t tempU16; + uint32_t tempU32; + const uint8_t * p; + size_t size; + const uint8_t ** ptrArg; + size_t * sizeArg; + + check_compile_time_code( sizeof( unsigned int ) >= 4 ); + + src = (const uint8_t *) inData; + end = src + inSize; + + // Loop thru each character in the format string, decode it, and unpack the data appropriately. + + for( c = *inFormat; c != '\0'; c = *( ++inFormat ) ) + { + switch( c ) + { + case 'b': // Byte (8-bit) + + require_action( ( end - src ) >= 1, exit, err = kSizeErr ); + b = va_arg( inArgs, uint8_t * ); + if( b ) + { + *b = *src; + } + ++src; + break; + + case 'h': // Half-word (16-bit) + + require_action( ( end - src ) >= 2, exit, err = kSizeErr ); + tempU16 = (uint16_t)( *src++ << 8 ); + tempU16 |= (uint16_t)( *src++ ); + h = va_arg( inArgs, uint16_t * ); + if( h ) + { + *h = tempU16; + } + break; + + case 'w': // Word (32-bit) + + require_action( ( end - src ) >= 4, exit, err = kSizeErr ); + tempU32 = (uint32_t)( *src++ << 24 ); + tempU32 |= (uint32_t)( *src++ << 16 ); + tempU32 |= (uint32_t)( *src++ << 8 ); + tempU32 |= (uint32_t)( *src++ ); + w = va_arg( inArgs, uint32_t * ); + if( w ) + { + *w = tempU32; + } + break; + + case 's': // UTF-8 String, null terminated; 1st arg=ptr to ptr, 2nd arg=ptr to size, excluding null terminator. + + p = src; + while( ( ( end - p ) > 0 ) && ( *p != 0 ) ) + { + ++p; + } + require_action( ( end - p ) > 0, exit, err = kSizeErr ); + size = (size_t)( p - src ); + + ptrArg = va_arg( inArgs, const uint8_t ** ); + if( ptrArg ) + { + *ptrArg = src; + } + + sizeArg = va_arg( inArgs, size_t * ); + if( sizeArg ) + { + *sizeArg = size; + } + + src = p + 1; + break; + + case 'n': // N bytes of raw data; 1st arg is ptr to ptr, 2nd arg is ptr to size. + + require_action( ( end - src ) >= 4, exit, err = kSizeErr ); + tempU32 = (uint32_t)( *src++ << 24 ); + tempU32 |= (uint32_t)( *src++ << 16 ); + tempU32 |= (uint32_t)( *src++ << 8 ); + tempU32 |= (uint32_t)( *src++ ); + require_action( ( end - src ) >= (ptrdiff_t) tempU32, exit, err = kSizeErr ); + size = (size_t) tempU32; + + ptrArg = va_arg( inArgs, const uint8_t ** ); + if( ptrArg ) + { + *ptrArg = src; + } + + sizeArg = va_arg( inArgs, size_t * ); + if( sizeArg ) + { + *sizeArg = size; + } + + src += size; + break; + + case ' ': // Ignore spaces (they're just for format string readability). + break; + + default: // Unknown format specifier + + err = kUnsupportedErr; + goto exit; + } + } + + // Success! + + err = kNoErr; + +exit: + return( err ); +} + +#if( DEBUG ) +//=========================================================================================================================== +// RMxPackUnpackTest +//=========================================================================================================================== + +OSStatus RMxPackUnpackTest( void ); + +OSStatus RMxPackUnpackTest( void ) +{ + static const uint8_t data[] = + { + 0xAA, + 0xBB, 0xCC, + 0x11, 0x22, 0x33, 0x44, + 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x70, 0x65, 0x6F, 0x70, 0x6C, 0x65, 0x00, // hello people\0 + 0x00, 0x00, 0x00, 0x05, 0x74, 0x65, 0x73, 0x74, 0x73 // tests + }; + OSStatus err; + uint8_t buffer[ 128 ]; + size_t size; + uint8_t b; + uint16_t h; + uint32_t w; + char * s; + size_t sSize; + uint8_t * d; + size_t dSize; + + check_compile_time_code( sizeof( data ) >= 29 ); + + // simple API test. + // + + printf( "\nsimple API test\n" ); + + err = RMxPackedSize( &size, "bhwsn ", 0xAA, 0xBBCC, 0x11223344, "hello people", 5, "tests" ); + require_noerr( err, exit ); + require_action( size == 29, exit, err = kSizeErr ); + + err = RMxPack( buffer, 29, &size, " bhwsn", 0xAA, 0xBBCC, 0x11223344, "hello people", 5, "tests" ); + require_noerr( err, exit ); + require_action( size == 29, exit, err = kSizeErr ); + require_action( memcmp( buffer, data, size ) == 0, exit, err = kMismatchErr ); + + err = RMxUnpack( data, sizeof( data ), "bhw sn", &b, &h, &w, &s, &sSize, &d, &dSize ); + require_noerr( err, exit ); + require_action( b == 0xAA, exit, err = kMismatchErr ); + require_action( h == 0xBBCC, exit, err = kMismatchErr ); + require_action( w == 0x11223344, exit, err = kMismatchErr ); + require_action( sSize == 12, exit, err = kSizeErr ); + require_action( strcmp( s, "hello people" ) == 0, exit, err = kMismatchErr ); + require_action( dSize == 5, exit, err = kSizeErr ); + require_action( memcmp( d, "tests", 5 ) == 0, exit, err = kMismatchErr ); + + printf( "\nsimple API test done\n\n" ); + + // Done + + printf( "\n\nALL TESTS PASSED\n\n" ); + +exit: + return( err ); +} +#endif // DEBUG + +#ifdef __cplusplus + } +#endif diff --git a/mDNSWindows/RMxCommon.h b/mDNSWindows/RMxCommon.h new file mode 100644 index 0000000..a28da74 --- /dev/null +++ b/mDNSWindows/RMxCommon.h @@ -0,0 +1,662 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: RMxCommon.h,v $ +Revision 1.1 2004/01/30 02:35:13 bradley +Rendezvous Message Exchange implementation for DNS-SD IPC on Windows. + +*/ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header RMxCommon.h + + @abstract Common code between the RMxClient and RMxServer. + + @discussion + + This handles establishing and accepting connections, the underying message sending and receiving, portable data + packing an unpacking, and shared utility routines. +*/ + +#ifndef __RMx_COMMON__ +#define __RMx_COMMON__ + +#include "CommonServices.h" +#include "DebugServices.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == Documentation == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header RMxCommon + + @abstract Rendezvous Message Exchange (RMx) protocol. + + @discussion + + Data Type Notes + =============== + + All multi-byte data types are transmitted in network byte order (i.e. big endian). + + Data types requiring the use of specific values (e.g. opcodes) are typedef'd to an abstract type. Data types used + in this manner can be easily searched to determine the appropriate values for the data type. Modern development + environments can jump directly to a type and therefore the appropriate constants, which speeds up development. + + Naming Conventions + ================== + + All data types and related constants are named to make it easy to associate them. The general scheme is: + + Data Types: RMx + + Describes the data type (e.g. "OpCode" is used for operation codes). + + For example: RMxOpCode + + Constants: kRMx + + Describes the data type (e.g. "OpCode" is used for operation codes in RMx messages). + Symbolic name for the constant (e.g. "Browse" for browse messages). + + For example: kRMxOpCodeBrowse + + These conventions make it easier for users to associate data types with their values and to find places where + these data types and constants are used. You can always search for the base portion of symbolic name (e.g. + "RMxOpCode" in kRMxOpCodeBrowse) and find all the data types and constants associated with it. Modern + development environments also allow you to jump directly to the data type or constant. + + Packet Format + ============= + + All data exchanged between client and server is encapsulated in packets of a specific format. There is a + generic header followed by an optional, opcode-specific data section: + + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + 0 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | signature (RMx1) | + 4 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | opcode | + 8 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | flags | + 12 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | xid | + 16 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | status | + 20 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | size | + 24 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | data ("size" octets) | + \ \ + \ \ + | | + n +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The packet format uses implicit typing, which means the sender and receiver must know the types and ordering of the + data based on the opcode of the message. There are standard primitive data types defined that all data structures + are built on: + + Byte - 8-bit value. Uses the "b" pack/unpack format specifier. + Half-Word - 16-bit value. Uses the "h" pack/unpack format specifier. + Word - 32-bit value. Uses the "w" pack/unpack format specifier. + String - UTF-8 string, null-terminated. Uses the "s" pack/unpack format specifier. + Raw Bytes - 32-bit length-prefixed block of data. Uses the "n" pack/unpack format specifier. + + The String and Raw Byte data types are designed to support directly referencing the raw packet data without + copying them into a temporary buffer for efficiency, easy of use, and to reduce the possibility of resource + leaks. The String data type contains a null terminator in the packet data so it can be treated as a normal + C-style string (e.g. strcpy works on it). The Raw Bytes data type can be used via a pointer and size. The data + packing and unpacking routines are designed around the packet format to allow for efficient packet handling. + + Protocol + ======== + + The RMx protocol is a TCP-based client/server protocol. The server listens on a well-known TCP port. The client + connects to the server port and a session of message exchange begins. The client drives the session by sending + the initial message describing the action it wants performed by the server. The server performs operations + requested by the client and asynchronously sends reply messages as specified by the type of request. + + The general operation of the RMx server is as follows: + + 1) Initialize server (set up synchronization support, etc.). + 2) Run server by creating socket(s) for listening and wait until a connection is accepted. + 3) If a connection is accepted: + 3a) Accept session (allocate, link into session list, create session thread, etc.). + + The general operation of the RMx client is as follows: + + 1) Initialize client (set up synchronization support, etc.). + 2) Start a session to the server. + + The general operation of the RMx session thread (used by the client and server) is as follows: + + 1) Initialize session (set up synchronization support, etc.). + 2) Wait for a message to be received. + 3) If a message is received: + 3a) Process the message. +*/ + +#if 0 +#pragma mark == Versions == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef kRMxCurrentVersion + + @abstract Current version of the RMx implementation. +*/ + +#define kRMxCurrentVersion NumVersionBuild( 1, 0, 0, kVersionStageFinal, 0 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef kRMxOldestClientVersion + + @abstract Oldest version of the RMx client that works with this server. +*/ + +#define kRMxOldestClientVersion NumVersionBuild( 1, 0, 0, kVersionStageFinal, 0 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef kRMxOldestServerVersion + + @abstract Oldest version of the RMx server that works with this client. +*/ + +#define kRMxOldestServerVersion NumVersionBuild( 1, 0, 0, kVersionStageFinal, 0 ) + +#if 0 +#pragma mark == General == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kRMxServerPort + + @abstract TCP port of DNS-SD server in host byte order. +*/ + +#define kRMxServerPort 5354 +#define kRMxServerPortString "5354" + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kRMxClientTimeout + + @abstract Default client timeout. +*/ + +#define kRMxClientTimeout ( 5 * 1000 ) // 5 seconds + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @defined kDNSRecordIndexDefault + + @abstract Record index for the default TXT record. +*/ + +#define kDNSRecordIndexDefaultTXT 0 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @enum RMxState + + @abstract State of the RMx engine. + + @constant kRMxStateInvalid Not in a valid state. + @constant kRMxStateStop Stopped. + @constant kRMxStateRun Running. +*/ + +typedef enum +{ + kRMxStateInvalid, + kRMxStateStop, + kRMxStateRun + +} RMxState; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @var gRMxState + + @abstract Global state of the RMx engine. +*/ + +extern RMxState gRMxState; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @var gRMxState + + @abstract Global state change event associated with the RMx engine. +*/ + +extern HANDLE gRMxStateChangeEvent; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxInitialize + + @abstract Initializes the RMx engine. +*/ + +OSStatus RMxInitialize( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxFinalize + + @abstract Finalizes the RMx engine. +*/ + +void RMxFinalize( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxLock + + @abstract Locks the RMx engine. +*/ + +void RMxLock( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxUnlock + + @abstract Unlocks the RMx engine. +*/ + +void RMxUnlock( void ); + +#if 0 +#pragma mark == Messages == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef RMxSignature + + @abstract Signature for RMx messages. + + @constant kRMxSignatureVersion1 Version 1 message. +*/ + +typedef uint32_t RMxSignature; + +#define kRMxSignatureVersion1 0x524D7831 // 'RMx1' Version 1 message. + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef RMxFlags + + @abstract Flags for RMx messages. + + @constant kRMxFlagsNone No flags. +*/ + +typedef uint32_t RMxFlags; + +#define kRMxFlagsNone 0 + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef RMxOpCode + + @abstract Operation code for RMx messages. + + @constant kRMxOpCodeInvalid Invalid message. + @constant kRMxOpCodeCheckVersion DNSServiceCheckVersion message. + @constant kRMxOpCodeCopyProperty DNSServiceCopyProperty message. + @constant kRMxOpCodeEnumerateDomains DNSServiceEnumerateDomains message. + @constant kRMxOpCodeRegister DNSServiceRegister message. + @constant kRMxOpCodeAddRecord DNSServiceAddRecord message. + @constant kRMxOpCodeUpdateRecord DNSServiceUpdateRecord message. + @constant kRMxOpCodeRemoveRecord DNSServiceRemoveRecord message. + @constant kRMxOpCodeBrowse DNSServiceBrowse message. + @constant kRMxOpCodeResolve DNSServiceResolve message. + @constant kRMxOpCodeCreateConnection DNSServiceCreateConnection message. + @constant kRMxOpCodeRegisterRecord DNSServiceRegisterRecord message. + @constant kRMxOpCodeQueryRecord DNSServiceQueryRecord message. + @constant kRMxOpCodeReconfirmRecord DNSServiceReconfirmRecord message. +*/ + +typedef uint32_t RMxOpCode; + +#define kRMxOpCodeInvalid 0 + +#define kRMxOpCodeCheckVersion 10 + // Request: + // Reply: + +#define kRMxOpCodeCopyProperty 11 + // Request: + // Reply: + +#define kRMxOpCodeEnumerateDomains 100 + // Request: + // Reply: + +#define kRMxOpCodeRegister 200 + // Request: + // Reply: + +#define kRMxOpCodeAddRecord 201 + // Request: + // Reply: no reply + +#define kRMxOpCodeUpdateRecord 202 + // Request: + // Reply: no reply + +#define kRMxOpCodeRemoveRecord 203 + // Request: + // Reply: no reply + +#define kRMxOpCodeBrowse 300 + // Request: + // Reply: + +#define kRMxOpCodeResolve 400 + // Request: + // Reply: + +#define kRMxOpCodeCreateConnection 500 + // Request: no data + // Reply: no reply + +#define kRMxOpCodeRegisterRecord 510 + // Request: + // Reply: + +#define kRMxOpCodeQueryRecord 511 + // Request: + // Reply: + +#define kRMxOpCodeReconfirmRecord 512 + // Request: + // Reply: no reply + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef RMxMessageCallBack + + @abstract Callback function for a message. +*/ + +typedef struct RMxMessage RMxMessage; + +typedef void ( *RMxMessageCallBack )( RMxMessage *inMessage ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef RMxSessionRef + + @abstract References to a session. +*/ + +typedef struct RMxSession * RMxSessionRef; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct RMxMessage + + @abstract RMx message. + + @field signature Signature identifying message as an RMx message (and for future versioning). + @field opcode Operation code of the message. + @field flags Flags for the message. + @field xid Transaction ID. WARNING: Must be unique within execution environment (e.g. process). + @field status Status of the message (used to return error replies). + + @field sendSize Size of data to send. + @field sendData Data to send. + @field recvSize Size of received data. + @field recvData Received data. + + @field context Context for the completion callback (specified when session started). + @field session Session where the message is sent/received. + + @field bufferSize PRIVATE: Size of entire message buffer or 0 if message not constructed. + @field buffer PRIVATE: Ptr to entire message buffer or NULL if message not constructed. + @field storage PRIVATE: Local storage for small messages. + + @constant kRMxMessageSynchronous Meta-message indicating operation should be performed synchronously. + @constant kRMxMessageHeaderSize Size of the header section of an RMx message packet. + @constant kRMxMessageInlineBufferSize Size of the inline message buffer. + + @discussion + + Meanings of the field descriptions: + + PRIVATE - Indicates field is private and should not be touched. + IN - Input field that must be set up before passing the message to RMx. + OUT - Output field that whose value on input will be ignore and replaced with a new value on completion. + + When a message is passed to RMx, the message becomes the ownership of RMx until completion of the message. + No fields of the messages should be touched while a message is in use by RMx. + + When sending messages asynchronously, the underlying storage for the message must be valid for the duration + of the message. This means stack-based storage cannot be for asynchronous messages because when the function + that allocated the stack-based variable returns, the message storage will go out of scope and become invalid. +*/ + +#define kRMxMessageHeaderSize 24 // +#define kRMxMessageInlineBufferSize 1024 + +check_compile_time( kRMxMessageHeaderSize <= kRMxMessageInlineBufferSize ); + +struct RMxMessage +{ + RMxSignature signature; + RMxOpCode opcode; + RMxFlags flags; + uint32_t xid; + OSStatus status; + + uint32_t sendSize; + uint8_t * sendData; + uint32_t recvSize; + uint8_t * recvData; + + void * context; + RMxSessionRef session; + + // PRIVATE (internal use only) + + uint32_t bufferSize; + uint8_t * buffer; + uint8_t storage[ kRMxMessageInlineBufferSize ]; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxMessageInitialize + + @abstract Initializes an RMx message structure. Must be called before the message structure can be used. +*/ + +void RMxMessageInitialize( RMxMessage *inMessage ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxMessageRelease + + @abstract Releases the resources used by an RMx message. +*/ + +void RMxMessageRelease( RMxMessage *inMessage ); + +#if 0 +#pragma mark == Sessions == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef RMxSessionFlags + + @abstract Session flags. + + @constant kRMxSessionFlagsNone No flags. + @constant kRMxSessionFlagServer Session is operating in server mode (replying to requests). + @constant kRMxSessionFlagsNoThread Do not create a thread to process messages (i.e. one-shot sync session). + @constant kRMxSessionFlagsThreadDone Thread is no longer active. + @constant kRMxSessionFlagsNoClose Do not close the session when the thread exits. +*/ + +typedef uint32_t RMxSessionFlags; + +#define kRMxSessionFlagsNone 0 +#define kRMxSessionFlagsServer ( 1 << 0 ) +#define kRMxSessionFlagsNoThread ( 1 << 1 ) +#define kRMxSessionFlagsThreadDone ( 1 << 2 ) +#define kRMxSessionFlagsNoClose ( 1 << 3 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @struct RMxSession + + @abstract Data structure for a session. +*/ + +typedef struct RMxSession RMxSession; +struct RMxSession +{ + RMxSessionRef next; + RMxSessionFlags flags; + SocketRef sock; + HANDLE sockEvent; + HANDLE closeEvent; + HANDLE thread; + unsigned threadID; + DWORD waitCount; + HANDLE waitHandles[ 3 ]; + bool quit; + + RMxMessageCallBack callback; + RMxMessage message; + uint8_t * messageRecvBuffer; + uint8_t * messageRecvBufferPtr; + int messageRecvRemaining; + bool messageRecvHeaderDone; +}; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxSessionOpen + + @abstract Opens a session and optionally sends a formatted message. +*/ + +OSStatus + RMxSessionOpen( + const char * inServer, + RMxSessionFlags inFlags, + SocketRef inSock, + RMxMessageCallBack inCallBack, + void * inContext, + RMxSessionRef * outSession, + RMxOpCode inMessageOpCode, + const char * inMessageFormat, + ... ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxSessionClose + + @abstract Closes a session. +*/ + +OSStatus RMxSessionClose( RMxSessionRef inSession, OSStatus inReason ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxSessionSendMessage + + @abstract Sends a formatted message. +*/ + +OSStatus RMxSessionSendMessage( RMxSessionRef inSession, RMxOpCode inOpCode, OSStatus inStatus, const char *inFormat, ... ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxSessionRecvMessage + + @abstract Synchronously receives a message. +*/ + +OSStatus RMxSessionRecvMessage( RMxSessionRef inSession, DWORD inTimeout ); + +#if 0 +#pragma mark == Utilities == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxCheckVersion + + @abstract Compares client and server version information and returns if they are compatible. +*/ + +OSStatus + RMxCheckVersion( + uint32_t inClientCurrentVersion, uint32_t inClientOldestClientVersion, uint32_t inClientOldestServerVersion, + uint32_t inServerCurrentVersion, uint32_t inServerOldestClientVersion, uint32_t inServerOldestServerVersion ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxPackedSize + + @abstract Determines the number of bytes required to pack data using the specified format string and arguments. +*/ + +OSStatus RMxPackedSize( size_t *outSize, const char *inFormat, ... ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxPackedSizeVAList + + @abstract Determines the number of bytes required to pack data using the format string and argument list. +*/ + +OSStatus RMxPackedSizeVAList( size_t *outSize, const char *inFormat, va_list inArgs ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxPack + + @abstract Packs data into a buffer using the format string and arguments. +*/ + +OSStatus RMxPack( void *inBuffer, size_t inMaxSize, size_t *outSize, const char *inFormat, ... ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxPackVAList + + @abstract Packs data into a buffer using the format string and argument list. +*/ + +OSStatus RMxPackVAList( void *inBuffer, size_t inMaxSize, size_t *outSize, const char *inFormat, va_list inArgs ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxUnpack + + @abstract Unpacks data from a buffer using the format string and arguments. +*/ + +OSStatus RMxUnpack( const void *inData, size_t inSize, const char *inFormat, ... ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxUnpackVAList + + @abstract Unpacks data from a buffer using the format string and argument list. +*/ + +OSStatus RMxUnpackVAList( const void *inData, size_t inSize, const char *inFormat, va_list inArgs ); + +#ifdef __cplusplus + } +#endif + +#endif // __RMx_COMMON__ diff --git a/mDNSWindows/RMxServer.c b/mDNSWindows/RMxServer.c new file mode 100644 index 0000000..3c28089 --- /dev/null +++ b/mDNSWindows/RMxServer.c @@ -0,0 +1,1656 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: RMxServer.c,v $ +Revision 1.6 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.5 2004/04/09 21:03:14 bradley +Changed port numbers to use network byte order for consistency with other platforms. + +Revision 1.4 2004/04/08 21:14:19 bradley +Changed resolve callback to use correct calling conventions (previously hidden by a cast). + +Revision 1.3 2004/04/08 09:43:43 bradley +Changed callback calling conventions to __stdcall so they can be used with C# delegates. + +Revision 1.2 2004/03/16 22:09:03 bradley +Skip socket creation failures to handle local IPv6 addresses being returned by Windows even when +they are not actually supported by the OS; Log a message and only fail if no sockets can be created. + +Revision 1.1 2004/01/30 02:35:13 bradley +Rendezvous Message Exchange implementation for DNS-SD IPC on Windows. + +*/ + +#include +#include +#include + +#include "CommonServices.h" +#include "DebugServices.h" + +#include "DNSSD.h" +#include "DNSSDDirect.h" +#include "RMxCommon.h" + +#include "RMxServer.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[RMxServer] " + +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== + +typedef void ( *DNSServiceRefReleaseCallBack )( DNSServiceRef inRef ); + +// DNSServiceRef + +typedef struct _DNSServiceRef_t _DNSServiceRef_t; +struct _DNSServiceRef_t +{ + RMxOpCode opcode; + bool sessionClosing; + RMxSessionRef session; + DNSServiceRef directObj; + DNSRecordRef records; +}; + +// DNSRecordRef + +typedef struct _DNSRecordRef_t _DNSRecordRef_t; +struct _DNSRecordRef_t +{ + DNSRecordRef next; + uint32_t id; + DNSServiceRef owner; + DNSRecordRef directObj; +}; + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +// RMx + +DEBUG_LOCAL OSStatus RMxServerRunInitialize( void ); +DEBUG_LOCAL void RMxServerRunFinalize( void ); +DEBUG_LOCAL void RMxServerMessageCallBack( RMxMessage *inMessage ); + +// General + +DEBUG_LOCAL void DNSServiceRefDeallocate_server( DNSServiceRef inRef ); +DEBUG_LOCAL void DNSServiceCheckVersion_server( RMxMessage *inMessage ); + +// Properties + +DEBUG_LOCAL void DNSServiceCopyProperty_server( RMxMessage *inMessage ); +DEBUG_LOCAL OSStatus DNSServiceCopyPropertySendData_server( DNSPropertyCode inCode, RMxMessage *inMessage ); + +// Domain Enumeration + +DEBUG_LOCAL void DNSServiceEnumerateDomains_server( RMxMessage *inMessage ); +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceEnumerateDomainsCallBack_server( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char * replyDomain, + void * context ); + +// Service Registration + +DEBUG_LOCAL void DNSServiceRegister_server( RMxMessage *inMessage ); +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceRegisterCallBack_server( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + +DEBUG_LOCAL void DNSServiceAddRecord_server( RMxMessage *inMessage ); +DEBUG_LOCAL void DNSServiceUpdateRecord_server( RMxMessage *inMessage ); +DEBUG_LOCAL void DNSServiceRemoveRecord_server( RMxMessage *inMessage ); + +// Service Discovery + +DEBUG_LOCAL void DNSServiceBrowse_server( RMxMessage *inMessage ); +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceBrowseCallBack_server( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + +DEBUG_LOCAL void DNSServiceResolve_server( RMxMessage *inMessage ); +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceResolveCallBack_server( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ); + +// Special Purpose + +DEBUG_LOCAL void DNSServiceCreateConnection_server( RMxMessage *inMessage ); + +DEBUG_LOCAL void DNSServiceRegisterRecord_server( RMxMessage *inMessage ); +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceRegisterRecordCallBack_server( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + void * inContext ); + +DEBUG_LOCAL void DNSServiceQueryRecord_server( RMxMessage *inMessage ); +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceQueryRecordCallBack_server( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ); + +DEBUG_LOCAL void DNSServiceReconfirmRecord_server( RMxMessage *inMessage ); + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +DEBUG_LOCAL RMxServerFlags gRMxServerFlags = kRMxServerFlagsNone; +DEBUG_LOCAL SocketRef gRMxServerSockets[ 2 ] = { kInvalidSocketRef, kInvalidSocketRef }; +DEBUG_LOCAL HANDLE gRMxServerSocketEvents[ 2 ] = { NULL, NULL }; +DEBUG_LOCAL HANDLE gRMxServerQuitDoneEvent = NULL; + +#if 0 +#pragma mark - +#pragma mark == RMx == +#endif + +//=========================================================================================================================== +// RMxServerInitialize +//=========================================================================================================================== + +OSStatus RMxServerInitialize( RMxServerFlags inFlags ) +{ + OSStatus err; + + err = RMxInitialize(); + require_noerr( err, exit ); + + // Set up the quit done event to allow for waiting until the run loop is quit. A manual-reset event is used so + // multiple calls to the stopping code will all see that the quit event has been signaled (not just the first). + // The event is also created as signaled so even if run is not called, a stop or finalize will still run successfully. + // The event will be explicitly reset to an unsignaled state when the run loop starts. + + gRMxServerQuitDoneEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + err = translate_errno( gRMxServerQuitDoneEvent, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + gRMxServerFlags = inFlags; + +exit: + return( err ); +} + +//=========================================================================================================================== +// RMxServerFinalize +//=========================================================================================================================== + +void RMxServerFinalize( void ) +{ + BOOL ok; + + RMxServerStop( kRMxServerStopFlagsNone ); + + // Release the quit event. + + if( gRMxServerQuitDoneEvent ) + { + ok = CloseHandle( gRMxServerQuitDoneEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + gRMxServerQuitDoneEvent = NULL; + } + + RMxFinalize(); +} + +//=========================================================================================================================== +// RMxServerRun +//=========================================================================================================================== + +OSStatus RMxServerRun( void ) +{ + OSStatus err; + DWORD waitCount; + HANDLE waitHandles[ 3 ]; + + // Initialize the server run loop and set up the list of objects to wait for. + + err = RMxServerRunInitialize(); + require_noerr( err, exit ); + + waitCount = 0; + waitHandles[ waitCount++ ] = gRMxServerSocketEvents[ 0 ]; + waitHandles[ waitCount++ ] = gRMxServerSocketEvents[ 1 ]; + waitHandles[ waitCount++ ] = gRMxStateChangeEvent; + check( waitCount == sizeof_array( waitHandles ) ); + + // Main event loop. Process connection requests and state changes (i.e. quit). + + while( gRMxState == kRMxStateRun ) + { + DWORD result; + + result = WaitForMultipleObjects( waitCount, waitHandles, FALSE, INFINITE ); + if( ( result == WAIT_OBJECT_0 ) || + ( result == ( WAIT_OBJECT_0 + 1 ) ) ) // Socket Events + { + SocketRef sock; + struct sockaddr_storage addr; + socklen_t addrSize; + DWORD index; + + index = result - WAIT_OBJECT_0; + check( ( index == 0 ) || IsValidSocket( gRMxServerSockets[ index ] ) ); + + // The socket event is only set up to notify on connection requests so try to accept the connection. + // Note: It is possible for accept to fail if the client aborts the connection after the network stack + // sees the connection request, but before accept is called. See UNPv1 section 15.6 for more information. + + addrSize = sizeof( addr ); + sock = accept( gRMxServerSockets[ index ], (struct sockaddr *) &addr, &addrSize ); + if( IsValidSocket( sock ) ) + { + if( ( gRMxServerFlags & kRMxServerFlagsAllowRemote ) || SOCKADDR_IS_IP_LOOPBACK( &addr ) ) + { + dlog( kDebugLevelNotice, "\n" DEBUG_NAME "accepting connection from %##a\n", &addr ); + + err = RMxSessionOpen( NULL, kRMxSessionFlagsNone, sock, RMxServerMessageCallBack, NULL, NULL, + kRMxOpCodeInvalid, NULL ); + check_noerr( err ); + } + else + { + dlog( kDebugLevelNotice, DEBUG_NAME "remote connection attempt from %##a when local-only\n", &addr ); + + err = close_compat( sock ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + } + } + else + { + err = errno_compat(); + dlog( kDebugLevelWarning, DEBUG_NAME "connection abort before accept (%d %m)\n", err, err ); + } + } + else if( result == ( WAIT_OBJECT_0 + 2 ) ) // State Change Event + { + dlog( kDebugLevelNotice, DEBUG_NAME "state change (%d)\n", gRMxState ); + break; + } + else + { + err = (OSStatus) GetLastError(); + dlog( kDebugLevelAlert, DEBUG_NAME "wait error: 0x%08X, %d (%m)\n", result, err, err ); + err = (OSStatus) result; + goto exit; + } + } + err = kNoErr; + +exit: + RMxServerRunFinalize(); + return( err ); +} + +//=========================================================================================================================== +// RMxServerRunInitialize +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus RMxServerRunInitialize( void ) +{ + OSStatus err; + struct addrinfo hints; + struct addrinfo * addrList; + struct addrinfo * addr; + int nAddrs; + BOOL ok; + int option; + + addrList = NULL; + + // Set up a socket for each address (only expected to be a single IPv4 and optionally a single IPv6 address). + // Windows does not support IPv4-mapped IPv6 addresses so we have to create a separate socket for each type. + + memset( &hints, 0, sizeof( hints ) ); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + err = getaddrinfo( NULL, kRMxServerPortString, &hints, &addrList ); + require_noerr( err, exit ); + + nAddrs = 0; + for( addr = addrList; addr; addr = addr->ai_next ) + { + dlog( kDebugLevelTrace, DEBUG_NAME "setting up %s socket\n", + ( addr->ai_family == AF_INET ) ? "AF_INET" : ( addr->ai_family == AF_INET6 ) ? "AF_INET6" : "" ); + if( nAddrs > 1 ) + { + dlog( kDebugLevelWarning, DEBUG_NAME "cannot handle more than 2 listening sockets (unexpected)\n" ); + break; + } + + gRMxServerSockets[ nAddrs ] = socket( addr->ai_family, addr->ai_socktype, addr->ai_protocol ); + err = translate_errno( IsValidSocket( gRMxServerSockets[ nAddrs ] ), errno_compat(), kNoResourcesErr ); + if( err != kNoErr ) + { + dlog( kDebugLevelNotice, DEBUG_NAME "%s socket not supported (%d %m)\n", + ( addr->ai_family == AF_INET ) ? "AF_INET" : ( addr->ai_family == AF_INET6 ) ? "AF_INET6" : "", + err, err ); + continue; + } + + // Enable the address-reuse option so the server can be stopped and re-started without the TIME_WAIT delay. + + option = 1; + err = setsockopt( gRMxServerSockets[ nAddrs ], SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) ); + err = translate_errno( err == 0, errno_compat(), kInUseErr ); + require_noerr( err, exit ); + + // Set up to signal an event when a connection can be accepted so we can wait for it with WaitForMultipleObjects. + // Note: WSAEventSelect makes the socket non-blocking so it does not need to made non-blocking separately. + + gRMxServerSocketEvents[ nAddrs ] = CreateEvent( NULL, FALSE, FALSE, NULL ); + err = translate_errno( gRMxServerSocketEvents[ nAddrs ], errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + err = WSAEventSelect( gRMxServerSockets[ nAddrs ], gRMxServerSocketEvents[ nAddrs ], FD_ACCEPT ); + err = translate_errno( err == 0, errno_compat(), kInUseErr ); + require_noerr( err, exit ); + + // Bind to the server port and enable listening for connections. + + err = bind( gRMxServerSockets[ nAddrs ], addr->ai_addr, (int) addr->ai_addrlen ); + err = translate_errno( err == 0, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + err = listen( gRMxServerSockets[ nAddrs ], SOMAXCONN ); + err = translate_errno( err == 0, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + ++nAddrs; + } + require_action( nAddrs > 0, exit, err = kUnknownErr ); + + // Create a dummy event (which will never be signaled) to simplify WaitForMultipleObjects usage, + + if( !gRMxServerSocketEvents[ 1 ] ) + { + gRMxServerSocketEvents[ 1 ] = CreateEvent( NULL, FALSE, FALSE, NULL ); + err = translate_errno( gRMxServerSocketEvents[ 1 ], errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + } + + // Reset the quit event to an unsignaled state so other code can wait for the run loop to quit. + + gRMxState = kRMxStateRun; + ok = ResetEvent( gRMxServerQuitDoneEvent ); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + +exit: + if( addrList ) + { + freeaddrinfo( addrList ); + } + if( err != kNoErr ) + { + RMxServerRunFinalize(); + } + return( err ); +} + +//=========================================================================================================================== +// RMxServerRunFinalize +// +// Note: This routine is safe to call multiple times (for easier cleanup). +//=========================================================================================================================== + +DEBUG_LOCAL void RMxServerRunFinalize( void ) +{ + OSStatus err; + BOOL ok; + + // Release the socket events + + if( gRMxServerSocketEvents[ 0 ] ) + { + ok = CloseHandle( gRMxServerSocketEvents[ 0 ] ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + gRMxServerSocketEvents[ 0 ] = NULL; + } + if( gRMxServerSocketEvents[ 1 ] ) + { + ok = CloseHandle( gRMxServerSocketEvents[ 1 ] ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + gRMxServerSocketEvents[ 1 ] = NULL; + } + + // Close the sockets. + + if( IsValidSocket( gRMxServerSockets[ 0 ] ) ) + { + err = close_compat( gRMxServerSockets[ 0 ] ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + gRMxServerSockets[ 0 ] = kInvalidSocketRef; + } + if( IsValidSocket( gRMxServerSockets[ 1 ] ) ) + { + err = close_compat( gRMxServerSockets[ 1 ] ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + gRMxServerSockets[ 1 ] = kInvalidSocketRef; + } + + // Signal the quit event to indicate the run loop has quit. + + ok = SetEvent( gRMxServerQuitDoneEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); +} + +//=========================================================================================================================== +// RMxServerStop +//=========================================================================================================================== + +OSStatus RMxServerStop( RMxServerStopFlags inFlags ) +{ + BOOL ok; + DWORD result; + + // Signal a state changed event to trigger everything to stop. Note: This is a manual-reset event so it will + // remain signaled to allow all running threads to wake up and exit cleanly. + + gRMxState = kRMxStateStop; + if( gRMxStateChangeEvent ) + { + ok = SetEvent( gRMxStateChangeEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + } + + // Wait for the quit event indicating the run loop has quit. Give up after 10 seconds to handle a hung thread. + + if( !( inFlags & kRMxServerStopFlagsNoWait ) ) + { + if( gRMxServerQuitDoneEvent ) + { + result = WaitForSingleObject( gRMxServerQuitDoneEvent, 10 * 1000 ); + check_translated_errno( result == WAIT_OBJECT_0, errno_compat(), result ); + } + } + + return( kNoErr ); +} + +//=========================================================================================================================== +// RMxServerMessageCallBack +//=========================================================================================================================== + +DEBUG_LOCAL void RMxServerMessageCallBack( RMxMessage *inMessage ) +{ + DNSServiceRef obj; + OSStatus err; + + check( inMessage ); + check( inMessage->session ); + + switch( inMessage->opcode ) + { + // General + + case kRMxOpCodeInvalid: + + // The session is closing so delete the object (if still valid). Invalidate the session because the session + // is already in the process of closing so we don't want the deallocate code closing it again. + + obj = (DNSServiceRef) inMessage->context; + if( obj ) + { + obj->sessionClosing = true; + DNSServiceRefDeallocate_server( obj ); + } + break; + + case kRMxOpCodeCheckVersion: + DNSServiceCheckVersion_server( inMessage ); + break; + + // Properties + + case kRMxOpCodeCopyProperty: + DNSServiceCopyProperty_server( inMessage ); + break; + + // Domain Enumeration + + case kRMxOpCodeEnumerateDomains: + DNSServiceEnumerateDomains_server( inMessage ); + break; + + // Service Registration + + case kRMxOpCodeRegister: + DNSServiceRegister_server( inMessage ); + break; + + case kRMxOpCodeAddRecord: + DNSServiceAddRecord_server( inMessage ); + break; + + case kRMxOpCodeUpdateRecord: + DNSServiceUpdateRecord_server( inMessage ); + break; + + case kRMxOpCodeRemoveRecord: + DNSServiceRemoveRecord_server( inMessage ); + break; + + // Service Discovery + + case kRMxOpCodeBrowse: + DNSServiceBrowse_server( inMessage ); + break; + + case kRMxOpCodeResolve: + DNSServiceResolve_server( inMessage ); + break; + + // Special Purpose + + case kRMxOpCodeCreateConnection: + DNSServiceCreateConnection_server( inMessage ); + break; + + case kRMxOpCodeRegisterRecord: + DNSServiceRegisterRecord_server( inMessage ); + break; + + case kRMxOpCodeQueryRecord: + DNSServiceQueryRecord_server( inMessage ); + break; + + case kRMxOpCodeReconfirmRecord: + DNSServiceReconfirmRecord_server( inMessage ); + break; + + default: + dlog( kDebugLevelWarning, DEBUG_NAME "message with unknown opcode received (%d)\n", inMessage->opcode ); + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, kUnsupportedErr, "" ); + check_noerr( err ); + break; + } +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD General == +#endif + +//=========================================================================================================================== +// DNSServiceRefDeallocate_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRefDeallocate_server( DNSServiceRef inRef ) +{ + OSStatus err; + + check( inRef ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: ref=%#p\n", __ROUTINE__, inRef ); + + // Release the direct object if active. + + if( inRef->directObj ) + { + DNSServiceRefDeallocate_direct( inRef->directObj ); + } + + // Close the session. + + if( inRef->session && !inRef->sessionClosing ) + { + inRef->session->callback = NULL; + inRef->session->message.context = NULL; + + err = RMxSessionClose( inRef->session, kEndingErr ); + check_noerr( err ); + } + + // Release any extra records. + + while( inRef->records ) + { + DNSRecordRef record; + + record = inRef->records; + inRef->records = record->next; + + free( record ); + } + + // Release the object. + + free( inRef ); +} + +//=========================================================================================================================== +// DNSServiceCheckVersion_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceCheckVersion_server( RMxMessage *inMessage ) +{ + OSStatus err; + uint32_t clientCurrentVersion; + uint32_t clientOldestClientVersion; + uint32_t clientOldestServerVersion; + + check( inMessage ); + check( inMessage->session ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "www", + &clientCurrentVersion, &clientOldestClientVersion, &clientOldestServerVersion ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: clientCurrent=%v, clientOldestClient=%v, clientOldestServer=%v\n", + __ROUTINE__, clientCurrentVersion, clientOldestClientVersion, clientOldestServerVersion ); + + err = RMxCheckVersion( clientCurrentVersion, clientOldestClientVersion, clientOldestServerVersion, + kRMxCurrentVersion, kRMxOldestClientVersion, kRMxOldestServerVersion ); + + // Send the response and our version information. Cause the session to quit since this is a one-shot session. + +exit: + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, kNoErr, "wwww", + err, kRMxCurrentVersion, kRMxOldestClientVersion, kRMxOldestServerVersion ); + check_noerr( err ); + + inMessage->session->quit = true; +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Properties == +#endif + +//=========================================================================================================================== +// DNSServiceCopyProperty_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceCopyProperty_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSPropertyCode code; + + check( inMessage ); + check( inMessage->session ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "w", &code ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: code='%C'\n", __ROUTINE__, code ); + + + // Send the response and our version information. Cause the session to quit since this is a one-shot session. + +exit: + err = DNSServiceCopyPropertySendData_server( code, inMessage ); + check_noerr( err ); + + inMessage->session->quit = true; +} + +//=========================================================================================================================== +// DNSServiceCopyPropertySendData_server +//=========================================================================================================================== + +DEBUG_LOCAL OSStatus DNSServiceCopyPropertySendData_server( DNSPropertyCode inCode, RMxMessage *inMessage ) +{ + OSStatus err; + + switch( inCode ) + { + case kDNSPropertyCodeVersion: + + // Note: This manually builds the "n" data type using a "w" size followed by the data to avoid a temporary buffer. + + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, kNoErr, "ww wwww", kNoErr, inCode, + 4 + 4 + 4, kRMxCurrentVersion, kRMxOldestClientVersion, kRMxOldestServerVersion ); + require_noerr( err, exit ); + break; + + default: + dlog( kDebugLevelWarning, DEBUG_NAME "%s: unknown property code (%C)\n", __ROUTINE__, inCode ); + err = kDNSServiceErr_Unsupported; + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, kNoErr, "wn", err, 0, NULL ); + require_noerr( err, exit ); + break; + } + err = kNoErr; + +exit: + return( err ); +} + +#if 0 +#pragma mark DNSServiceReleaseProperty_server +#endif + +// Note: DNSServiceReleaseProperty_server is not needed on the server side because memory is released immediately after sending. + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Domain Enumeration == +#endif + +//=========================================================================================================================== +// DNSServiceEnumerateDomains_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceEnumerateDomains_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + + obj = NULL; + check( inMessage ); + check( inMessage->session ); + require_action( !inMessage->session->message.context, exit, err = kTypeErr ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "ww", &flags, &interfaceIndex ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: flags=0x%08X, ifi=%d\n", __ROUTINE__, flags, interfaceIndex ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->opcode = inMessage->opcode; + obj->session = inMessage->session; + + err = DNSServiceEnumerateDomains_direct( &obj->directObj, flags, interfaceIndex, + DNSServiceEnumerateDomainsCallBack_server, obj ); + require_noerr( err, exit ); + + // Success! + + inMessage->session->message.context = obj; + obj = NULL; + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } + if( obj ) + { + DNSServiceRefDeallocate_server( obj ); + } +} + +//=========================================================================================================================== +// DNSServiceEnumerateDomainsCallBack_server +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceEnumerateDomainsCallBack_server( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inDomain, + void * inContext ) +{ + DNSServiceRef obj; + OSStatus err; + + DEBUG_UNUSED( inRef ); + + check( inDomain ); + obj = (DNSServiceRef) inContext; + check( obj ); + check( obj->session ); + + err = RMxSessionSendMessage( obj->session, kRMxOpCodeEnumerateDomains, kNoErr, "wwws", + inFlags, inInterfaceIndex, inErrorCode, inDomain ); + check_noerr( err ); +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Service Registration == +#endif + +//=========================================================================================================================== +// DNSServiceRegister_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRegister_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + const char * name; + const char * type; + const char * domain; + const char * host; + uint16_t port; + const void * txt; + size_t txtSize; + + obj = NULL; + check( inMessage ); + check( inMessage->session ); + require_action( !inMessage->session->message.context, exit, err = kTypeErr ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwsssshn", + &flags, &interfaceIndex, &name, NULL, &type, NULL, &domain, NULL, &host, NULL, &port, &txt, &txtSize ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME + "%s: flags=0x%08X, ifi=%ld, name=\"%s\", type=\"%s\", domain=\"%s\", host=\"%s\", port=%d, txtSize=%d\n", + __ROUTINE__, flags, interfaceIndex, name, type, domain, host, ntohs( port ), txtSize ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->opcode = inMessage->opcode; + obj->session = inMessage->session; + + err = DNSServiceRegister_direct( &obj->directObj, flags, interfaceIndex, name, type, domain, host, port, + (uint16_t) txtSize, txt, DNSServiceRegisterCallBack_server, obj ); + require_noerr( err, exit ); + + // Success! + + inMessage->session->message.context = obj; + obj = NULL; + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } + if( obj ) + { + DNSServiceRefDeallocate_server( obj ); + } +} + +//=========================================================================================================================== +// DNSServiceRegisterCallBack_server +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceRegisterCallBack_server( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + DNSServiceRef obj; + OSStatus err; + + DEBUG_UNUSED( inRef ); + + check( inDomain ); + obj = (DNSServiceRef) inContext; + check( obj ); + check( obj->session ); + + err = RMxSessionSendMessage( obj->session, kRMxOpCodeRegister, kNoErr, "wwsss", + inFlags, inErrorCode, inName, inType, inDomain ); + check_noerr( err ); +} + +//=========================================================================================================================== +// DNSServiceAddRecord_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceAddRecord_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSRecordRef obj; + DNSServiceRef ref; + uint32_t id; + DNSServiceFlags flags; + uint16_t rrType; + uint8_t * rData; + size_t rDataSize; + uint32_t ttl; + DNSRecordRef * p; + + obj = NULL; + check( inMessage ); + check( inMessage->session ); + ref = (DNSServiceRef) inMessage->context; + require_action( ref, exit, err = kNotInitializedErr ); + require_action( ref->opcode == kRMxOpCodeRegister, exit, err = kTypeErr ); + check( ref->directObj ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwhnw", &id, &flags, &rrType, &rData, &rDataSize, &ttl ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: id=%d, flags=0x%08X, rrType=%d, rDataSize=%d, ttl=%d\n", + __ROUTINE__, id, flags, rrType, rDataSize, ttl ); + + // Allocate and initialize the object and add it to the list of records. + + obj = (DNSRecordRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->id = id; + + obj->next = ref->records; + ref->records = obj; + + err = DNSServiceAddRecord_direct( ref->directObj, &obj->directObj, flags, rrType, (uint16_t) rDataSize, rData, ttl ); + require_noerr( err, exit ); + + obj = NULL; + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } + if( obj ) + { + for( p = &ref->records; *p; p = &( *p )->next ) + { + if( *p == obj ) + { + *p = obj->next; + free( obj ); + break; + } + } + } +} + +//=========================================================================================================================== +// DNSServiceUpdateRecord_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceUpdateRecord_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef ref; + uint32_t id; + DNSServiceFlags flags; + uint8_t * rData; + size_t rDataSize; + uint32_t ttl; + DNSRecordRef obj; + + check( inMessage ); + check( inMessage->session ); + ref = (DNSServiceRef) inMessage->context; + require_action( ref, exit, err = kNotInitializedErr ); + require_action( ( ref->opcode == kRMxOpCodeRegister ) || ( ref->opcode == kRMxOpCodeCreateConnection ), exit, err = kTypeErr ); + check( ref->directObj ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwnw", &id, &flags, &rData, &rDataSize, &ttl ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: id=%d, flags=0x%08X, rDataSize=%d, ttl=%d\n", __ROUTINE__, id, flags, rDataSize, ttl ); + + // Find the record with the specified ID and update it. + + if( id == kDNSRecordIndexDefaultTXT ) + { + obj = NULL; + } + else + { + for( obj = ref->records; obj; obj = obj->next ) + { + if( obj->id == id ) + { + break; + } + } + require_action( obj, exit, err = kNotFoundErr ); + check( obj->directObj ); + } + + err = DNSServiceUpdateRecord_direct( ref->directObj, obj->directObj, flags, (uint16_t) rDataSize, rData, ttl ); + require_noerr( err, exit ); + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } +} + +//=========================================================================================================================== +// DNSServiceRemoveRecord_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRemoveRecord_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef ref; + uint32_t id; + DNSServiceFlags flags; + DNSRecordRef * p; + DNSRecordRef obj; + DNSRecordRef directObj; + + check( inMessage ); + check( inMessage->session ); + ref = (DNSServiceRef) inMessage->context; + require_action( ref, exit, err = kNotInitializedErr ); + require_action( ( ref->opcode == kRMxOpCodeRegister ) || ( ref->opcode == kRMxOpCodeCreateConnection ), exit, err = kTypeErr ); + check( ref->directObj ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "ww", &id, &flags ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: id=%d, flags=0x%08X\n", __ROUTINE__, id, flags ); + + // Find the record with the specified ID, remove it from the list, and free it. + + for( p = &ref->records; *p; p = &( *p )->next ) + { + if( ( *p )->id == id ) + { + break; + } + } + obj = *p; + require_action( obj, exit, err = kNotFoundErr ); + *p = obj->next; + + directObj = obj->directObj; + check( directObj ); + free( obj ); + + err = DNSServiceRemoveRecord_direct( ref->directObj, directObj, flags ); + require_noerr( err, exit ); + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Service Discovery == +#endif + +//=========================================================================================================================== +// DNSServiceBrowse_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceBrowse_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + const char * type; + const char * domain; + + obj = NULL; + check( inMessage ); + check( inMessage->session ); + require_action( !inMessage->session->message.context, exit, err = kTypeErr ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwss", &flags, &interfaceIndex, &type, NULL, &domain, NULL ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: flags=0x%08X, ifi=%d, type=\"%s\", domain=\"%s\"\n", + __ROUTINE__, flags, interfaceIndex, type, domain ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->opcode = inMessage->opcode; + obj->session = inMessage->session; + + err = DNSServiceBrowse_direct( &obj->directObj, flags, interfaceIndex, type, domain, DNSServiceBrowseCallBack_server, obj ); + require_noerr( err, exit ); + + // Success! + + inMessage->session->message.context = obj; + obj = NULL; + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } + if( obj ) + { + DNSServiceRefDeallocate_server( obj ); + } +} + +//=========================================================================================================================== +// DNSServiceBrowseCallBack_server +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceBrowseCallBack_server( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + DNSServiceRef obj; + OSStatus err; + + DEBUG_UNUSED( inRef ); + + check( inName ); + check( inType ); + check( inDomain ); + obj = (DNSServiceRef) inContext; + check( obj ); + check( obj->session ); + + err = RMxSessionSendMessage( obj->session, kRMxOpCodeBrowse, kNoErr, "wwwsss", + inFlags, inInterfaceIndex, inErrorCode, inName, inType, inDomain ); + check_noerr( err ); +} + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// DNSServiceResolve_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceResolve_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + const char * name; + const char * type; + const char * domain; + + obj = NULL; + check( inMessage ); + check( inMessage->session ); + require_action( !inMessage->session->message.context, exit, err = kTypeErr ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwsss", + &flags, &interfaceIndex, &name, NULL, &type, NULL, &domain, NULL ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: flags=0x%08X, ifi=%d, name=\"%s\", type=\"%s\", domain=\"%s\"\n", + __ROUTINE__, flags, interfaceIndex, name, type, domain ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->opcode = inMessage->opcode; + obj->session = inMessage->session; + + err = DNSServiceResolve_direct( &obj->directObj, flags, interfaceIndex, name, type, domain, + DNSServiceResolveCallBack_server, obj ); + require_noerr( err, exit ); + + // Success! + + inMessage->session->message.context = obj; + obj = NULL; + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } + if( obj ) + { + DNSServiceRefDeallocate_server( obj ); + } +} + +//=========================================================================================================================== +// DNSServiceResolveCallBack_server +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceResolveCallBack_server( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inFullName, + const char * inHostName, + uint16_t inPort, + uint16_t inTXTSize, + const char * inTXT, + void * inContext ) +{ + DNSServiceRef obj; + OSStatus err; + + DEBUG_UNUSED( inRef ); + + check( inFullName ); + check( inHostName ); + obj = (DNSServiceRef) inContext; + dlog( kDebugLevelTrace, DEBUG_NAME "%s: ref=%#p, directRef=%#p\n", __ROUTINE__, obj, inRef ); + check( obj ); + check( obj->session ); + + err = RMxSessionSendMessage( obj->session, kRMxOpCodeResolve, kNoErr, "wwwsshn", + inFlags, inInterfaceIndex, inErrorCode, inFullName, inHostName, inPort, inTXTSize, inTXT ); + check_noerr( err ); +} + +#if 0 +#pragma mark - +#pragma mark == DNS-SD Special Purpose == +#endif + +//=========================================================================================================================== +// DNSServiceCreateConnection_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceCreateConnection_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + + obj = NULL; + check( inMessage ); + check( inMessage->session ); + require_action( !inMessage->session->message.context, exit, err = kTypeErr ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s\n", __ROUTINE__ ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->opcode = inMessage->opcode; + obj->session = inMessage->session; + + err = DNSServiceCreateConnection_direct( &obj->directObj ); + require_noerr( err, exit ); + + // Success! + + inMessage->session->message.context = obj; + obj = NULL; + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } + if( obj ) + { + DNSServiceRefDeallocate_server( obj ); + } +} + +//=========================================================================================================================== +// DNSServiceRegisterRecord_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceRegisterRecord_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSRecordRef obj; + DNSServiceRef ref; + uint32_t id; + DNSServiceFlags flags; + uint32_t interfaceIndex; + const char * name; + uint16_t rrType; + uint16_t rrClass; + uint8_t * rData; + size_t rDataSize; + uint32_t ttl; + DNSRecordRef * p; + + obj = NULL; + check( inMessage ); + check( inMessage->session ); + ref = (DNSServiceRef) inMessage->context; + require_action( ref, exit, err = kNotInitializedErr ); + require_action( ref->opcode == kRMxOpCodeCreateConnection, exit, err = kTypeErr ); + check( ref->directObj ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwwshhnw", + &id, &flags, &interfaceIndex, &name, NULL, &rrType, &rrClass, &rData, &rDataSize, &ttl ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME + "%s: id=%d, flags=0x%08X, ifi=%d, name=\"%s\", rrType=%d, rrClass=%d, rDataSize=%d, ttl=%d\n", + __ROUTINE__, id, flags, interfaceIndex, name, rrType, rrClass, rDataSize, ttl ); + + // Allocate and initialize the object and add it to the list of records. + + obj = (DNSRecordRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->id = id; + obj->owner = ref; + + obj->next = ref->records; + ref->records = obj; + + err = DNSServiceRegisterRecord_direct( ref->directObj, &obj->directObj, flags, interfaceIndex, name, rrType, rrClass, + (uint16_t) rDataSize, rData, ttl, DNSServiceRegisterRecordCallBack_server, obj ); + require_noerr( err, exit ); + + obj = NULL; + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } + if( obj ) + { + for( p = &ref->records; *p; p = &( *p )->next ) + { + if( *p == obj ) + { + *p = obj->next; + free( obj ); + break; + } + } + } +} + +//=========================================================================================================================== +// DNSServiceRegisterRecordCallBack_server +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceRegisterRecordCallBack_server( + DNSServiceRef inRef, + DNSRecordRef inRecordRef, + DNSServiceFlags inFlags, + DNSServiceErrorType inErrorCode, + void * inContext ) +{ + DNSRecordRef record; + DNSServiceRef obj; + OSStatus err; + + DEBUG_UNUSED( inRef ); + DEBUG_UNUSED( inRecordRef ); + + record = (DNSRecordRef) inContext; + check( record ); + obj = record->owner; + check( obj ); + check( obj->session ); + + err = RMxSessionSendMessage( obj->session, kRMxOpCodeRegisterRecord, kNoErr, "www", inFlags, inErrorCode, record->id ); + check_noerr( err ); +} + +//=========================================================================================================================== +// DNSServiceQueryRecord_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceQueryRecord_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceRef obj; + DNSServiceFlags flags; + uint32_t interfaceIndex; + const char * name; + uint16_t rrType; + uint16_t rrClass; + + obj = NULL; + check( inMessage ); + check( inMessage->session ); + require_action( !inMessage->session->message.context, exit, err = kTypeErr ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwshh", + &flags, &interfaceIndex, &name, NULL, &rrType, &rrClass ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, DEBUG_NAME "%s: flags=0x%08X, ifi=%d, name=\"%s\", rrType=%d, rrClass=%d\n", + __ROUTINE__, flags, interfaceIndex, name, rrType, rrClass ); + + // Allocate and initialize the object. + + obj = (DNSServiceRef) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->opcode = inMessage->opcode; + obj->session = inMessage->session; + + err = DNSServiceQueryRecord_direct( &obj->directObj, flags, interfaceIndex, name, rrType, rrClass, + DNSServiceQueryRecordCallBack_server, obj ); + require_noerr( err, exit ); + + // Success! + + inMessage->session->message.context = obj; + obj = NULL; + +exit: + if( err != kNoErr ) + { + err = RMxSessionSendMessage( inMessage->session, inMessage->opcode, err, "" ); + check_noerr( err ); + } + if( obj ) + { + DNSServiceRefDeallocate_server( obj ); + } +} + +//=========================================================================================================================== +// DNSServiceQueryRecordCallBack_server +//=========================================================================================================================== + +DEBUG_LOCAL void CALLBACK_COMPAT + DNSServiceQueryRecordCallBack_server( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + uint16_t inRRType, + uint16_t inRRClass, + uint16_t inRDataSize, + const void * inRData, + uint32_t inTTL, + void * inContext ) +{ + DNSServiceRef obj; + OSStatus err; + + DEBUG_UNUSED( inRef ); + + obj = (DNSServiceRef) inContext; + check( obj ); + check( obj->session ); + + err = RMxSessionSendMessage( obj->session, kRMxOpCodeQueryRecord, kNoErr, "wwwshhnw", + inFlags, inInterfaceIndex, inErrorCode, inName, inRRType, inRRClass, inRDataSize, inRData, inTTL ); + check_noerr( err ); +} + +//=========================================================================================================================== +// DNSServiceReconfirmRecord_server +//=========================================================================================================================== + +DEBUG_LOCAL void DNSServiceReconfirmRecord_server( RMxMessage *inMessage ) +{ + OSStatus err; + DNSServiceFlags flags; + uint32_t interfaceIndex; + const char * name; + uint16_t rrType; + uint16_t rrClass; + uint8_t * rData; + size_t rDataSize; + + check( inMessage ); + check( inMessage->session ); + require_action( !inMessage->session->message.context, exit, err = kTypeErr ); + + // Extract parameters from the message. + + err = RMxUnpack( inMessage->recvData, inMessage->recvSize, "wwshhn", + &flags, &interfaceIndex, &name, NULL, &rrType, &rrClass, &rData, &rDataSize ); + require_noerr( err, exit ); + + dlog( kDebugLevelTrace, "\n" DEBUG_NAME "%s: flags=0x%08X, ifi=%d, name=\"%s\", rrType=%d, rrClass=%d, rDataSize=%d\n", + __ROUTINE__, flags, interfaceIndex, name, rrType, rrClass, rDataSize ); + + // Trigger the reconfirm then cause the session to quit. + + DNSServiceReconfirmRecord_direct( flags, interfaceIndex, name, rrType, rrClass, (uint16_t) rDataSize, rData ); + require_noerr( err, exit ); + +exit: + inMessage->session->quit = true; + return; +} + +#ifdef __cplusplus + } +#endif diff --git a/mDNSWindows/RMxServer.h b/mDNSWindows/RMxServer.h new file mode 100644 index 0000000..4b0fb3a --- /dev/null +++ b/mDNSWindows/RMxServer.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: RMxServer.h,v $ +Revision 1.1 2004/01/30 02:35:13 bradley +Rendezvous Message Exchange implementation for DNS-SD IPC on Windows. + +*/ + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @header RMxServer.h + + @abstract Server-side implementation of the DNS-SD IPC API. + + @discussion + + This listens for and accepts connections from IPC clients, starts server sessions, and acts as a mediator between the + "direct" (compiled-in mDNSCore) code and the IPC client. +*/ + +#ifndef __RMx_SERVER__ +#define __RMx_SERVER__ + +#ifdef __cplusplus + extern "C" { +#endif + +#if 0 +#pragma mark == RMx == +#endif + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef RMxServerFlags + + @abstract Flags to control how the server is stopped. + + @constant kRMxServerFlagsNone No flags. + @constant kRMxServerFlagsAllowRemote Allow remote connections. +*/ + +typedef uint32_t RMxServerFlags; + +#define kRMxServerFlagsNone 0 +#define kRMxServerFlagsAllowRemote ( 1 << 0 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef RMxServerStopFlags + + @abstract Flags to control how the server is stopped. + + @constant kRMxServerStopFlagsNone No flags. + @constant kRMxServerStopFlagNoWait Do not wait for the server to stop (signal and return). +*/ + +typedef uint32_t RMxServerStopFlags; + +#define kRMxServerStopFlagsNone 0 +#define kRMxServerStopFlagsNoWait ( 1 << 0 ) + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxServerInitialize + + @abstract Initializes the RMx server. +*/ + +OSStatus RMxServerInitialize( RMxServerFlags inFlags ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxServerFinalize + + @abstract Finalizes the RMx server. +*/ + +void RMxServerFinalize( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxServerRun + + @abstract Runs the RMx server. Does not return unless stopped or an error occurs. +*/ + +OSStatus RMxServerRun( void ); + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function RMxServerStop + + @abstract Stops the RMx server. +*/ + +OSStatus RMxServerStop( RMxServerStopFlags inFlags ); + +#ifdef __cplusplus + } +#endif + +#endif // __RMx_SERVER__ diff --git a/mDNSWindows/mDNSWin32.c b/mDNSWindows/mDNSWin32.c index 709574f..b24527f 100755 --- a/mDNSWindows/mDNSWin32.c +++ b/mDNSWindows/mDNSWin32.c @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,8 +25,69 @@ Change History (most recent first): $Log: mDNSWin32.c,v $ -Revision 1.22.2.1 2004/04/03 05:26:07 bradley -Integrated changes from TOT to remove legacy port 53 support. +Revision 1.40 2004/05/26 09:06:07 bradley +Retry while building the interface list if it returns an error since the two-step process required to +get the interface list could allow a subsequent interface change to come in that window and change the +needed size after getting the size, but before getting the list, causing it to return an error. +Fixed structure name typo in search domain list stuff. Fixed spelling error in global for GAA. + +Revision 1.39 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "" format for bug numbers + +Revision 1.38 2004/05/13 04:57:48 ksekar +Removed unnecessary FreeSearchList function + +Revision 1.37 2004/05/13 04:54:20 ksekar +Unified list copy/free code. Added symetric list for + +Revision 1.36 2004/05/12 22:03:09 ksekar +Made GetSearchDomainList a true platform-layer call (declaration moved +from mDNSMacOSX.h to mDNSClientAPI.h), impelemted to return "local" +only on non-OSX platforms. Changed call to return a copy of the list +to avoid shared memory issues. Added a routine to free the list. + +Revision 1.35 2004/04/21 02:49:12 cheshire +To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx' + +Revision 1.34 2004/04/15 01:00:05 bradley +Removed support for automatically querying for A/AAAA records when resolving names. Platforms +without .local name resolving support will need to manually query for A/AAAA records as needed. + +Revision 1.33 2004/04/14 23:09:29 ksekar +Support for TSIG signed dynamic updates. + +Revision 1.32 2004/04/09 17:40:26 cheshire +Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field + +Revision 1.31 2004/04/09 00:40:46 bradley +Re-enable IPv6 support, AAAA records over IPv4, and IPv4 routable IPv6 exclusion support. + +Revision 1.30 2004/04/09 00:33:58 bradley +Turn on Multicast flag for interfaces to tell mDNSCore that the interfaces are multicast capable. + +Revision 1.29 2004/03/15 02:07:46 bradley +Changed interface index handling to use the upper 24 bits for IPv4 and the lower 8 bits for IPv6 to +handle some IPv4 interface indexes that are greater than 16-bit. This is not perfect because Windows +does not provide a consistent index for IPv4 and IPv6, but it seems to handle the known cases. + +Revision 1.28 2004/03/07 00:26:39 bradley +Allow non-NULL PlatformSupport ptr when initializing so non-Apple clients can provide their own storage. +Added count assert when building the wait list to catch underruns/overruns if the code is changed. + +Revision 1.27 2004/01/30 02:44:32 bradley +Added support for IPv6 (v4 & v6, v4-only, v6-only, AAAA over v4, etc.). Added support for DNS-SD +InterfaceID<->Interface Index mappings. Added support for loopback usage when no other interfaces +are available. Updated unlock signaling to no longer require timenow - NextScheduledTime to be >= 0 +(it no longer is). Added unicast-capable detection to avoid using unicast when there is other mDNS +software running on the same machine. Removed unneeded sock_XtoY routines. Added support for +reporting HINFO records with the Windows and mDNSResponder version information. + +Revision 1.26 2004/01/24 04:59:16 cheshire +Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again + +Revision 1.25 2003/11/14 20:59:09 cheshire +Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h. +Best solution is just to combine mDNSClientAPI.h and mDNSPlatformFunctions.h into a single file. Revision 1.24 2003/10/24 23:23:02 bradley Removed legacy port 53 support as it is no longer needed. @@ -95,7 +158,7 @@ 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 + 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 @@ -109,36 +172,30 @@ Added APSL info Revision 1.3 2002/09/20 05:50:45 bradley Multicast DNS platform plugin for Win32 + To Do: + + - Get unicode name of machine for nice name instead of just the host name. + - Use the IPv6 Internet Connection Firewall API to allow IPv6 mDNS without manually changing the firewall. + - Get DNS server address(es) from Windows and provide them to the uDNS layer. + - Implement TCP support for truncated packets (only stubs now). */ -#if( defined( _MSC_VER ) ) - #pragma warning( disable:4127 ) // Disable "conditional expression is constant" warning for debug macros. -#endif - -#if( !defined( WIN32_LEAN_AND_MEAN ) ) - #define WIN32_LEAN_AND_MEAN // Needed to avoid redefinitions by Windows interfaces. -#endif - #include #include #include #include #include -#include -#include -#include +#include "CommonServices.h" +#include "DebugServices.h" -#if( !defined( _WIN32_WCE ) ) // Windows CE does not have process.h. +#include +#if( !TARGET_OS_WINDOWS_CE ) + #include #include #endif -#if( DEBUG ) - #define mDNSlocal -#endif - #include "mDNSClientAPI.h" -#include "mDNSPlatformFunctions.h" #include "mDNSWin32.h" @@ -150,213 +207,26 @@ Multicast DNS platform plugin for Win32 // 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 ) +#define DEBUG_NAME "[mDNSWin32] " -#if 0 -#pragma mark == Macros - General == -#endif +#define MDNS_WINDOWS_USE_IPV6_IF_ADDRS 1 +#define MDNS_WINDOWS_ENABLE_IPV4 1 +#define MDNS_WINDOWS_ENABLE_IPV6 1 +#define MDNS_WINDOWS_EXCLUDE_IPV4_ROUTABLE_IPV6 1 +#define MDNS_WINDOWS_AAAA_OVER_IPV4 1 -//=========================================================================================================================== -// Macros - General -//=========================================================================================================================== +#define kMDNSDefaultName "My Computer" -#define kInvalidSocketRef INVALID_SOCKET -#define IsValidSocket( X ) ( ( X ) != INVALID_SOCKET ) -#define close_compat( X ) closesocket( X ) -#define errno_compat() WSAGetLastError() +#define kWinSockMajorMin 2 +#define kWinSockMinorMin 2 -// _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. +#define kWaitListCancelEvent ( WAIT_OBJECT_0 + 0 ) +#define kWaitListInterfaceListChangedEvent ( WAIT_OBJECT_0 + 1 ) +#define kWaitListWakeupEvent ( WAIT_OBJECT_0 + 2 ) +#define kWaitListFixedItemCount 3 -#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 ) +#if( !TARGET_OS_WINDOWS_CE ) + static GUID kWSARecvMsgGUID = WSAID_WSARECVMSG; #endif #if 0 @@ -367,29 +237,15 @@ Multicast DNS platform plugin for Win32 // 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 SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD ); mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD ); -mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, - const struct sockaddr_in * inAddress, - SocketRef * outSocketRef ); +mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, SocketRef *outSocketRef ); +mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort ); mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS ); mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS ); @@ -398,7 +254,7 @@ 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 ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSock ); mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ); // Platform Accessors @@ -417,6 +273,23 @@ struct mDNSPlatformInterfaceInfo mDNSexport mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID ); mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo ); +// Utilities + +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) + mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ); +#endif + +#if( !TARGET_OS_WINDOWS_CE ) + mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs ); +#endif + +#if( TARGET_OS_WINDOWS_CE ) + mDNSlocal int getifaddrs_ce( struct ifaddrs **outAddrs ); +#endif + +mDNSlocal mDNSBool CanReceiveUnicast( void ); +mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize ); + #ifdef __cplusplus } #endif @@ -432,13 +305,24 @@ mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInter mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport; mDNSs32 mDNSPlatformOneSecond = 0; -#if( MDNS_DEBUGMSGS ) - mDNSlocal unsigned long gDebugLevel = kDebugLevelInfo + 1; +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) + + typedef DWORD + ( WINAPI * GetAdaptersAddressesFunctionPtr )( + ULONG inFamily, + DWORD inFlags, + PVOID inReserved, + PIP_ADAPTER_ADDRESSES inAdapter, + PULONG outBufferSize ); + + mDNSlocal HMODULE gIPHelperLibraryInstance = NULL; + mDNSlocal GetAdaptersAddressesFunctionPtr gGetAdaptersAddressesFunctionPtr = NULL; + #endif #if 0 #pragma mark - -#pragma mark == Platform Support APIs == +#pragma mark == Platform Support == #endif //=========================================================================================================================== @@ -451,23 +335,40 @@ mStatus mDNSPlatformInit( mDNS * const inMDNS ) WSADATA wsaData; int supported; - dlog( kDebugLevelVerbose, DEBUG_NAME "platform init\n" ); + dlog( kDebugLevelTrace, DEBUG_NAME "platform init\n" ); - // Initialize variables. + // Initialize variables. If the PlatformSupport pointer is not null then just assume that a non-Apple client is + // calling mDNS_Init and wants to provide its own storage for the platform-specific data so do not overwrite it. memset( &gMDNSPlatformSupport, 0, sizeof( gMDNSPlatformSupport ) ); - inMDNS->p = &gMDNSPlatformSupport; - inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef; - mDNSPlatformOneSecond = 1000; // Use milliseconds as the quantum of time. + if( !inMDNS->p ) inMDNS->p = &gMDNSPlatformSupport; + inMDNS->p->interfaceListChangedSocket = kInvalidSocketRef; + mDNSPlatformOneSecond = 1000; // Use milliseconds as the quantum of time - // Set everything up. + // Startup WinSock 2.2 or later. 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 ); - + + inMDNS->CanReceiveUnicast = CanReceiveUnicast(); + + // Setup the HINFO HW/SW strings. + + err = GetWindowsVersionString( (char *) &inMDNS->HIHardware.c[ 1 ], sizeof( inMDNS->HIHardware.c ) - 2 ); + check_noerr( err ); + inMDNS->HIHardware.c[ 0 ] = (mDNSu8) mDNSPlatformStrLen( &inMDNS->HIHardware.c[ 1 ] ); + dlog( kDebugLevelInfo, DEBUG_NAME "HIHardware: %#s\n", inMDNS->HIHardware.c ); + + mDNS_snprintf( (char *) &inMDNS->HISoftware.c[ 1 ], sizeof( inMDNS->HISoftware.c ) - 2, + "mDNSResponder (%s %s)", __DATE__, __TIME__ ); + inMDNS->HISoftware.c[ 0 ] = (mDNSu8) mDNSPlatformStrLen( &inMDNS->HISoftware.c[ 1 ] ); + dlog( kDebugLevelInfo, DEBUG_NAME "HISoftware: %#s\n", inMDNS->HISoftware.c ); + + // Set up the mDNS thread. + err = SetupSynchronizationObjects( inMDNS ); require_noerr( err, exit ); @@ -483,7 +384,7 @@ exit: { mDNSPlatformClose( inMDNS ); } - dlog( kDebugLevelVerbose, DEBUG_NAME "platform init done (err=%ld)\n", err ); + dlog( kDebugLevelTrace, DEBUG_NAME "platform init done (err=%d %m)\n", err, err ); return( err ); } @@ -495,7 +396,7 @@ void mDNSPlatformClose( mDNS * const inMDNS ) { mStatus err; - dlog( kDebugLevelVerbose, DEBUG_NAME "platform close\n" ); + dlog( kDebugLevelTrace, DEBUG_NAME "platform close\n" ); check( inMDNS ); // Tear everything down in reverse order to how it was set up. @@ -508,10 +409,22 @@ void mDNSPlatformClose( mDNS * const inMDNS ) err = TearDownSynchronizationObjects( inMDNS ); check_noerr( err ); + + // Free the DLL needed for IPv6 support. +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) + if( gIPHelperLibraryInstance ) + { + gGetAdaptersAddressesFunctionPtr = NULL; + + FreeLibrary( gIPHelperLibraryInstance ); + gIPHelperLibraryInstance = NULL; + } +#endif + WSACleanup(); - dlog( kDebugLevelVerbose, DEBUG_NAME "platform close done (err=%ld)\n", err ); + dlog( kDebugLevelTrace, DEBUG_NAME "platform close done\n" ); } //=========================================================================================================================== @@ -524,51 +437,62 @@ mStatus 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" ); + mStatus err; + mDNSInterfaceData * ifd; + struct sockaddr_storage addr; + int n; - // Check parameters. + DEBUG_USE_ONLY( inMDNS ); + n = (int)( inMsgEnd - ( (const mDNSu8 * const) inMsg ) ); check( inMDNS ); check( inMsg ); check( inMsgEnd ); check( inInterfaceID ); check( inDstIP ); - if( inDstIP->type != mDNSAddrType_IPv4 ) + + ifd = (mDNSInterfaceData *) inInterfaceID; + require_action_quiet( ifd->interfaceInfo.McastTxRx, exit, err = mStatus_Invalid ); // Silent Interface + require_action_quiet( inDstIP->type == ifd->interfaceInfo.ip.type, exit, err = mStatus_NoError ); // Wrong Type + check( IsValidSocket( ifd->sock ) ); + + dlog( kDebugLevelChatty, DEBUG_NAME "platform send %d bytes to %#a:%u\n", n, inDstIP, ntohs( inDstPort.NotAnInteger ) ); + + if( inDstIP->type == mDNSAddrType_IPv4 ) + { + struct sockaddr_in * sa4; + + sa4 = (struct sockaddr_in *) &addr; + sa4->sin_family = AF_INET; + sa4->sin_port = inDstPort.NotAnInteger; + sa4->sin_addr.s_addr = inDstIP->ip.v4.NotAnInteger; + } + else if( inDstIP->type == mDNSAddrType_IPv6 ) { + struct sockaddr_in6 * sa6; + + sa6 = (struct sockaddr_in6 *) &addr; + sa6->sin6_family = AF_INET6; + sa6->sin6_port = inDstPort.NotAnInteger; + sa6->sin6_flowinfo = 0; + sa6->sin6_addr = *( (struct in6_addr *) &inDstIP->ip.v6 ); + sa6->sin6_scope_id = 0; // Windows requires the scope ID to be zero. IPV6_MULTICAST_IF specifies interface. + } + else + { + dlog( kDebugLevelError, DEBUG_NAME "%s: dst is not an IPv4 or IPv6 address (type=%d)\n", __ROUTINE__, inDstIP->type ); err = mStatus_BadParamErr; goto exit; } - // Send the packet. - - ifd = (mDNSInterfaceData *) inInterfaceID; - check( IsValidSocket( ifd->sock ) ); - - 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->sock, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) ); - check_errno( n, errno_compat() ); + err = translate_errno( n > 0, errno_compat(), kWriteErr ); + require_noerr( err, exit ); - 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 ); } @@ -578,6 +502,9 @@ exit: void mDNSPlatformLock( const mDNS * const inMDNS ) { + check( inMDNS ); + check( inMDNS->p->lockInitialized ); + EnterCriticalSection( &inMDNS->p->lock ); } @@ -600,32 +527,32 @@ void mDNSPlatformUnlock( const mDNS * const inMDNS ) BOOL wasSet; wasSet = SetEvent( inMDNS->p->wakeupEvent ); - check( wasSet ); + check_translated_errno( wasSet, GetLastError(), kUnknownErr ); } LeaveCriticalSection( &inMDNS->p->lock ); } //=========================================================================================================================== -// mDNSPlatformStrLen +// mDNSPlatformStrCopy //=========================================================================================================================== -mDNSu32 mDNSPlatformStrLen( const void *inSrc ) +void mDNSPlatformStrCopy( const void *inSrc, void *inDst ) { check( inSrc ); + check( inDst ); - return( (mDNSu32) strlen( (const char *) inSrc ) ); + strcpy( (char *) inDst, (const char*) inSrc ); } //=========================================================================================================================== -// mDNSPlatformStrCopy +// mDNSPlatformStrLen //=========================================================================================================================== -void mDNSPlatformStrCopy( const void *inSrc, void *inDst ) +mDNSu32 mDNSPlatformStrLen( const void *inSrc ) { check( inSrc ); - check( inDst ); - strcpy( (char *) inDst, (const char*) inSrc ); + return( (mDNSu32) strlen( (const char *) inSrc ) ); } //=========================================================================================================================== @@ -713,6 +640,15 @@ mDNSs32 mDNSPlatformTimeNow( void ) return( (mDNSs32) GetTickCount() ); } +//=========================================================================================================================== +// mDNSPlatformUTC +//=========================================================================================================================== + +mDNSexport mDNSs32 mDNSPlatformUTC( void ) +{ + return( -1 ); +} + //=========================================================================================================================== // mDNSPlatformInterfaceNameToID //=========================================================================================================================== @@ -776,13 +712,153 @@ mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID // Success! outInfo->name = ifd->name; - outInfo->ip = ifd->hostSet.ip; + outInfo->ip = ifd->interfaceInfo.ip; err = mStatus_NoError; exit: return( err ); } +//=========================================================================================================================== +// mDNSPlatformInterfaceIDfromInterfaceIndex +//=========================================================================================================================== + +mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex( const mDNS * const inMDNS, mDNSu32 inIndex ) +{ + mDNSInterfaceID id; + + id = mDNSNULL; + if( inIndex == (mDNSu32) ~0 ) + { + id = mDNSInterface_LocalOnly; + } + else if( inIndex != 0 ) + { + mDNSInterfaceData * ifd; + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( ( ifd->scopeID == inIndex ) && ifd->interfaceInfo.InterfaceActive ) + { + id = ifd->interfaceInfo.InterfaceID; + break; + } + } + check( ifd ); + } + return( id ); +} + +//=========================================================================================================================== +// mDNSPlatformInterfaceIndexfromInterfaceID +//=========================================================================================================================== + +mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( const mDNS * const inMDNS, mDNSInterfaceID inID ) +{ + mDNSu32 index; + + index = 0; + if( inID == mDNSInterface_LocalOnly ) + { + index = (mDNSu32) ~0; + } + else if( inID ) + { + mDNSInterfaceData * ifd; + + for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) + { + if( (mDNSInterfaceID) ifd == inID ) + { + index = ifd->scopeID; + break; + } + } + check( ifd ); + } + return( index ); +} + +//=========================================================================================================================== +// mDNSPlatformTCPConnect +//=========================================================================================================================== + +mStatus + mDNSPlatformTCPConnect( + const mDNSAddr * inDstIP, + mDNSOpaque16 inDstPort, + mDNSInterfaceID inInterfaceID, + TCPConnectionCallback inCallback, + void * inContext, + int * outSock ) +{ + DEBUG_UNUSED( inDstIP ); + DEBUG_UNUSED( inDstPort ); + DEBUG_UNUSED( inInterfaceID ); + DEBUG_UNUSED( inCallback ); + DEBUG_UNUSED( inContext ); + DEBUG_UNUSED( outSock ); + + return( mStatus_UnsupportedErr ); +} + +//=========================================================================================================================== +// mDNSPlatformTCPCloseConnection +//=========================================================================================================================== + +void mDNSPlatformTCPCloseConnection( int inSock ) +{ + DEBUG_UNUSED( inSock ); +} + +//=========================================================================================================================== +// mDNSPlatformReadTCP +//=========================================================================================================================== + +int mDNSPlatformReadTCP( int inSock, void *inBuffer, int inBufferSize ) +{ + DEBUG_UNUSED( inSock ); + DEBUG_UNUSED( inBuffer ); + DEBUG_UNUSED( inBufferSize ); + + return( -1 ); +} + +//=========================================================================================================================== +// mDNSPlatformWriteTCP +//=========================================================================================================================== + +int mDNSPlatformWriteTCP( int inSock, const char *inMsg, int inMsgSize ) +{ + DEBUG_UNUSED( inSock ); + DEBUG_UNUSED( inMsg ); + DEBUG_UNUSED( inMsgSize ); + + return( -1 ); +} + + +//=========================================================================================================================== +// mDNSPlatformGetSearchDomainList +//=========================================================================================================================== + + +mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void) + { + static DNameListElem tmp; + static int init = 0; + + if (!init) + { + MakeDomainNameFromDNSNameString(&tmp.name, "local."); + tmp.next = NULL; + init = 1; + } + return mDNS_CopyDNameList(&tmp); + } + + + #if 0 #pragma mark - #endif @@ -792,7 +868,7 @@ exit: //=========================================================================================================================== #if( MDNS_DEBUGMSGS ) -void debugf_( const char *inFormat, ... ) +void debugf_( const char *inFormat, ... ) { char buffer[ 512 ]; va_list args; @@ -811,7 +887,7 @@ void debugf_( const char *inFormat, ... ) //=========================================================================================================================== #if( MDNS_DEBUGMSGS > 1 ) -void verbosedebugf_( const char *inFormat, ... ) +void verbosedebugf_( const char *inFormat, ... ) { char buffer[ 512 ]; va_list args; @@ -829,7 +905,7 @@ void verbosedebugf_( const char *inFormat, ... ) // LogMsg //=========================================================================================================================== -void LogMsg( const char *inFormat, ... ) +void LogMsg( const char *inFormat, ... ) { char buffer[ 512 ]; va_list args; @@ -842,84 +918,6 @@ void LogMsg( const char *inFormat, ... ) 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 == @@ -932,32 +930,31 @@ mDNSlocal void 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 ); + err = translate_errno( inMDNS->p->cancelEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); inMDNS->p->quitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr ); + err = translate_errno( inMDNS->p->quitEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); inMDNS->p->interfaceListChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - require_action( inMDNS->p->interfaceListChangedEvent, exit, err = mStatus_NoMemoryErr ); + err = translate_errno( inMDNS->p->interfaceListChangedEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); inMDNS->p->wakeupEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - require_action( inMDNS->p->wakeupEvent, exit, err = mStatus_NoMemoryErr ); - - err = mStatus_NoError; + err = translate_errno( inMDNS->p->wakeupEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); exit: if( err ) { TearDownSynchronizationObjects( inMDNS ); } - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up synchronization objects done (err=%ld)\n", err ); return( err ); } @@ -967,10 +964,6 @@ exit: 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 ); @@ -996,10 +989,7 @@ mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS ) 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 ); + return( mStatus_NoError ); } //=========================================================================================================================== @@ -1013,11 +1003,11 @@ mDNSlocal mStatus SetupName( mDNS * const inMDNS ) check( inMDNS ); - // Get the name of this machine. + // Set up the nice name. tempString[ 0 ] = '\0'; err = gethostname( tempString, sizeof( tempString ) - 1 ); - check_errno( err, errno_compat() ); + check_translated_errno( err == 0, errno_compat(), kNameErr ); if( err || ( tempString[ 0 ] == '\0' ) ) { // Invalidate name so fall back to a default name. @@ -1026,10 +1016,11 @@ mDNSlocal mStatus SetupName( mDNS * const inMDNS ) } 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 ] ); + + // Set up the host name. + ConvertUTF8PstringToRFC1034HostLabel( inMDNS->nicelabel.c, &inMDNS->hostlabel ); if( inMDNS->hostlabel.c[ 0 ] == 0 ) { @@ -1058,10 +1049,11 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) 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" ); + dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list\n" ); check( inMDNS ); check( inMDNS->p ); @@ -1081,34 +1073,108 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) err = SetupNotifications( inMDNS ); check_noerr( err ); - // Set up each interface that is active, multicast-capable, and not the loopback interface or point-to-point. + // Set up IPv4 interface(s). We have to set up IPv4 first so any IPv6 interface with an IPv4-routable address + // can refer to the IPv4 interface when it registers to allow DNS AAAA records over the IPv4 interface. + + err = getifaddrs( &addrs ); + require_noerr( err, exit ); - flagMask = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTTOPOINT; + loopback = NULL; + next = &inMDNS->p->interfaceList; + + flagMask = IFF_UP | IFF_MULTICAST | IFF_POINTTOPOINT; flagTest = IFF_UP | IFF_MULTICAST; - next = &inMDNS->p->interfaceList; +#if( MDNS_WINDOWS_ENABLE_IPV4 ) + for( p = addrs; p; p = p->ifa_next ) + { + if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) || ( ( p->ifa_flags & flagMask ) != flagTest ) ) + { + continue; + } + if( p->ifa_flags & IFF_LOOPBACK ) + { + if( !loopback ) + { + loopback = p; + } + continue; + } + dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", + p->ifa_name ? p->ifa_name : "", p->ifa_extra.index, p->ifa_addr ); + + err = SetupInterface( inMDNS, p, &ifd ); + require_noerr( err, exit ); + + *next = ifd; + next = &ifd->next; + ++inMDNS->p->interfaceCount; + } +#endif - err = getifaddrs( &addrs ); - require_noerr( err, exit ); + // Set up IPv6 interface(s) after IPv4 is set up (see IPv4 notes above for reasoning). +#if( MDNS_WINDOWS_ENABLE_IPV6 ) for( p = addrs; p; p = p->ifa_next ) { - if( ( p->ifa_flags & flagMask ) == flagTest ) + if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET6 ) || ( ( p->ifa_flags & flagMask ) != flagTest ) ) + { + continue; + } + if( p->ifa_flags & IFF_LOOPBACK ) { - if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) ) // $$$ TO DO: Update for IPv6. + if( !loopback ) { - continue; + loopback = p; } - - 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; + continue; + } + dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", + p->ifa_name ? p->ifa_name : "", p->ifa_extra.index, p->ifa_addr ); + + err = SetupInterface( inMDNS, p, &ifd ); + require_noerr( err, exit ); + + *next = ifd; + next = &ifd->next; + ++inMDNS->p->interfaceCount; + } +#endif + + // If there are no real interfaces, but there is a loopback interface, use that so same-machine operations work. + +#if( !MDNS_WINDOWS_ENABLE_IPV4 && !MDNS_WINDOWS_ENABLE_IPV6 ) + + flagMask |= IFF_LOOPBACK; + flagTest |= IFF_LOOPBACK; + + for( p = addrs; p; p = p->ifa_next ) + { + if( !p->ifa_addr || ( ( p->ifa_flags & flagMask ) != flagTest ) ) + { + continue; + } + if( ( p->ifa_addr->sa_family != AF_INET ) && ( p->ifa_addr->sa_family != AF_INET6 ) ) + { + continue; } + loopback = p; + break; + } + +#endif + + if( !inMDNS->p->interfaceList && loopback ) + { + dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", + loopback->ifa_name ? loopback->ifa_name : "", loopback->ifa_extra.index, loopback->ifa_addr ); + + err = SetupInterface( inMDNS, loopback, &ifd ); + require_noerr( err, exit ); + + *next = ifd; + next = &ifd->next; + ++inMDNS->p->interfaceCount; } exit: @@ -1120,7 +1186,7 @@ exit: { freeifaddrs( addrs ); } - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface list done (err=%ld)\n", err ); + dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list done (err=%d %m)\n", err, err ); return( err ); } @@ -1133,7 +1199,7 @@ mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ) mStatus err; mDNSInterfaceData * ifd; - dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list\n" ); + dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list\n" ); check( inMDNS ); check( inMDNS->p ); @@ -1153,7 +1219,7 @@ mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ) } inMDNS->p->interfaceCount = 0; - dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down interface list done\n" ); + dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list done\n" ); return( mStatus_NoError ); } @@ -1161,54 +1227,137 @@ mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ) // SetupInterface //=========================================================================================================================== -mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct sockaddr_in *inAddress, mDNSInterfaceData **outIFD ) +mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD ) { mStatus err; mDNSInterfaceData * ifd; - SocketRef socketRef; + SocketRef sock; - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface\n" ); + ifd = NULL; + dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface\n" ); check( inMDNS ); check( inMDNS->p ); - check( inAddress ); + check( inIFA ); + check( inIFA->ifa_addr ); check( outIFD ); - // Allocate memory for the info item. + // Allocate memory for the interface and initialize it. ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) ); require_action( ifd, exit, err = mStatus_NoMemoryErr ); - ifd->sock = kInvalidSocketRef; + ifd->sock = kInvalidSocketRef; + ifd->index = inIFA->ifa_extra.index; + ifd->scopeID = inIFA->ifa_extra.index; - // Set up a multicast DNS (port 5353) socket for this interface. + check( strlen( inIFA->ifa_name ) < sizeof( ifd->name ) ); + strncpy( ifd->name, inIFA->ifa_name, sizeof( ifd->name ) - 1 ); + ifd->name[ sizeof( ifd->name ) - 1 ] = '\0'; + + // We always send and receive using IPv4, but 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. - err = SetupSocket( inMDNS, inAddress, &socketRef ); - require_noerr( err, exit ); - ifd->sock = socketRef; + ifd->interfaceInfo.McastTxRx = mDNStrue; - // Set up the read pending event and associate it so we can block until data is available for this socket. +#if( MDNS_WINDOWS_EXCLUDE_IPV4_ROUTABLE_IPV6 ) + if( inIFA->ifa_addr->sa_family != AF_INET ) + { + const mDNSInterfaceData * p; + + for( p = inMDNS->p->interfaceList; p; p = p->next ) + { + if( ( p->interfaceInfo.ip.type == mDNSAddrType_IPv4 ) && + ( ( p->interfaceInfo.ip.ip.v4.b[ 0 ] != 169 ) && ( p->interfaceInfo.ip.ip.v4.b[ 1 ] != 254 ) ) && + ( strcmp( p->name, inIFA->ifa_name ) == 0 ) ) + { + ifd->interfaceInfo.McastTxRx = mDNSfalse; + break; + } + } + } +#endif + + // If this is an IPv6 interface, search for its IPv4 equivalent and use that InterfaceID. This causes the IPv4 + // interface to send both A and AAAA records so we can publish IPv6 support without doubling the packet rate. + // Note: this search only works because we register all IPv4 interfaces before IPv6 interfaces. - ifd->readPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - require_action( ifd->readPendingEvent, exit, err = mStatus_NoMemoryErr ); + ifd->interfaceInfo.InterfaceID = (mDNSInterfaceID) ifd; - err = WSAEventSelect( ifd->sock, ifd->readPendingEvent, FD_READ ); - require_noerr( err, exit ); +#if( MDNS_WINDOWS_AAAA_OVER_IPV4 ) + if( inIFA->ifa_addr->sa_family != AF_INET ) + { + mDNSInterfaceData * ipv4IFD; + for( ipv4IFD = inMDNS->p->interfaceList; ipv4IFD; ipv4IFD = ipv4IFD->next ) + { + if( strcmp( ipv4IFD->name, ifd->name ) == 0 ) + { + ipv4IFD->scopeID = ifd->scopeID; + ifd->interfaceInfo.McastTxRx = mDNSfalse; + ifd->interfaceInfo.InterfaceID = (mDNSInterfaceID) ipv4IFD; + break; + } + } + } +#endif + + // Set up a socket for this interface (if needed). + + if( ifd->interfaceInfo.McastTxRx ) + { + err = SetupSocket( inMDNS, inIFA->ifa_addr, &sock ); + require_noerr( err, exit ); + ifd->sock = sock; + ifd->defaultAddr = ( inIFA->ifa_addr->sa_family == AF_INET6 ) ? AllDNSLinkGroup_v6 : AllDNSLinkGroup_v4; + + // Get a ptr to the WSARecvMsg function, if supported. Otherwise, we'll fallback to recvfrom. + + #if( !TARGET_OS_WINDOWS_CE ) + { + DWORD size; + + err = WSAIoctl( sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), + &ifd->wsaRecvMsgFunctionPtr, sizeof( ifd->wsaRecvMsgFunctionPtr ), &size, NULL, NULL ); + if( err != 0 ) + { + ifd->wsaRecvMsgFunctionPtr = NULL; + } + } + #endif + + // Set up the read pending event and associate it so we can block until data is available for this socket. + + ifd->readPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + err = translate_errno( ifd->readPendingEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + err = WSAEventSelect( ifd->sock, ifd->readPendingEvent, FD_READ ); + require_noerr( err, exit ); + } + else + { + // Create a placeholder event so WaitForMultipleObjects Handle slot for this interface is valid. + + ifd->readPendingEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + err = translate_errno( ifd->readPendingEvent, (mStatus) GetLastError(), kUnknownErr ); + 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 = SockAddrToMDNSAddr( inIFA->ifa_addr, &ifd->interfaceInfo.ip, NULL ); + require_noerr( err, exit ); + + ifd->interfaceInfo.Advertise = inMDNS->AdvertiseLocalAddresses; - err = mDNS_RegisterInterface( inMDNS, &ifd->hostSet ); + err = mDNS_RegisterInterface( inMDNS, &ifd->interfaceInfo ); 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 ] ); + dlog( kDebugLevelInfo, DEBUG_NAME "Registered interface %##a with mDNS\n", inIFA->ifa_addr ); // Success! @@ -1220,7 +1369,7 @@ exit: { TearDownInterface( inMDNS, ifd ); } - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface done (err=%ld)\n", err ); + dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface done (err=%d %m)\n", err, err ); return( err ); } @@ -1230,23 +1379,19 @@ exit: mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD ) { - SocketRef socketRef; + SocketRef sock; 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 ] ); + dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering interface %#a with mDNS\n", &inIFD->interfaceInfo.ip ); if( inIFD->hostRegistered ) { inIFD->hostRegistered = mDNSfalse; - mDNS_DeregisterInterface( inMDNS, &inIFD->hostSet ); + mDNS_DeregisterInterface( inMDNS, &inIFD->interfaceInfo ); } // Tear down the multicast socket. @@ -1257,12 +1402,11 @@ mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inI inIFD->readPendingEvent = 0; } - socketRef = inIFD->sock; + sock = inIFD->sock; inIFD->sock = kInvalidSocketRef; - if( IsValidSocket( socketRef ) ) + if( IsValidSocket( sock ) ) { - dlog( kDebugLevelVerbose, DEBUG_NAME "tearing down socket %d\n", socketRef ); - close_compat( socketRef ); + close_compat( sock ); } // Free the memory used by the interface info. @@ -1275,84 +1419,223 @@ mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inI // SetupSocket //=========================================================================================================================== -mDNSlocal mStatus - SetupSocket( - mDNS * const inMDNS, - const struct sockaddr_in * inAddress, - SocketRef * outSocketRef ) +mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, SocketRef *outSocketRef ) { - mStatus err; - SocketRef socketRef; - int option; - struct ip_mreq mreq; - struct sockaddr_in addr; - mDNSv4Addr ip; + mStatus err; + SocketRef sock; + int option; - MDNS_UNUSED( inMDNS ); + DEBUG_UNUSED( inMDNS ); - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done\n" ); + dlog( kDebugLevelTrace, DEBUG_NAME "setting up socket %##a\n", inAddr ); 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 ); + // Set up an IPv4 or IPv6 UDP socket. + sock = socket( inAddr->sa_family, SOCK_DGRAM, IPPROTO_UDP ); + err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + // 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 mutlicast DNS port 5353. - - ip.NotAnInteger = inAddress->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_compat() ); + err = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); - // Join the all-DNS multicast group so we receive Multicast DNS packets. - - 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). + if( inAddr->sa_family == AF_INET ) + { + mDNSv4Addr ipv4; + struct sockaddr_in sa4; + struct ip_mreq mreqv4; - 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() ); + // Bind to the multicast DNS port 5353. - // Success! + ipv4.NotAnInteger = ( (const struct sockaddr_in *) inAddr )->sin_addr.s_addr; + memset( &sa4, 0, sizeof( sa4 ) ); + sa4.sin_family = AF_INET; + sa4.sin_port = MulticastDNSPort.NotAnInteger; + sa4.sin_addr.s_addr = ipv4.NotAnInteger; + + err = bind( sock, (struct sockaddr *) &sa4, sizeof( sa4 ) ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + + // Turn on option to receive destination addresses and receiving interface. + + option = 1; + err = setsockopt( sock, IPPROTO_IP, IP_PKTINFO, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Join the all-DNS multicast group so we receive Multicast DNS packets. + + mreqv4.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; + mreqv4.imr_interface.s_addr = ipv4.NotAnInteger; + err = setsockopt( sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreqv4, sizeof( mreqv4 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Specify the interface to send multicast packets on this socket. + + sa4.sin_addr.s_addr = ipv4.NotAnInteger; + err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (char *) &sa4.sin_addr, sizeof( sa4.sin_addr ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Send unicast packets with TTL 255 (helps against spoofing). + + option = 255; + err = setsockopt( sock, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Send multicast packets with TTL 255 (helps against spoofing). + + option = 255; + err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Enable multicast loopback so we receive multicast packets we send (for same-machine operations). + + option = 1; + err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } + else if( inAddr->sa_family == AF_INET6 ) + { + struct sockaddr_in6 * sa6p; + struct sockaddr_in6 sa6; + struct ipv6_mreq mreqv6; + + sa6p = (struct sockaddr_in6 *) inAddr; + + // Bind to the multicast DNS port 5353. + + memset( &sa6, 0, sizeof( sa6 ) ); + sa6.sin6_family = AF_INET6; + sa6.sin6_port = MulticastDNSPort.NotAnInteger; + sa6.sin6_flowinfo = 0; + sa6.sin6_addr = sa6p->sin6_addr; + sa6.sin6_scope_id = sa6p->sin6_scope_id; + + err = bind( sock, (struct sockaddr *) &sa6, sizeof( sa6 ) ); + check_translated_errno( err == 0, errno_compat(), kUnknownErr ); + + // Turn on option to receive destination addresses and receiving interface. + + option = 1; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Turn on option to receive TTL so we can check for spoofing. + + option = 1; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_HOPLIMIT, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // We only want to receive IPv6 packets (not IPv4-mapped IPv6 addresses) because we have a separate socket + // for IPv4, but the IPv6 stack in Windows currently doesn't support IPv4-mapped IPv6 addresses and doesn't + // support the IPV6_V6ONLY socket option so the following code would typically not be executed (or needed). + + #if( defined( IPV6_V6ONLY ) ) + option = 1; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + #endif + + // Join the all-DNS multicast group so we receive Multicast DNS packets. + + mreqv6.ipv6mr_multiaddr = *( (struct in6_addr *) &AllDNSLinkGroupv6 ); + mreqv6.ipv6mr_interface = sa6p->sin6_scope_id; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreqv6, sizeof( mreqv6 ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Specify the interface to send multicast packets on this socket. + + option = (int) sa6p->sin6_scope_id; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Send unicast packets with TTL 255 (helps against spoofing). + + option = 255; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Send multicast packets with TTL 255 (helps against spoofing). + + option = 255; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + + // Enable multicast loopback so we receive multicast packets we send (for same-machine operations). + + option = 1; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &option, sizeof( option ) ); + check_translated_errno( err == 0, errno_compat(), kOptionErr ); + } + else + { + dlog( kDebugLevelError, DEBUG_NAME "%s: unsupport socket family (%d)\n", __ROUTINE__, inAddr->sa_family ); + err = kUnsupportedErr; + goto exit; + } - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up socket done (%u.%u.%u.%u, %d)\n", - ip.b[ 0 ], ip.b[ 1 ], ip.b[ 2 ], ip.b[ 3 ], socketRef ); + // Success! - *outSocketRef = socketRef; - socketRef = kInvalidSocketRef; + *outSocketRef = sock; + sock = kInvalidSocketRef; err = mStatus_NoError; exit: - if( IsValidSocket( socketRef ) ) + if( IsValidSocket( sock ) ) + { + close_compat( sock ); + } + return( err ); +} + +//=========================================================================================================================== +// SetupSocket +//=========================================================================================================================== + +mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort ) +{ + mStatus err; + + check( inSA ); + check( outIP ); + + if( inSA->sa_family == AF_INET ) + { + struct sockaddr_in * sa4; + + sa4 = (struct sockaddr_in *) inSA; + outIP->type = mDNSAddrType_IPv4; + outIP->ip.v4.NotAnInteger = sa4->sin_addr.s_addr; + if( outPort ) + { + outPort->NotAnInteger = sa4->sin_port; + } + err = mStatus_NoError; + } + else if( inSA->sa_family == AF_INET6 ) + { + struct sockaddr_in6 * sa6; + + sa6 = (struct sockaddr_in6 *) inSA; + outIP->type = mDNSAddrType_IPv6; + outIP->ip.v6 = *( (mDNSv6Addr *) &sa6->sin6_addr ); + if( IN6_IS_ADDR_LINKLOCAL( &sa6->sin6_addr ) ) + { + outIP->ip.v6.w[ 1 ] = 0; + } + if( outPort ) + { + outPort->NotAnInteger = sa6->sin6_port; + } + err = mStatus_NoError; + } + else { - close_compat( socketRef ); + dlog( kDebugLevelError, DEBUG_NAME "%s: invalid sa_family %d", __ROUTINE__, inSA->sa_family ); + err = mStatus_BadParamErr; } return( err ); } @@ -1364,7 +1647,7 @@ exit: mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS ) { mStatus err; - SocketRef socketRef; + SocketRef sock; unsigned long param; int inBuffer; int outBuffer; @@ -1372,27 +1655,30 @@ mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS ) // 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; + sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + inMDNS->p->interfaceListChangedSocket = sock; // Make the socket non-blocking so the WSAIoctl returns immediately with WSAEWOULDBLOCK. It will set the event // when a change to the interface list is detected. param = 1; - err = ioctlsocket( socketRef, FIONBIO, ¶m ); - require_errno( err, errno_compat(), exit ); + err = ioctlsocket( sock, FIONBIO, ¶m ); + err = translate_errno( err == 0, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); inBuffer = 0; outBuffer = 0; - err = WSAIoctl( socketRef, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL ); + err = WSAIoctl( sock, 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 ); + err = WSAEventSelect( sock, inMDNS->p->interfaceListChangedEvent, FD_ADDRESS_LIST_CHANGE ); + err = translate_errno( err == 0, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); exit: if( err ) @@ -1408,13 +1694,10 @@ exit: mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS ) { - SocketRef socketRef; - - socketRef = inMDNS->p->interfaceListChangedSocketRef; - inMDNS->p->interfaceListChangedSocketRef = kInvalidSocketRef; - if( IsValidSocket( socketRef ) ) + if( IsValidSocket( inMDNS->p->interfaceListChangedSocket ) ) { - close_compat( socketRef ); + close_compat( inMDNS->p->interfaceListChangedSocket ); + inMDNS->p->interfaceListChangedSocket = kInvalidSocketRef; } return( mStatus_NoError ); } @@ -1434,23 +1717,27 @@ mDNSlocal mStatus SetupThread( mDNS * const inMDNS ) unsigned threadID; DWORD result; - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread\n" ); + dlog( kDebugLevelTrace, 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 ); + err = translate_errno( inMDNS->p->initEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + inMDNS->p->initStatus = mStatus_Invalid; // Create thread with _beginthreadex() instead of CreateThread() to avoid memory leaks when using static run-time // libraries. See . - threadHandle = (HANDLE) _beginthreadex( NULL, 0, ProcessingThread, inMDNS, 0, &threadID ); - require_action( threadHandle, exit, err = mStatus_NoMemoryErr ); - + threadHandle = (HANDLE) _beginthreadex_compat( NULL, 0, ProcessingThread, inMDNS, 0, &threadID ); + err = translate_errno( threadHandle, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + result = WaitForSingleObject( inMDNS->p->initEvent, INFINITE ); - require_action( result == WAIT_OBJECT_0, exit, err = mStatus_UnknownErr ); + err = translate_errno( result == WAIT_OBJECT_0, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); err = inMDNS->p->initStatus; require_noerr( err, exit ); @@ -1460,7 +1747,7 @@ exit: CloseHandle( inMDNS->p->initEvent ); inMDNS->p->initEvent = 0; } - dlog( kDebugLevelVerbose, DEBUG_NAME "setting up thread done (err=%ld)\n", err ); + dlog( kDebugLevelTrace, DEBUG_NAME "setting up thread done (err=%d %m)\n", err, err ); return( err ); } @@ -1470,22 +1757,21 @@ exit: 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; + DWORD result; wasSet = SetEvent( inMDNS->p->cancelEvent ); - check( wasSet ); + check_translated_errno( wasSet, GetLastError(), kUnknownErr ); if( inMDNS->p->quitEvent ) { result = WaitForSingleObject( inMDNS->p->quitEvent, 5 * 1000 ); - check( result == WAIT_OBJECT_0 ); + check_translated_errno( result == WAIT_OBJECT_0, GetLastError(), kUnknownErr ); } } return( mStatus_NoError ); @@ -1533,7 +1819,7 @@ mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) 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 ) { @@ -1561,7 +1847,7 @@ mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) { // Wakeup event due to an mDNS API call. Loop back to call mDNS_Execute. - dlog( kDebugLevelChatty - 1, DEBUG_NAME "wakeup\n" ); + dlog( kDebugLevelChatty - 1, DEBUG_NAME "wakeup for mDNS_Execute\n" ); continue; } else @@ -1596,7 +1882,7 @@ mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) { // Unexpected wait result. - dlog( kDebugLevelAllowedError, DEBUG_NAME "unexpected wait result (result=0x%08X)\n", result ); + dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result ); } } } @@ -1615,12 +1901,12 @@ mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) exit: wasSet = SetEvent( m->p->quitEvent ); - check( wasSet ); - + check_translated_errno( wasSet, GetLastError(), kUnknownErr ); + // Call _endthreadex() explicitly instead of just exiting normally to avoid memory leaks when using static run-time // libraries. See . - _endthreadex( 0 ); + _endthreadex_compat( 0 ); return( 0 ); } @@ -1644,9 +1930,9 @@ exit: TearDownInterfaceList( inMDNS ); } inMDNS->p->initStatus = err; - wasSet = SetEvent( inMDNS->p->initEvent ); - check( wasSet ); + wasSet = SetEvent( inMDNS->p->initEvent ); + check_translated_errno( wasSet, GetLastError(), kUnknownErr ); return( err ); } @@ -1662,7 +1948,7 @@ mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **o HANDLE * waitItemPtr; mDNSInterfaceData * ifd; - dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list\n" ); + dlog( kDebugLevelTrace, DEBUG_NAME "thread setting up wait list\n" ); check( inMDNS ); check( inMDNS->p ); check( outWaitList ); @@ -1687,6 +1973,7 @@ mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **o { *waitItemPtr++ = ifd->readPendingEvent; } + check( (int)( waitItemPtr - waitList ) == waitListCount ); *outWaitList = waitList; *outWaitListCount = waitListCount; @@ -1698,7 +1985,7 @@ exit: { free( waitList ); } - dlog( kDebugLevelVerbose, DEBUG_NAME "thread setting up wait list done (err=%ld)\n", err ); + dlog( kDebugLevelTrace, DEBUG_NAME "thread setting up wait list done (err=%d %m)\n", err, err ); return( err ); } @@ -1706,58 +1993,111 @@ exit: // ProcessingThreadProcessPacket //=========================================================================================================================== -mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSocketRef ) +mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSock ) { - int n; - DNSMessage packet; - struct sockaddr_in addr; - int addrSize; - mDNSu8 * packetEndPtr; - mDNSAddr srcAddr; - mDNSIPPort srcPort; - mDNSAddr dstAddr; - mDNSIPPort dstPort; + OSStatus err; + mDNSAddr srcAddr; + mDNSIPPort srcPort; + mDNSAddr dstAddr; + mDNSIPPort dstPort; + mDNSu8 ttl; + struct sockaddr_storage addr; + DNSMessage packet; + mDNSu8 * end; + int n; - // Receive the packet. + check( inMDNS ); + check( inIFD ); + check( IsValidSocket( inSock ) ); - addrSize = sizeof( addr ); - n = recvfrom( inSocketRef, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize ); - check( n >= 0 ); - if( n >= 0 ) + // Set up the default in case the packet info options are not supported or reported correctly. + + dstAddr = inIFD->defaultAddr; + dstPort = MulticastDNSPort; + ttl = 255; + +#if( !TARGET_OS_WINDOWS_CE ) + if( inIFD->wsaRecvMsgFunctionPtr ) { - // Set up the src/dst/interface info. + WSAMSG msg; + WSABUF buf; + uint8_t controlBuffer[ 128 ]; + DWORD size; + LPWSACMSGHDR header; - 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 = AllDNSLinkGroup; - dstPort = MulticastDNSPort; + // Set up the buffer and read the packet. - 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" ); + msg.name = (LPSOCKADDR) &addr; + msg.namelen = (INT) sizeof( addr ); + buf.buf = (char *) &packet; + buf.len = (u_long) sizeof( packet ); + msg.lpBuffers = &buf; + msg.dwBufferCount = 1; + msg.Control.buf = (char *) controlBuffer; + msg.Control.len = (u_long) sizeof( controlBuffer ); + msg.dwFlags = 0; + + err = inIFD->wsaRecvMsgFunctionPtr( inSock, &msg, &size, NULL, NULL ); + err = translate_errno( err == 0, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + n = (int) size; + + // Parse the control information. Reject packets received on the wrong interface. - // Dispatch the packet to mDNS. + for( header = WSA_CMSG_FIRSTHDR( &msg ); header; header = WSA_CMSG_NXTHDR( &msg, header ) ) + { + if( ( header->cmsg_level == IPPROTO_IP ) && ( header->cmsg_type == IP_PKTINFO ) ) + { + IN_PKTINFO * ipv4PacketInfo; + + ipv4PacketInfo = (IN_PKTINFO *) WSA_CMSG_DATA( header ); + require_action( ipv4PacketInfo->ipi_ifindex == ( inIFD->index >> 8 ), exit, err = kMismatchErr ); + + dstAddr.type = mDNSAddrType_IPv4; + dstAddr.ip.v4.NotAnInteger = ipv4PacketInfo->ipi_addr.s_addr; + } + else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_PKTINFO ) ) + { + IN6_PKTINFO * ipv6PacketInfo; + + ipv6PacketInfo = (IN6_PKTINFO *) WSA_CMSG_DATA( header ); + require_action( ipv6PacketInfo->ipi6_ifindex == inIFD->index, exit, err = kMismatchErr ); + + dstAddr.type = mDNSAddrType_IPv6; + dstAddr.ip.v6 = *( (mDNSv6Addr *) &ipv6PacketInfo->ipi6_addr ); + } + else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_HOPLIMIT ) ) + { + ttl = (mDNSu8) *( (int *) WSA_CMSG_DATA( header ) ); + } + } + } + else +#endif + { + int addrSize; - packetEndPtr = ( (mDNSu8 *) &packet ) + n; - mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inIFD->hostSet.InterfaceID, 255 ); + addrSize = sizeof( addr ); + n = recvfrom( inSock, (char *) &packet, sizeof( packet ), 0, (struct sockaddr *) &addr, &addrSize ); + err = translate_errno( n > 0, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); } + SockAddrToMDNSAddr( (struct sockaddr *) &addr, &srcAddr, &srcPort ); + + // Dispatch the packet to mDNS. - // Update counters. + dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" ); + dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", n ); + dlog( kDebugLevelChatty, DEBUG_NAME " src = %#a:%u\n", &srcAddr, ntohs( srcPort.NotAnInteger ) ); + dlog( kDebugLevelChatty, DEBUG_NAME " dst = %#a:%u\n", &dstAddr, ntohs( dstPort.NotAnInteger ) ); + dlog( kDebugLevelChatty, DEBUG_NAME " interface = %#a (index=0x%08X)\n", &inIFD->interfaceInfo.ip, (int) inIFD->index ); + dlog( kDebugLevelChatty, DEBUG_NAME "\n" ); - inIFD->recvCounter += 1; - inIFD->recvErrorCounter += ( n < 0 ); + end = ( (mDNSu8 *) &packet ) + n; + mDNSCoreReceive( inMDNS, &packet, end, &srcAddr, srcPort, &dstAddr, dstPort, inIFD->interfaceInfo.InterfaceID, ttl ); + +exit: + return; } //=========================================================================================================================== @@ -1768,7 +2108,7 @@ mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ) { mStatus err; - dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed event\n" ); + dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed\n" ); check( inMDNS ); mDNSPlatformLock( inMDNS ); @@ -1800,97 +2140,201 @@ mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ) #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 ); + int err; - // 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. +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS && !TARGET_OS_WINDOWS_CE ) - size = 0; - WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0, &size, NULL, NULL ); - require_action( size > 0, exit, err = -1 ); - size *= 2; + // Try to the load the GetAdaptersAddresses function from the IP Helpers DLL. This API is only available on Windows + // XP or later. Looking up the symbol at runtime allows the code to still work on older systems without that API. - buffer = calloc( 1, size ); - require_action( buffer, exit, err = -1 ); + if( !gIPHelperLibraryInstance ) + { + gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) ); + if( gIPHelperLibraryInstance ) + { + gGetAdaptersAddressesFunctionPtr = + (GetAdaptersAddressesFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetAdaptersAddresses" ); + if( !gGetAdaptersAddressesFunctionPtr ) + { + BOOL ok; + + ok = FreeLibrary( gIPHelperLibraryInstance ); + check_translated_errno( ok, GetLastError(), kUnknownErr ); + gIPHelperLibraryInstance = NULL; + } + } + } - // We now know the size of the list and have a buffer to hold so call WSAIoctl again to get it. + // Use the new IPv6-capable routine if supported. Otherwise, fall back to the old and compatible IPv4-only code. - err = WSAIoctl( sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, buffer, size, &size, NULL, NULL ); + if( gGetAdaptersAddressesFunctionPtr ) + { + err = getifaddrs_ipv6( outAddrs ); + require_noerr( err, exit ); + } + else + { + err = getifaddrs_ipv4( outAddrs ); + require_noerr( err, exit ); + } + +#elif( !TARGET_OS_WINDOWS_CE ) + + err = getifaddrs_ipv4( outAddrs ); require_noerr( err, exit ); - addressList = (SOCKET_ADDRESS_LIST *) buffer; + +#else + + err = getifaddrs_ce( outAddrs ); + require_noerr( err, exit ); + +#endif + +exit: + return( err ); +} + +#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) +//=========================================================================================================================== +// getifaddrs_ipv6 +//=========================================================================================================================== + +mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ) +{ + DWORD err; + int i; + DWORD flags; + struct ifaddrs * head; + struct ifaddrs ** next; + IP_ADAPTER_ADDRESSES * iaaList; + ULONG iaaListSize; + IP_ADAPTER_ADDRESSES * iaa; + size_t size; + struct ifaddrs * ifa; - // 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. + check( gGetAdaptersAddressesFunctionPtr ); - n = addressList->iAddressCount; - if( n == 0 ) - { - n = 1; - } - for( i = 0; i < n; ++i ) + head = NULL; + next = &head; + iaaList = NULL; + + // Get the list of interfaces. The first call gets the size and the second call gets the actual data. + // This loops to handle the case where the interface changes in the window after getting the size, but before the + // second call completes. A limit of 100 retries is enforced to prevent infinite loops if something else is wrong. + + flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; + i = 0; + for( ;; ) { - ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); - require_action( ifa, exit, err = WSAENOBUFS ); + iaaListSize = 0; + err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, NULL, &iaaListSize ); + check( err == ERROR_BUFFER_OVERFLOW ); + check( iaaListSize >= sizeof( IP_ADAPTER_ADDRESSES ) ); - *next = ifa; - next = &ifa->ifa_next; + iaaList = (IP_ADAPTER_ADDRESSES *) malloc( iaaListSize ); + require_action( iaaList, exit, err = ERROR_NOT_ENOUGH_MEMORY ); - // Fetch the name. $$$ TO DO: Get the real name of the interface. + err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, iaaList, &iaaListSize ); + if( err == ERROR_SUCCESS ) break; - ifa->ifa_name = (char *) malloc( 16 ); - require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); - sprintf( ifa->ifa_name, "%d", i + 1 ); + free( iaaList ); + iaaList = NULL; + ++i; + require( i < 100, exit ); + dlog( kDebugLevelWarning, "%s: retrying GetAdaptersAddresses after %d failure(s) (%d %m)\n", __ROUTINE__, i, err, err ); + } + + for( iaa = iaaList; iaa; iaa = iaa->Next ) + { + IP_ADAPTER_UNICAST_ADDRESS * addr; + + if( iaa->IfIndex > 0xFFFFFF ) + { + dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv4 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->IfIndex ); + } + if( iaa->Ipv6IfIndex > 0xFF ) + { + dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv6 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->Ipv6IfIndex ); + } - // Fetch flags. Note: SIO_ADDRESS_LIST_QUERY does not report flags so just fake IFF_UP and IFF_MULTICAST. + // Skip psuedo and tunnel interfaces. - ifa->ifa_flags = IFF_UP | IFF_MULTICAST; + if( ( iaa->Ipv6IfIndex == 1 ) || ( iaa->IfType == IF_TYPE_TUNNEL ) ) + { + continue; + } - // Fetch addresses. + // Add each address as a separate interface to emulate the way getifaddrs works. - switch( addressList->Address[ i ].lpSockaddr->sa_family ) - { - case AF_INET: + for( addr = iaa->FirstUnicastAddress; addr; addr = addr->Next ) + { + ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); + require_action( ifa, exit, err = WSAENOBUFS ); + + *next = ifa; + next = &ifa->ifa_next; + + // Get the name. + + size = strlen( iaa->AdapterName ) + 1; + ifa->ifa_name = (char *) malloc( size ); + require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_name, iaa->AdapterName, size ); + + // Get interface flags. + + ifa->ifa_flags = 0; + if( iaa->OperStatus == IfOperStatusUp ) + { + ifa->ifa_flags |= IFF_UP; + } + if( iaa->IfType == IF_TYPE_SOFTWARE_LOOPBACK ) + { + ifa->ifa_flags |= IFF_LOOPBACK; + } + if( !( iaa->Flags & IP_ADAPTER_NO_MULTICAST ) ) + { + ifa->ifa_flags |= IFF_MULTICAST; + } + + // Get the interface index. Windows does not have a uniform scheme for IPv4 and IPv6 interface indexes + // so the following is a hack to put IPv4 interface indexes in the upper 16-bits and IPv6 interface indexes + // in the lower 16-bits. This allows the IPv6 interface index to be usable as an IPv6 scope ID directly. + + switch( addr->Address.lpSockaddr->sa_family ) { - struct sockaddr_in * sinptr4; + case AF_INET: + ifa->ifa_extra.index = iaa->IfIndex << 8; + break; + + case AF_INET6: + ifa->ifa_extra.index = iaa->Ipv6IfIndex; + break; - 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; } - default: - break; + // Get addresses. + + switch( addr->Address.lpSockaddr->sa_family ) + { + case AF_INET: + case AF_INET6: + ifa->ifa_addr = (struct sockaddr *) calloc( 1, (size_t) addr->Address.iSockaddrLength ); + require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_addr, addr->Address.lpSockaddr, (size_t) addr->Address.iSockaddrLength ); + break; + + default: + break; + } } } @@ -1901,47 +2345,40 @@ int getifaddrs( struct ifaddrs **outAddrs ) *outAddrs = head; head = NULL; } - err = 0; + err = ERROR_SUCCESS; exit: if( head ) { freeifaddrs( head ); } - if( buffer ) - { - free( buffer ); - } - if( sock != INVALID_SOCKET ) + if( iaaList ) { - closesocket( sock ); + free( iaaList ); } - return( err ); + return( (int) err ); } -#endif // defined( _WIN32_WCE ) ) +#endif // MDNS_WINDOWS_USE_IPV6_IF_ADDRS -#if( !defined( _WIN32_WCE ) ) +#if( !TARGET_OS_WINDOWS_CE ) //=========================================================================================================================== -// getifaddrs +// getifaddrs_ipv4 //=========================================================================================================================== -int getifaddrs( struct ifaddrs **outAddrs ) +mDNSlocal int getifaddrs_ipv4( 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; + 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; sock = INVALID_SOCKET; buffer = NULL; @@ -1953,8 +2390,9 @@ int getifaddrs( struct ifaddrs **outAddrs ) // 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 ); - + err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + n = 0; size = 16 * sizeof( INTERFACE_INFO ); for( ;; ) @@ -1978,11 +2416,15 @@ int getifaddrs( struct ifaddrs **outAddrs ) check( ( actualSize % sizeof( INTERFACE_INFO ) ) == 0 ); n = (int)( actualSize / sizeof( INTERFACE_INFO ) ); - // Process the raw interface list and build a linked list of interfaces. + // Process the raw interface list and build a linked list of IPv4 interfaces. for( i = 0; i < n; ++i ) { ifInfo = &buffer[ i ]; + if( ifInfo->iiAddress.Address.sa_family != AF_INET ) + { + continue; + } ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) ); require_action( ifa, exit, err = WSAENOBUFS ); @@ -1990,55 +2432,157 @@ int getifaddrs( struct ifaddrs **outAddrs ) *next = ifa; next = &ifa->ifa_next; - // Fetch the name. $$$ TO DO: Get the real name of the interface. + // Get the name. ifa->ifa_name = (char *) malloc( 16 ); require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); sprintf( ifa->ifa_name, "%d", i + 1 ); - // Fetch interface flags. + // Get interface flags. ifa->ifa_flags = (u_int) ifInfo->iiFlags; - // Fetch addresses. + // Get 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 ) ); - } + { + struct sockaddr_in * sa4; - 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 ) ); - } + sa4 = &ifInfo->iiAddress.AddressIn; + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa4 ) ); + require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_addr, sa4, sizeof( *sa4 ) ); break; + } - case AF_INET6: - sinptr6 = &ifInfo->iiAddress.AddressIn6; - ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sinptr6 ) ); + default: + break; + } + + // Emulate an interface index. + + ifa->ifa_extra.index = (uint32_t)( i + 1 ); + } + + // 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 // !TARGET_OS_WINDOWS_CE ) + +#if( TARGET_OS_WINDOWS_CE ) +//=========================================================================================================================== +// getifaddrs_ce +//=========================================================================================================================== + +mDNSlocal int getifaddrs_ce( 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 ); + err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + // 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 = calloc( 1, 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; + + // Get the name. + + ifa->ifa_name = (char *) malloc( 16 ); + require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); + sprintf( ifa->ifa_name, "%d", i + 1 ); + + // Get 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; + + // Get addresses. + + switch( addressList->Address[ i ].lpSockaddr->sa_family ) + { + case AF_INET: + { + struct sockaddr_in * sa4; + + sa4 = (struct sockaddr_in *) addressList->Address[ i ].lpSockaddr; + ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa4 ) ); require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); - memcpy( ifa->ifa_addr, sinptr6, sizeof( *sinptr6 ) ); + memcpy( ifa->ifa_addr, sa4, sizeof( *sa4 ) ); 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; } } @@ -2067,20 +2611,20 @@ exit: } return( err ); } -#endif // !defined( _WIN32_WCE ) ) +#endif // TARGET_OS_WINDOWS_CE ) //=========================================================================================================================== // freeifaddrs //=========================================================================================================================== -void freeifaddrs( struct ifaddrs *inAddrs ) +void freeifaddrs( struct ifaddrs *inIFAs ) { 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 ) + for( p = inIFAs; p; p = q ) { q = p->ifa_next; @@ -2118,89 +2662,132 @@ void freeifaddrs( struct ifaddrs *inAddrs ) } } -#if( !defined( _WIN32_WCE ) ) //=========================================================================================================================== -// sock_pton +// CanReceiveUnicast //=========================================================================================================================== -int sock_pton( const char *inString, int inFamily, void *outAddr, size_t inAddrSize, size_t *outAddrSize ) +mDNSlocal mDNSBool CanReceiveUnicast( void ) { - 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; + mDNSBool ok; + SocketRef sock; + struct sockaddr_in addr; - err = WSAStringToAddressA( (char *) inString, inFamily, NULL, (LPSOCKADDR) outAddr, &size ); - if( err != 0 ) goto exit; + // Try to bind to the port without the SO_REUSEADDR option to test if someone else has already bound to it. - if( outAddrSize ) + sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + check_translated_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr ); + ok = IsValidSocket( sock ); + if( ok ) { - *outAddrSize = (size_t) size; + memset( &addr, 0, sizeof( addr ) ); + addr.sin_family = AF_INET; + addr.sin_port = MulticastDNSPort.NotAnInteger; + addr.sin_addr.s_addr = htonl( INADDR_ANY ); + + ok = ( bind( sock, (struct sockaddr *) &addr, sizeof( addr ) ) == 0 ); + close_compat( sock ); } -exit: - return( err ); + dlog( kDebugLevelInfo, DEBUG_NAME "Unicast UDP responses %s\n", ok ? "okay" : "*not allowed*" ); + return( ok ); } //=========================================================================================================================== -// sock_ntop +// GetWindowsVersionString //=========================================================================================================================== -char * sock_ntop( const void *inAddr, size_t inAddrSize, char *inBuffer, size_t inBufferSize ) +mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize ) { - DWORD size; - int err; - DWORD stringSize; +#if( !defined( VER_PLATFORM_WIN32_CE ) ) + #define VER_PLATFORM_WIN32_CE 3 +#endif + + OSStatus err; + OSVERSIONINFO osInfo; + BOOL ok; + const char * versionString; + DWORD platformID; + DWORD majorVersion; + DWORD minorVersion; + DWORD buildNumber; + + versionString = "unknown Windows version"; + + osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); + ok = GetVersionEx( &osInfo ); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + platformID = osInfo.dwPlatformId; + majorVersion = osInfo.dwMajorVersion; + minorVersion = osInfo.dwMinorVersion; + buildNumber = osInfo.dwBuildNumber & 0xFFFF; - if( inAddrSize == 0 ) + if( ( platformID == VER_PLATFORM_WIN32_WINDOWS ) && ( majorVersion == 4 ) ) { - const struct sockaddr * addr; - - addr = (const struct sockaddr *) inAddr; - if( addr->sa_family == AF_INET ) + if( ( minorVersion < 10 ) && ( buildNumber == 950 ) ) + { + versionString = "Windows 95"; + } + else if( ( minorVersion < 10 ) && ( ( buildNumber > 950 ) && ( buildNumber <= 1080 ) ) ) + { + versionString = "Windows 95 SP1"; + } + else if( ( minorVersion < 10 ) && ( buildNumber > 1080 ) ) + { + versionString = "Windows 95 OSR2"; + } + else if( ( minorVersion == 10 ) && ( buildNumber == 1998 ) ) + { + versionString = "Windows 98"; + } + else if( ( minorVersion == 10 ) && ( ( buildNumber > 1998 ) && ( buildNumber < 2183 ) ) ) { - size = sizeof( struct sockaddr_in ); + versionString = "Windows 98 SP1"; } - else if( addr->sa_family == AF_INET6 ) + else if( ( minorVersion == 10 ) && ( buildNumber >= 2183 ) ) { - size = sizeof( struct sockaddr_in6 ); + versionString = "Windows 98 SE"; } - else + else if( minorVersion == 90 ) { - WSASetLastError( WSAEAFNOSUPPORT ); - inBuffer = NULL; - goto exit; + versionString = "Windows ME"; } } - else + else if( platformID == VER_PLATFORM_WIN32_NT ) { - size = (DWORD) inAddrSize; + if( ( majorVersion == 3 ) && ( minorVersion == 51 ) ) + { + versionString = "Windows NT 3.51"; + } + else if( ( majorVersion == 4 ) && ( minorVersion == 0 ) ) + { + versionString = "Windows NT 4"; + } + else if( ( majorVersion == 5 ) && ( minorVersion == 0 ) ) + { + versionString = "Windows 2000"; + } + else if( ( majorVersion == 5 ) && ( minorVersion == 1 ) ) + { + versionString = "Windows XP"; + } + else if( ( majorVersion == 5 ) && ( minorVersion == 2 ) ) + { + versionString = "Windows Server 2003"; + } } - - stringSize = (DWORD) inBufferSize; - err = WSAAddressToStringA( (LPSOCKADDR) inAddr, size, NULL, inBuffer, &stringSize ); - if( err ) + else if( platformID == VER_PLATFORM_WIN32_CE ) { - inBuffer = NULL; + versionString = "Windows CE"; } exit: - return( inBuffer ); + if( inBuffer && ( inBufferSize > 0 ) ) + { + inBufferSize -= 1; + strncpy( inBuffer, versionString, inBufferSize ); + inBuffer[ inBufferSize ] = '\0'; + } + return( err ); } -#endif // !defined( _WIN32_WCE ) diff --git a/mDNSWindows/mDNSWin32.h b/mDNSWindows/mDNSWin32.h index 499f6a6..946fd7e 100755 --- a/mDNSWindows/mDNSWin32.h +++ b/mDNSWindows/mDNSWin32.h @@ -1,8 +1,10 @@ /* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * + * 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 @@ -23,8 +25,13 @@ Change History (most recent first): $Log: mDNSWin32.h,v $ -Revision 1.9.2.1 2004/04/03 05:26:08 bradley -Integrated changes from TOT to remove legacy port 53 support. +Revision 1.11 2004/01/30 02:44:32 bradley +Added support for IPv6 (v4 & v6, v4-only, v6-only, AAAA over v4, etc.). Added support for DNS-SD +InterfaceID<->Interface Index mappings. Added support for loopback usage when no other interfaces +are available. Updated unlock signaling to no longer require timenow - NextScheduledTime to be >= 0 +(it no longer is). Added unicast-capable detection to avoid using unicast when there is other mDNS +software running on the same machine. Removed unneeded sock_XtoY routines. Added support for +reporting HINFO records with the Windows and mDNSResponder version information. Revision 1.10 2003/10/24 23:23:02 bradley Removed legacy port 53 support as it is no longer needed. @@ -67,13 +74,11 @@ 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 "CommonServices.h" -#include -#include -#include +#if( !defined( _WIN32_WCE ) ) + #include +#endif #include "mDNSClientAPI.h" @@ -81,14 +86,6 @@ Multicast DNS platform plugin for Win32 extern "C" { #endif -//--------------------------------------------------------------------------------------------------------------------------- -/*! @typedef SocketRef - - @abstract Socket file descriptor alias for improved readability. -*/ - -typedef SOCKET SocketRef; - //--------------------------------------------------------------------------------------------------------------------------- /*! @struct mDNSInterfaceData @@ -99,18 +96,17 @@ typedef struct mDNSInterfaceData mDNSInterfaceData; struct mDNSInterfaceData { mDNSInterfaceData * next; - char name[ 256 ]; + char name[ 128 ]; + uint32_t index; + uint32_t scopeID; SocketRef sock; +#if( !defined( _WIN32_WCE ) ) + LPFN_WSARECVMSG wsaRecvMsgFunctionPtr; +#endif HANDLE readPendingEvent; - NetworkInterfaceInfo hostSet; + NetworkInterfaceInfo interfaceInfo; + mDNSAddr defaultAddr; mDNSBool hostRegistered; - - int sendMulticastCounter; - int sendUnicastCounter; - int sendErrorCounter; - - int recvCounter; - int recvErrorCounter; }; //--------------------------------------------------------------------------------------------------------------------------- @@ -129,8 +125,7 @@ struct mDNS_PlatformSupport_struct HANDLE wakeupEvent; HANDLE initEvent; mStatus initStatus; - - SocketRef interfaceListChangedSocketRef; + SocketRef interfaceListChangedSocket; int interfaceCount; mDNSInterfaceData * interfaceList; DWORD threadID; @@ -152,6 +147,12 @@ struct ifaddrs struct sockaddr * ifa_broadaddr; struct sockaddr * ifa_dstaddr; void * ifa_data; + + struct + { + uint32_t index; + + } ifa_extra; }; //--------------------------------------------------------------------------------------------------------------------------- @@ -170,26 +171,6 @@ int getifaddrs( struct ifaddrs **outAddrs ); 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 -- 2.47.2