From 7f0064bd55e3fa98568d2c359429ff8a38b23a6c Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 15 Dec 2004 21:58:48 +0000 Subject: [PATCH] mDNSResponder-87.tar.gz --- Clients/DNS-SD.xcode/project.pbxproj | 14 +- Clients/DNSServiceBrowser.NET/App.ico | Bin 0 -> 1078 bytes Clients/DNSServiceBrowser.NET/AssemblyInfo.cs | 89 + .../DNSServiceBrowser.NET.csproj | 117 + .../DNSServiceBrowser.cs | 745 ++++ .../DNSServiceBrowser.resx | 102 + Clients/DNSServiceBrowser.m | 2 - ...on-Info.plist => DNSServiceReg-Info.plist} | 2 +- ...SServiceRegistration.m => DNSServiceReg.m} | 4 +- .../classes.nib | 0 .../info.nib | 0 .../objects.nib | Bin Clients/ExplorerPlugin/About.cpp | 63 + Clients/ExplorerPlugin/About.h | 27 + .../ExplorerPlugin/ClassFactory.cpp | 10 +- .../ExplorerPlugin/ClassFactory.h | 10 +- .../ExplorerPlugin/ExplorerBar.cpp | 107 +- .../ExplorerPlugin/ExplorerBar.h | 19 +- .../ExplorerPlugin/ExplorerBarWindow.cpp | 373 +- .../ExplorerPlugin/ExplorerBarWindow.h | 55 +- .../ExplorerPlugin/ExplorerPlugin.cpp | 71 +- .../ExplorerPlugin/ExplorerPlugin.def | 8 +- .../ExplorerPlugin/ExplorerPlugin.h | 10 +- .../ExplorerPlugin/ExplorerPlugin.rc | 70 +- .../ExplorerPlugin/ExplorerPlugin.vcproj | 185 +- .../ExplorerPlugin/LoginDialog.cpp | 10 +- .../ExplorerPlugin/LoginDialog.h | 10 +- .../ExplorerPlugin/ReadMe.txt | 2 +- .../ExplorerPlugin/Resource.h | 13 +- .../ExplorerPlugin/StdAfx.cpp | 10 +- .../ExplorerPlugin/StdAfx.h | 10 +- Clients/ExplorerPlugin/res/about.bmp | Bin 0 -> 137944 bytes Clients/ExplorerPlugin/res/button-2k.ico | Bin 0 -> 10342 bytes Clients/ExplorerPlugin/res/button-xp.ico | Bin 0 -> 7574 bytes Clients/ExplorerPlugin/res/cold.ico | Bin 0 -> 10342 bytes Clients/ExplorerPlugin/res/hot.ico | Bin 0 -> 10342 bytes Clients/ExplorerPlugin/res/logo.bmp | Bin 0 -> 822 bytes Clients/Java/BrowserApp.java | 74 +- Clients/Java/BrowserApp.manifest | 2 + Clients/Java/DNSSDUnitTest.java | 24 +- Clients/Java/SimpleChat.java | 78 +- Clients/Java/SimpleChat.manifest | 2 + Clients/Java/SwingBrowseListener.java | 64 +- Clients/Java/SwingDomainListener.java | 64 +- Clients/Java/SwingQueryListener.java | 64 +- Clients/Java/SwingResolveListener.java | 64 +- Clients/Java/nmakefile | 106 + Clients/Makefile | 14 +- Clients/PrinterSetupWizard/About.cpp | 31 + Clients/PrinterSetupWizard/About.h | 21 + Clients/PrinterSetupWizard/FirstPage.cpp | 93 + Clients/PrinterSetupWizard/FirstPage.h | 63 + Clients/PrinterSetupWizard/FourthPage.cpp | 143 + Clients/PrinterSetupWizard/FourthPage.h | 71 + .../PrinterSetupWizard/PrinterSetupWizard.ncb | 1 + .../PrinterSetupWizard/PrinterSetupWizard.rc | 346 ++ .../PrinterSetupWizard.vcproj | 269 ++ .../PrinterSetupWizardApp.cpp | 117 + .../PrinterSetupWizardApp.h | 47 +- .../PrinterSetupWizardSheet.cpp | 1085 +++++ .../PrinterSetupWizardSheet.h | 207 + Clients/PrinterSetupWizard/ReadMe.txt | 94 + Clients/PrinterSetupWizard/SecondPage.cpp | 347 ++ Clients/PrinterSetupWizard/SecondPage.h | 95 + Clients/PrinterSetupWizard/ThirdPage.cpp | 1224 ++++++ Clients/PrinterSetupWizard/ThirdPage.h | 133 + Clients/PrinterSetupWizard/UtilTypes.h | 144 + Clients/PrinterSetupWizard/res/Info.ico | Bin 0 -> 23558 bytes .../PrinterSetupWizard/res/NetworkPrinter.ico | Bin 0 -> 77214 bytes Clients/PrinterSetupWizard/res/Print.ico | Bin 0 -> 77214 bytes .../res/PrinterSetupWizard.ico | Bin 0 -> 77214 bytes .../res/PrinterSetupWizard.manifest | 22 + .../res/PrinterSetupWizard.rc2 | 13 + Clients/PrinterSetupWizard/res/Thumbs.db | Bin 0 -> 6144 bytes Clients/PrinterSetupWizard/res/about.bmp | Bin 0 -> 254802 bytes .../PrinterSetupWizard/res/banner_icon.bmp | Bin 0 -> 7308 bytes Clients/PrinterSetupWizard/res/watermark.bmp | Bin 0 -> 162908 bytes Clients/PrinterSetupWizard/resource.h | 74 + Clients/PrinterSetupWizard/stdafx.cpp | 34 + Clients/PrinterSetupWizard/stdafx.h | 71 + Clients/SimpleChat.NET/App.ico | Bin 0 -> 1078 bytes Clients/SimpleChat.NET/AssemblyInfo.cs | 90 + Clients/SimpleChat.NET/SimpleChat.NET.csproj | 116 + Clients/SimpleChat.NET/SimpleChat.cs | 757 ++++ Clients/SimpleChat.NET/SimpleChat.resx | 102 + Clients/dns-sd.c | 492 ++- Makefile | 2 +- README.txt | 23 + mDNSCore/DNSCommon.c | 538 ++- mDNSCore/DNSCommon.h | 139 +- mDNSCore/DNSDigest.c | 71 +- mDNSCore/mDNS.c | 2073 ++++++---- mDNSCore/mDNSDebug.h | 10 +- .../{mDNSClientAPI.h => mDNSEmbeddedAPI.h} | 923 ++++- mDNSCore/uDNS.c | 3532 ++++++++++++----- mDNSCore/uDNS.h | 95 +- mDNSMacOS9/CarbonResource.r | 2 - mDNSMacOS9/Mac OS Test Responder.c | 16 +- mDNSMacOS9/Mac OS Test Searcher.c | 24 +- mDNSMacOS9/Responder.c | 2 - mDNSMacOS9/Searcher.c | 2 - mDNSMacOS9/SubTypeTester.c | 248 ++ mDNSMacOS9/mDNS.mcp | Bin 967336 -> 1002330 bytes mDNSMacOS9/mDNSLibrary.c | 10 +- mDNSMacOS9/mDNSLibraryLoader.c | 2 - mDNSMacOS9/mDNSLibraryResources.r | 51 +- mDNSMacOS9/mDNSMacOS9.c | 84 +- mDNSMacOS9/mDNSMacOS9.h | 2 - mDNSMacOS9/mDNSPrefix.h | 13 +- mDNSMacOSX/DNSServiceDiscoveryDefines.h | 6 +- mDNSMacOSX/DNSServiceDiscoveryReply.defs | 2 - mDNSMacOSX/DNSServiceDiscoveryRequest.defs | 5 +- mDNSMacOSX/LegacyNATTraversal.c | 3010 ++++++++++++++ mDNSMacOSX/SamplemDNSClient.c | 37 +- mDNSMacOSX/daemon.c | 1347 ++++--- mDNSMacOSX/{CFSocket.c => mDNSMacOSX.c} | 1946 ++++++--- mDNSMacOSX/mDNSMacOSX.h | 62 +- .../{CFSocketPuma.c => mDNSMacOSXPuma.c} | 11 +- .../mDNSResponder.pbproj/project.pbxproj | 514 ++- mDNSPosix/Client.c | 25 +- mDNSPosix/ExampleClientApp.c | 23 +- mDNSPosix/ExampleClientApp.h | 8 +- mDNSPosix/Identify.c | 80 +- mDNSPosix/Makefile | 275 +- mDNSPosix/NetMonitor.c | 90 +- mDNSPosix/PosixDaemon.c | 351 +- mDNSPosix/ProxyResponder.c | 87 +- mDNSPosix/ReadMe.txt | 32 +- mDNSPosix/Responder.c | 314 +- mDNSPosix/Services.txt | 9 +- mDNSPosix/dnsextd.c | 1983 +++++++++ mDNSPosix/libnss_mdns.8 | 149 + mDNSPosix/mDNSPosix.c | 184 +- mDNSPosix/mDNSPosix.h | 10 +- mDNSPosix/mDNSUNP.c | 129 +- mDNSPosix/mDNSUNP.h | 22 +- mDNSPosix/mdnsd.sh | 14 +- mDNSPosix/nss_ReadMe.txt | 125 + mDNSPosix/nss_mdns.c | 2732 +++++++++++++ mDNSPosix/nss_mdns.conf | 7 + mDNSPosix/nss_mdns.conf.5 | 136 + mDNSShared/GenLinkedList.c | 2 - mDNSShared/GenLinkedList.h | 2 - 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/PlatformCommon.c | 131 + mDNSShared/PlatformCommon.h | 36 + mDNSShared/dns-sd.1 | 208 + mDNSShared/dns_sd.h | 566 ++- mDNSShared/dnsextd.8 | 80 + mDNSShared/dnssd_clientlib.c | 70 +- mDNSShared/dnssd_clientshim.c | 40 +- mDNSShared/dnssd_clientstub.c | 948 +++-- mDNSShared/dnssd_ipc.c | 209 +- mDNSShared/dnssd_ipc.h | 202 +- mDNSShared/mDNS.1 | 69 +- mDNSShared/mDNSDebug.c | 42 +- mDNSShared/mDNSResponder.8 | 6 + mDNSShared/uds_daemon.c | 2308 +++++++---- mDNSShared/uds_daemon.h | 61 +- mDNSVxWorks/mDNSVxWorks.c | 107 +- mDNSVxWorks/mDNSVxWorks.h | 10 +- .../Applications/DNSServiceTest/ToolWin32.mcp | Bin 284642 -> 0 bytes .../ExplorerPlugin/ExplorerPlugin.sln | 21 - mDNSWindows/Applications/NSPTool/Tool.mcp | Bin 306146 -> 0 bytes .../Applications/SystemService/Service.c | 829 ---- .../Applications/SystemService/Service.mcp | Bin 299890 -> 0 bytes .../SystemService/Service2002.sln | 21 - .../SystemService/Service2002.vcproj | 196 - .../Applications/SystemServiceTest/Tool.mcp | Bin 398728 -> 0 bytes .../SystemServiceTest/Tool2002.sln | 21 - mDNSWindows/Applications/mdnsNSP/NSP.c | 1367 ------- mDNSWindows/Applications/mdnsNSP/NSP.def | 36 - mDNSWindows/Applications/mdnsNSP/NSP.mcp | Bin 198118 -> 0 bytes mDNSWindows/Applications/mdnsNSP/ReadMe.txt | 15 - mDNSWindows/CommonServices.h | 2 - mDNSWindows/DLL.NET/AssemblyInfo.cpp | 100 + mDNSWindows/DLL.NET/PString.h | 86 + .../Resource.h => DLL.NET/Stdafx.cpp} | 69 +- mDNSWindows/DLL.NET/Stdafx.h | 40 + mDNSWindows/DLL.NET/dnssd_NET.cpp | 1276 ++++++ mDNSWindows/DLL.NET/dnssd_NET.h | 1424 +++++++ mDNSWindows/DLL.NET/dnssd_NET.ico | Bin 0 -> 1078 bytes mDNSWindows/DLL.NET/dnssd_NET.rc | 113 + mDNSWindows/DLL.NET/dnssd_NET.vcproj | 154 + mDNSWindows/DLL.NET/resource.h | 3 + mDNSWindows/DLL/dll.aps | Bin 0 -> 18692 bytes mDNSWindows/DLL/dll.rc | 102 + mDNSWindows/{Applications => }/DLL/dllmain.c | 7 +- mDNSWindows/{Applications => }/DLL/dnssd.def | 23 +- mDNSWindows/DLL/dnssd.vcproj | 178 + mDNSWindows/DLL/resource.h | 27 + mDNSWindows/DNSSD.c | 1720 -------- mDNSWindows/DNSSD.h | 1689 -------- mDNSWindows/DNSSDDirect.c | 1863 --------- mDNSWindows/DNSSDDirect.h | 285 -- .../Windows/ApplicationVS2002.sln | 0 .../Windows/ApplicationVS2002.vcproj | 4 +- .../Windows/ApplicationVS2003.sln | 0 .../Windows/ApplicationVS2003.vcproj | 4 +- .../Windows/Resources/Application.ico | Bin .../Windows/Resources/Application.rc | 18 +- .../Windows/Resources/Application.rc2 | 10 +- .../Windows/Resources/Resource.h | 0 .../Windows/Sources/AboutDialog.cpp | 12 +- .../Windows/Sources/AboutDialog.h | 12 +- .../Windows/Sources/Application.cpp | 12 +- .../Windows/Sources/Application.h | 12 +- .../Windows/Sources/ChooserDialog.cpp | 12 +- .../Windows/Sources/ChooserDialog.h | 12 +- .../Windows/Sources/LoginDialog.cpp | 5 +- .../Windows/Sources/LoginDialog.h | 5 +- .../Windows/Sources/StdAfx.cpp | 12 +- .../Windows/Sources/StdAfx.h | 12 +- .../WindowsCE/Application.vcc | 0 .../WindowsCE/Application.vcp | 0 .../WindowsCE/Application.vcw | 0 .../WindowsCE/Resources/Application.ico | Bin .../WindowsCE/Resources/Application.rc | 6 +- .../WindowsCE/Resources/Application.rc2 | 0 .../WindowsCE/Resources/newres.h | 0 .../WindowsCE/Resources/resource.h | 0 .../WindowsCE/Sources/Application.cpp | 10 +- .../WindowsCE/Sources/Application.h | 10 +- .../WindowsCE/Sources/BrowserDialog.cpp | 10 +- .../WindowsCE/Sources/BrowserDialog.h | 10 +- .../WindowsCE/Sources/StdAfx.cpp | 10 +- .../WindowsCE/Sources/StdAfx.h | 10 +- .../{Applications => }/DNSServiceTest/Tool.c | 61 +- .../DNSServiceTest/ToolPrefixWindows.h | 5 +- .../DNSServiceTest/ToolPrefixWindowsDebug.h | 5 +- mDNSWindows/DNSServiceTest/ToolWin32.mcp | Bin 0 -> 127071 bytes .../DNSServiceTest/ToolWin32VS2002.sln | 0 .../DNSServiceTest/ToolWin32VS2002.vcproj | 2 +- .../DNSServiceTest/ToolWin32VS2003.sln | 0 .../DNSServiceTest/ToolWin32VS2003.vcproj | 2 +- mDNSWindows/DNSServices/DNSServiceDiscovery.c | 12 +- mDNSWindows/DNSServices/DNSServiceDiscovery.h | 2 - mDNSWindows/DNSServices/DNSServices.c | 38 +- mDNSWindows/DNSServices/DNSServices.h | 9 +- mDNSWindows/DebugServices.c | 18 +- mDNSWindows/DebugServices.h | 2 - mDNSWindows/Installer.vct | Bin 40028 -> 0 bytes mDNSWindows/Installer/Main.ism | Bin 0 -> 195584 bytes mDNSWindows/Installer/SDK.ism | Bin 0 -> 189952 bytes .../Service.rc => Java/jdns_sd.rc} | 67 +- mDNSWindows/{Applications => }/Java/makefile | 27 +- mDNSWindows/NSPTool/NSPTool.aps | Bin 0 -> 34296 bytes .../NSPTool/Tool.c => NSPTool/NSPTool.c} | 43 +- mDNSWindows/NSPTool/NSPTool.rc | 102 + .../dnssd.vcproj => NSPTool/NSPTool.vcproj} | 299 +- .../{Applications => }/NSPTool/Prefix.h | 5 +- mDNSWindows/NSPTool/resource.h | 27 + mDNSWindows/README.txt | 10 +- mDNSWindows/RMxClient.c | 1302 ------ mDNSWindows/RMxClient.h | 307 -- mDNSWindows/RMxCommon.c | 1501 ------- mDNSWindows/RMxCommon.h | 662 --- mDNSWindows/RMxServer.c | 1656 -------- mDNSWindows/RMxServer.h | 119 - .../SystemService/EventLogMessages.bin | Bin mDNSWindows/SystemService/Firewall.cpp | 330 ++ mDNSWindows/SystemService/Firewall.h | 59 + .../{Applications => }/SystemService/Prefix.h | 5 +- mDNSWindows/SystemService/Service.aps | Bin 0 -> 3388 bytes mDNSWindows/SystemService/Service.c | 1892 +++++++++ mDNSWindows/SystemService/Service.mcp | Bin 0 -> 126095 bytes mDNSWindows/SystemService/Service.rc | 113 + mDNSWindows/SystemService/Service.vcproj | 199 + mDNSWindows/SystemService/resource.h | 17 + mDNSWindows/SystemService/resrc1.h | 15 + .../SystemServiceTest/Prefix.h | 5 +- .../SystemServiceTest/Tool.c | 10 +- .../Tool.mcp} | Bin 198118 -> 210145 bytes .../SystemServiceTest/Tool2002.vcproj | 0 mDNSWindows/WinServices.cpp | 80 + mDNSWindows/WinServices.h | 47 + mDNSWindows/WinVersRes.h | 90 + mDNSWindows/mDNSWin32.c | 467 ++- mDNSWindows/mDNSWin32.h | 78 +- mDNSWindows/mdnsNSP/ReadMe.txt | 15 + mDNSWindows/mdnsNSP/mdnsNSP.aps | Bin 0 -> 34332 bytes .../{Applications => }/mdnsNSP/mdnsNSP.c | 127 +- .../{Applications => }/mdnsNSP/mdnsNSP.def | 12 +- mDNSWindows/mdnsNSP/mdnsNSP.rc | 104 + mDNSWindows/mdnsNSP/mdnsNSP.vcproj | 160 + mDNSWindows/mdnsNSP/resource.h | 27 + 298 files changed, 37799 insertions(+), 20430 deletions(-) create mode 100755 Clients/DNSServiceBrowser.NET/App.ico create mode 100755 Clients/DNSServiceBrowser.NET/AssemblyInfo.cs create mode 100755 Clients/DNSServiceBrowser.NET/DNSServiceBrowser.NET.csproj create mode 100755 Clients/DNSServiceBrowser.NET/DNSServiceBrowser.cs create mode 100755 Clients/DNSServiceBrowser.NET/DNSServiceBrowser.resx rename Clients/{DNSServiceRegistration-Info.plist => DNSServiceReg-Info.plist} (95%) rename Clients/{DNSServiceRegistration.m => DNSServiceReg.m} (99%) rename Clients/{DNSServiceRegistration.nib => DNSServiceReg.nib}/classes.nib (100%) rename Clients/{DNSServiceRegistration.nib => DNSServiceReg.nib}/info.nib (100%) rename Clients/{DNSServiceRegistration.nib => DNSServiceReg.nib}/objects.nib (100%) create mode 100644 Clients/ExplorerPlugin/About.cpp create mode 100644 Clients/ExplorerPlugin/About.h rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ClassFactory.cpp (95%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ClassFactory.h (88%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ExplorerBar.cpp (84%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ExplorerBar.h (83%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ExplorerBarWindow.cpp (73%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ExplorerBarWindow.h (83%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ExplorerPlugin.cpp (83%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ExplorerPlugin.def (82%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ExplorerPlugin.h (86%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ExplorerPlugin.rc (66%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ExplorerPlugin.vcproj (58%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/LoginDialog.cpp (93%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/LoginDialog.h (88%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/ReadMe.txt (82%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/Resource.h (57%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/StdAfx.cpp (81%) rename {mDNSWindows/Applications => Clients}/ExplorerPlugin/StdAfx.h (86%) create mode 100644 Clients/ExplorerPlugin/res/about.bmp create mode 100755 Clients/ExplorerPlugin/res/button-2k.ico create mode 100755 Clients/ExplorerPlugin/res/button-xp.ico create mode 100644 Clients/ExplorerPlugin/res/cold.ico create mode 100644 Clients/ExplorerPlugin/res/hot.ico create mode 100644 Clients/ExplorerPlugin/res/logo.bmp create mode 100644 Clients/Java/BrowserApp.manifest create mode 100644 Clients/Java/SimpleChat.manifest create mode 100644 Clients/Java/nmakefile create mode 100644 Clients/PrinterSetupWizard/About.cpp create mode 100644 Clients/PrinterSetupWizard/About.h create mode 100644 Clients/PrinterSetupWizard/FirstPage.cpp create mode 100644 Clients/PrinterSetupWizard/FirstPage.h create mode 100644 Clients/PrinterSetupWizard/FourthPage.cpp create mode 100644 Clients/PrinterSetupWizard/FourthPage.h create mode 100644 Clients/PrinterSetupWizard/PrinterSetupWizard.ncb create mode 100644 Clients/PrinterSetupWizard/PrinterSetupWizard.rc create mode 100644 Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj create mode 100644 Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp rename mDNSWindows/Applications/mdnsNSP/Prefix.h => Clients/PrinterSetupWizard/PrinterSetupWizardApp.h (62%) create mode 100644 Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp create mode 100644 Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h create mode 100644 Clients/PrinterSetupWizard/ReadMe.txt create mode 100644 Clients/PrinterSetupWizard/SecondPage.cpp create mode 100644 Clients/PrinterSetupWizard/SecondPage.h create mode 100644 Clients/PrinterSetupWizard/ThirdPage.cpp create mode 100644 Clients/PrinterSetupWizard/ThirdPage.h create mode 100644 Clients/PrinterSetupWizard/UtilTypes.h create mode 100644 Clients/PrinterSetupWizard/res/Info.ico create mode 100644 Clients/PrinterSetupWizard/res/NetworkPrinter.ico create mode 100644 Clients/PrinterSetupWizard/res/Print.ico create mode 100644 Clients/PrinterSetupWizard/res/PrinterSetupWizard.ico create mode 100644 Clients/PrinterSetupWizard/res/PrinterSetupWizard.manifest create mode 100644 Clients/PrinterSetupWizard/res/PrinterSetupWizard.rc2 create mode 100644 Clients/PrinterSetupWizard/res/Thumbs.db create mode 100644 Clients/PrinterSetupWizard/res/about.bmp create mode 100644 Clients/PrinterSetupWizard/res/banner_icon.bmp create mode 100644 Clients/PrinterSetupWizard/res/watermark.bmp create mode 100644 Clients/PrinterSetupWizard/resource.h create mode 100644 Clients/PrinterSetupWizard/stdafx.cpp create mode 100644 Clients/PrinterSetupWizard/stdafx.h create mode 100755 Clients/SimpleChat.NET/App.ico create mode 100755 Clients/SimpleChat.NET/AssemblyInfo.cs create mode 100755 Clients/SimpleChat.NET/SimpleChat.NET.csproj create mode 100755 Clients/SimpleChat.NET/SimpleChat.cs create mode 100755 Clients/SimpleChat.NET/SimpleChat.resx rename mDNSCore/{mDNSClientAPI.h => mDNSEmbeddedAPI.h} (72%) create mode 100644 mDNSMacOS9/SubTypeTester.c create mode 100644 mDNSMacOSX/LegacyNATTraversal.c rename mDNSMacOSX/{CFSocket.c => mDNSMacOSX.c} (53%) rename mDNSMacOSX/{CFSocketPuma.c => mDNSMacOSXPuma.c} (96%) create mode 100644 mDNSPosix/dnsextd.c create mode 100755 mDNSPosix/libnss_mdns.8 create mode 100755 mDNSPosix/nss_ReadMe.txt create mode 100755 mDNSPosix/nss_mdns.c create mode 100755 mDNSPosix/nss_mdns.conf create mode 100755 mDNSPosix/nss_mdns.conf.5 create mode 100644 mDNSShared/PlatformCommon.c create mode 100644 mDNSShared/PlatformCommon.h create mode 100644 mDNSShared/dns-sd.1 create mode 100644 mDNSShared/dnsextd.8 delete mode 100644 mDNSWindows/Applications/DNSServiceTest/ToolWin32.mcp delete mode 100644 mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.sln delete mode 100644 mDNSWindows/Applications/NSPTool/Tool.mcp delete mode 100644 mDNSWindows/Applications/SystemService/Service.c delete mode 100644 mDNSWindows/Applications/SystemService/Service.mcp delete mode 100644 mDNSWindows/Applications/SystemService/Service2002.sln delete mode 100644 mDNSWindows/Applications/SystemService/Service2002.vcproj delete mode 100644 mDNSWindows/Applications/SystemServiceTest/Tool.mcp delete mode 100644 mDNSWindows/Applications/SystemServiceTest/Tool2002.sln delete mode 100644 mDNSWindows/Applications/mdnsNSP/NSP.c delete mode 100644 mDNSWindows/Applications/mdnsNSP/NSP.def delete mode 100644 mDNSWindows/Applications/mdnsNSP/NSP.mcp delete mode 100644 mDNSWindows/Applications/mdnsNSP/ReadMe.txt create mode 100755 mDNSWindows/DLL.NET/AssemblyInfo.cpp create mode 100755 mDNSWindows/DLL.NET/PString.h rename mDNSWindows/{Applications/SystemService/Resource.h => DLL.NET/Stdafx.cpp} (76%) mode change 100644 => 100755 create mode 100755 mDNSWindows/DLL.NET/Stdafx.h create mode 100755 mDNSWindows/DLL.NET/dnssd_NET.cpp create mode 100755 mDNSWindows/DLL.NET/dnssd_NET.h create mode 100755 mDNSWindows/DLL.NET/dnssd_NET.ico create mode 100755 mDNSWindows/DLL.NET/dnssd_NET.rc create mode 100755 mDNSWindows/DLL.NET/dnssd_NET.vcproj create mode 100755 mDNSWindows/DLL.NET/resource.h create mode 100644 mDNSWindows/DLL/dll.aps create mode 100644 mDNSWindows/DLL/dll.rc rename mDNSWindows/{Applications => }/DLL/dllmain.c (91%) rename mDNSWindows/{Applications => }/DLL/dnssd.def (79%) create mode 100644 mDNSWindows/DLL/dnssd.vcproj create mode 100644 mDNSWindows/DLL/resource.h delete mode 100644 mDNSWindows/DNSSD.c delete mode 100644 mDNSWindows/DNSSD.h delete mode 100644 mDNSWindows/DNSSDDirect.c delete mode 100644 mDNSWindows/DNSSDDirect.h rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/ApplicationVS2002.sln (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj (98%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/ApplicationVS2003.sln (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj (98%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Resources/Application.ico (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Resources/Application.rc (95%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Resources/Application.rc2 (86%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Resources/Resource.h (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp (93%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/AboutDialog.h (92%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/Application.cpp (94%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/Application.h (92%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp (99%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/ChooserDialog.h (95%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp (98%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/LoginDialog.h (96%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/StdAfx.cpp (86%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/Windows/Sources/StdAfx.h (91%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Application.vcc (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Application.vcp (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Application.vcw (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Resources/Application.ico (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Resources/Application.rc (96%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Resources/Application.rc2 (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Resources/newres.h (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Resources/resource.h (100%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Sources/Application.cpp (94%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Sources/Application.h (92%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp (98%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h (94%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp (86%) rename mDNSWindows/{Applications => }/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h (91%) rename mDNSWindows/{Applications => }/DNSServiceTest/Tool.c (95%) rename mDNSWindows/{Applications => }/DNSServiceTest/ToolPrefixWindows.h (95%) rename mDNSWindows/{Applications => }/DNSServiceTest/ToolPrefixWindowsDebug.h (95%) create mode 100644 mDNSWindows/DNSServiceTest/ToolWin32.mcp rename mDNSWindows/{Applications => }/DNSServiceTest/ToolWin32VS2002.sln (100%) rename mDNSWindows/{Applications => }/DNSServiceTest/ToolWin32VS2002.vcproj (99%) rename mDNSWindows/{Applications => }/DNSServiceTest/ToolWin32VS2003.sln (100%) rename mDNSWindows/{Applications => }/DNSServiceTest/ToolWin32VS2003.vcproj (99%) delete mode 100644 mDNSWindows/Installer.vct create mode 100644 mDNSWindows/Installer/Main.ism create mode 100644 mDNSWindows/Installer/SDK.ism rename mDNSWindows/{Applications/SystemService/Service.rc => Java/jdns_sd.rc} (51%) rename mDNSWindows/{Applications => }/Java/makefile (87%) mode change 100755 => 100644 create mode 100644 mDNSWindows/NSPTool/NSPTool.aps rename mDNSWindows/{Applications/NSPTool/Tool.c => NSPTool/NSPTool.c} (94%) create mode 100644 mDNSWindows/NSPTool/NSPTool.rc rename mDNSWindows/{Applications/DLL/dnssd.vcproj => NSPTool/NSPTool.vcproj} (50%) rename mDNSWindows/{Applications => }/NSPTool/Prefix.h (94%) create mode 100644 mDNSWindows/NSPTool/resource.h delete mode 100644 mDNSWindows/RMxClient.c delete mode 100644 mDNSWindows/RMxClient.h delete mode 100644 mDNSWindows/RMxCommon.c delete mode 100644 mDNSWindows/RMxCommon.h delete mode 100644 mDNSWindows/RMxServer.c delete mode 100644 mDNSWindows/RMxServer.h rename mDNSWindows/{Applications => }/SystemService/EventLogMessages.bin (100%) create mode 100755 mDNSWindows/SystemService/Firewall.cpp create mode 100755 mDNSWindows/SystemService/Firewall.h rename mDNSWindows/{Applications => }/SystemService/Prefix.h (95%) create mode 100644 mDNSWindows/SystemService/Service.aps create mode 100644 mDNSWindows/SystemService/Service.c create mode 100644 mDNSWindows/SystemService/Service.mcp create mode 100644 mDNSWindows/SystemService/Service.rc create mode 100644 mDNSWindows/SystemService/Service.vcproj create mode 100644 mDNSWindows/SystemService/resource.h create mode 100644 mDNSWindows/SystemService/resrc1.h rename mDNSWindows/{Applications => }/SystemServiceTest/Prefix.h (94%) rename mDNSWindows/{Applications => }/SystemServiceTest/Tool.c (99%) rename mDNSWindows/{Applications/mdnsNSP/mdnsNSP.mcp => SystemServiceTest/Tool.mcp} (50%) rename mDNSWindows/{Applications => }/SystemServiceTest/Tool2002.vcproj (100%) create mode 100644 mDNSWindows/WinServices.cpp create mode 100644 mDNSWindows/WinServices.h create mode 100644 mDNSWindows/WinVersRes.h create mode 100644 mDNSWindows/mdnsNSP/ReadMe.txt create mode 100644 mDNSWindows/mdnsNSP/mdnsNSP.aps rename mDNSWindows/{Applications => }/mdnsNSP/mdnsNSP.c (90%) rename mDNSWindows/{Applications => }/mdnsNSP/mdnsNSP.def (77%) create mode 100644 mDNSWindows/mdnsNSP/mdnsNSP.rc create mode 100644 mDNSWindows/mdnsNSP/mdnsNSP.vcproj create mode 100644 mDNSWindows/mdnsNSP/resource.h diff --git a/Clients/DNS-SD.xcode/project.pbxproj b/Clients/DNS-SD.xcode/project.pbxproj index 46805b9..574ad8b 100644 --- a/Clients/DNS-SD.xcode/project.pbxproj +++ b/Clients/DNS-SD.xcode/project.pbxproj @@ -151,7 +151,7 @@ GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; GCC_WARN_UNKNOWN_PRAGMAS = NO; - HEADER_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = ../mDNSShared; INSTALL_PATH = "$(HOME)/bin"; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = ""; @@ -282,6 +282,7 @@ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO; GCC_WARN_UNKNOWN_PRAGMAS = NO; + HEADER_SEARCH_PATHS = ../mDNSShared; INFOPLIST_FILE = "DNSServiceBrowser-Info.plist"; INSTALL_PATH = "$(USER_APPS_DIR)"; OTHER_CFLAGS = ""; @@ -396,7 +397,8 @@ 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"; + HEADER_SEARCH_PATHS = ../mDNSShared; + INFOPLIST_FILE = "DNSServiceReg-Info.plist"; INSTALL_PATH = "$(USER_APPS_DIR)"; OTHER_CFLAGS = ""; OTHER_LDFLAGS = "-framework Foundation -framework AppKit"; @@ -436,7 +438,7 @@ CFBundleVersion 1.0.0d1 NSMainNibFile - DNSServiceRegistration + DNSServiceReg NSPrincipalClass NSApplication @@ -457,7 +459,7 @@ isa = PBXContainerItemProxy; proxyType = 1; remoteGlobalIDString = FF1E351206711B5C003DD5BC; - remoteInfo = DNSServiceRegistration; + remoteInfo = DNSServiceReg; }; FF1E351806711B6A003DD5BC = { isa = PBXTargetDependency; @@ -468,7 +470,7 @@ fileEncoding = 4; isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; - path = DNSServiceRegistration.m; + path = DNSServiceReg.m; refType = 4; sourceTree = ""; }; @@ -481,7 +483,7 @@ FF1E352506711BD6003DD5BC = { isa = PBXFileReference; lastKnownFileType = wrapper.nib; - path = DNSServiceRegistration.nib; + path = DNSServiceReg.nib; refType = 4; sourceTree = ""; }; diff --git a/Clients/DNSServiceBrowser.NET/App.ico b/Clients/DNSServiceBrowser.NET/App.ico new file mode 100755 index 0000000000000000000000000000000000000000..3a5525fd794f7a7c5c8e6187f470ea3af38cd2b6 GIT binary patch literal 1078 zcmeHHJr05}7=1t!Hp3A*8IHkVf+j?-!eHY14Gtcw1Eb*_9>Bq^zETJ@GKj{_2j4$w zo9}xCh!8{T3=X##Skq>ikMjsvB|y%crWBM2iW(4pI}c%z6%lW!=~4v77#3{z!dmB1 z__&l)-{KUYR+|8|;wB^R|9ET$J@(@=#rd^=)qs85?vAy(PSF5CyNkus435LVkZ$rj zNw|JG-P7^hF<(;#o*Vk}5R#e|^13tBbQkeF?djULtvqyxd3<{9 literal 0 HcmV?d00001 diff --git a/Clients/DNSServiceBrowser.NET/AssemblyInfo.cs b/Clients/DNSServiceBrowser.NET/AssemblyInfo.cs new file mode 100755 index 0000000..793362b --- /dev/null +++ b/Clients/DNSServiceBrowser.NET/AssemblyInfo.cs @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1997-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: AssemblyInfo.cs,v $ +Revision 1.1 2004/07/19 07:54:24 shersche +Initial revision + + +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.NET.csproj b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.NET.csproj new file mode 100755 index 0000000..d43cf10 --- /dev/null +++ b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.NET.csproj @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.cs b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.cs new file mode 100755 index 0000000..75bbc93 --- /dev/null +++ b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.cs @@ -0,0 +1,745 @@ +/* + * Copyright (c) 1997-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: DNSServiceBrowser.cs,v $ +Revision 1.5 2004/09/21 16:26:58 shersche +Check to make sure browse list selected item is not null before resolving +Submitted by: prepin@gmail.com + +Revision 1.4 2004/09/13 19:38:17 shersche +Changed code to reflect namespace and type changes to dnssd.NET library + +Revision 1.3 2004/09/11 00:38:14 shersche +DNSService APIs now assume port in host format. Check for null text record in resolve callback. + +Revision 1.2 2004/07/22 23:15:25 shersche +Fix service names for teleport, tftp, and bootps + +Revision 1.1 2004/07/19 07:54:24 shersche +Initial revision + + + +*/ + +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Data; +using System.Text; +using Apple.DNSSD; + +namespace DNSServiceBrowser_NET +{ + /// + /// Summary description for Form1. + /// + public class Form1 : System.Windows.Forms.Form + { + private System.Windows.Forms.ComboBox typeBox; + private System.Windows.Forms.ListBox browseList; + private ServiceRef browser = null; + private ServiceRef resolver = null; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + + // + // These delegates are invoked as a result of DNSService + // operation. + // + delegate void AddServiceCallback(BrowseData data); + delegate void RemoveServiceCallback(BrowseData data); + delegate void ResolveServiceCallback(ResolveData data); + + AddServiceCallback addServiceCallback; + RemoveServiceCallback removeServiceCallback; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox nameField; + private System.Windows.Forms.TextBox typeField; + private System.Windows.Forms.TextBox domainField; + private System.Windows.Forms.TextBox hostField; + private System.Windows.Forms.TextBox portField; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.ListBox serviceTextField; + ResolveServiceCallback resolveServiceCallback; + + public Form1() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + addServiceCallback = new AddServiceCallback(OnAddService); + removeServiceCallback = new RemoveServiceCallback(OnRemoveService); + resolveServiceCallback = new ResolveServiceCallback(OnResolveService); + + this.Load += new System.EventHandler(this.Form1_Load); + } + + private void Form1_Load(object sender, EventArgs e) + { + typeBox.SelectedItem = "_spike._tcp"; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if (components != null) + { + components.Dispose(); + } + + if (browser != null) + { + browser.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.browseList = new System.Windows.Forms.ListBox(); + this.typeBox = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.nameField = new System.Windows.Forms.TextBox(); + this.typeField = new System.Windows.Forms.TextBox(); + this.domainField = new System.Windows.Forms.TextBox(); + this.hostField = new System.Windows.Forms.TextBox(); + this.portField = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.serviceTextField = new System.Windows.Forms.ListBox(); + this.SuspendLayout(); + // + // browseList + // + this.browseList.Location = new System.Drawing.Point(8, 48); + this.browseList.Name = "browseList"; + this.browseList.Size = new System.Drawing.Size(488, 147); + this.browseList.TabIndex = 0; + this.browseList.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged); + // + // typeBox + // + this.typeBox.Items.AddRange(new object[] + { + "_accessone._tcp", + "_accountedge._tcp", + "_actionitems._tcp", + "_addressbook._tcp", + "_aecoretech._tcp", + "_afpovertcp._tcp", + "_airport._tcp", + "_animolmd._tcp", + "_animobserver._tcp", + "_apple-sasl._tcp", + "_aquamon._tcp", + "_async._tcp", + "_auth._tcp", + "_beep._tcp", + "_bfagent._tcp", + "_bootps._udp", + "_bousg._tcp", + "_bsqdea._tcp", + "_cheat._tcp", + "_chess._tcp", + "_clipboard._tcp", + "_collection._tcp", + "_contactserver._tcp", + "_cvspserver._tcp", + "_cytv._tcp", + "_daap._tcp", + "_difi._tcp", + "_distcc._tcp", + "_dossier._tcp", + "_dpap._tcp", + "_earphoria._tcp", + "_ebms._tcp", + "_ebreg._tcp", + "_ecbyesfsgksc._tcp", + "_eheap._tcp", + "_embrace._tcp", + "_eppc._tcp", + "_eventserver._tcp", + "_exec._tcp", + "_facespan._tcp", + "_faxstfx._tcp", + "_fish._tcp", + "_fjork._tcp", + "_fmpro-internal._tcp", + "_ftp._tcp", + "_ftpcroco._tcp", + "_gbs-smp._tcp", + "_gbs-stp._tcp", + "_grillezvous._tcp", + "_h323._tcp", + "_http._tcp", + "_hotwayd._tcp", + "_hydra._tcp", + "_ica-networking._tcp", + "_ichalkboard._tcp", + "_ichat._tcp", + "_iconquer._tcp", + "_ifolder._tcp", + "_ilynx._tcp", + "_imap._tcp", + "_imidi._tcp", + "_ipbroadcaster._tcp", + "_ipp._tcp", + "_isparx._tcp", + "_ispq-vc._tcp", + "_ishare._tcp", + "_isticky._tcp", + "_istorm._tcp", + "_iwork._tcp", + "_lan2p._tcp", + "_ldap._tcp", + "_liaison._tcp", + "_login._tcp", + "_lontalk._tcp", + "_lonworks._tcp", + "_macfoh-remote._tcp", + "_macminder._tcp", + "_moneyworks._tcp", + "_mp3sushi._tcp", + "_mttp._tcp", + "_ncbroadcast._tcp", + "_ncdirect._tcp", + "_ncsyncserver._tcp", + "_net-assistant._tcp", + "_newton-dock._tcp", + "_nfs._udp", + "_nssocketport._tcp", + "_odabsharing._tcp", + "_omni-bookmark._tcp", + "_openbase._tcp", + "_p2pchat._udp", + "_pdl-datastream._tcp", + "_poch._tcp", + "_pop3._tcp", + "_postgresql._tcp", + "_presence._tcp", + "_printer._tcp", + "_ptp._tcp", + "_quinn._tcp", + "_raop._tcp", + "_rce._tcp", + "_realplayfavs._tcp", + "_rendezvouspong._tcp", + "_riousbprint._tcp", + "_rfb._tcp", + "_rtsp._tcp", + "_safarimenu._tcp", + "_sallingclicker._tcp", + "_scone._tcp", + "_sdsharing._tcp", + "_see._tcp", + "_seeCard._tcp", + "_serendipd._tcp", + "_servermgr._tcp", + "_shell._tcp", + "_shout._tcp", + "_shoutcast._tcp", + "_soap._tcp", + "_spike._tcp", + "_spincrisis._tcp", + "_spl-itunes._tcp", + "_spr-itunes._tcp", + "_ssh._tcp", + "_ssscreenshare._tcp", + "_strateges._tcp", + "_sge-exec._tcp", + "_sge-qmaster._tcp", + "_stickynotes._tcp", + "_sxqdea._tcp", + "_sybase-tds._tcp", + "_teamlist._tcp", + "_teleport._udp", + "_telnet._tcp", + "_tftp._udp", + "_ticonnectmgr._tcp", + "_tinavigator._tcp", + "_tryst._tcp", + "_upnp._tcp", + "_utest._tcp", + "_vue4rendercow._tcp", + "_webdav._tcp", + "_whamb._tcp", + "_wired._tcp", + "_workstation._tcp", + "_wormhole._tcp", + "_workgroup._tcp", + "_ws._tcp", + "_xserveraid._tcp", + "_xsync._tcp", + "_xtshapro._tcp" + }); + + this.typeBox.Location = new System.Drawing.Point(8, 16); + this.typeBox.Name = "typeBox"; + this.typeBox.Size = new System.Drawing.Size(192, 21); + this.typeBox.Sorted = true; + this.typeBox.TabIndex = 3; + this.typeBox.SelectedIndexChanged += new System.EventHandler(this.typeBox_SelectedIndexChanged); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 208); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(48, 16); + this.label1.TabIndex = 4; + this.label1.Text = "Name:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(8, 240); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(48, 16); + this.label2.TabIndex = 5; + this.label2.Text = "Type:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label3 + // + this.label3.Location = new System.Drawing.Point(8, 272); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(48, 16); + this.label3.TabIndex = 6; + this.label3.Text = "Domain:"; + this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label4 + // + this.label4.Location = new System.Drawing.Point(8, 304); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(48, 16); + this.label4.TabIndex = 7; + this.label4.Text = "Host:"; + this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // nameField + // + this.nameField.Location = new System.Drawing.Point(56, 208); + this.nameField.Name = "nameField"; + this.nameField.ReadOnly = true; + this.nameField.Size = new System.Drawing.Size(168, 20); + this.nameField.TabIndex = 8; + this.nameField.Text = ""; + // + // typeField + // + this.typeField.Location = new System.Drawing.Point(56, 240); + this.typeField.Name = "typeField"; + this.typeField.ReadOnly = true; + this.typeField.Size = new System.Drawing.Size(168, 20); + this.typeField.TabIndex = 9; + this.typeField.Text = ""; + // + // domainField + // + this.domainField.Location = new System.Drawing.Point(56, 272); + this.domainField.Name = "domainField"; + this.domainField.ReadOnly = true; + this.domainField.Size = new System.Drawing.Size(168, 20); + this.domainField.TabIndex = 10; + this.domainField.Text = ""; + // + // hostField + // + this.hostField.Location = new System.Drawing.Point(56, 304); + this.hostField.Name = "hostField"; + this.hostField.ReadOnly = true; + this.hostField.Size = new System.Drawing.Size(168, 20); + this.hostField.TabIndex = 11; + this.hostField.Text = ""; + + // + // portField + // + this.portField.Location = new System.Drawing.Point(56, 336); + this.portField.Name = "portField"; + this.portField.ReadOnly = true; + this.portField.Size = new System.Drawing.Size(168, 20); + this.portField.TabIndex = 12; + this.portField.Text = ""; + // + // label5 + // + this.label5.Location = new System.Drawing.Point(8, 336); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(48, 16); + this.label5.TabIndex = 14; + this.label5.Text = "Port:"; + this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // serviceTextField + // + this.serviceTextField.HorizontalScrollbar = true; + this.serviceTextField.Location = new System.Drawing.Point(264, 208); + this.serviceTextField.Name = "serviceTextField"; + this.serviceTextField.Size = new System.Drawing.Size(232, 147); + this.serviceTextField.TabIndex = 16; + // + // Form1 + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(512, 365); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.serviceTextField, + this.label5, + this.portField, + this.hostField, + this.domainField, + this.typeField, + this.nameField, + this.label4, + this.label3, + this.label2, + this.label1, + this.typeBox, + this.browseList}); + this.Name = "Form1"; + this.Text = "DNSServices Browser"; + this.ResumeLayout(false); + + } + #endregion + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.Run(new Form1()); + } + // + // BrowseData + // + // This class is used to store data associated + // with a DNSService.Browse() operation + // + public class BrowseData + { + public int InterfaceIndex; + public String Name; + public String Type; + public String Domain; + + public override String + ToString() + { + return Name; + } + + public override bool + Equals(object other) + { + bool result = false; + + if (other != null) + { + if ((object) this == other) + { + result = true; + } + else if (other is BrowseData) + { + BrowseData otherBrowseData = (BrowseData) other; + + result = (this.Name == otherBrowseData.Name); + } + } + + return result; + } + + public override int + GetHashCode() + { + return Name.GetHashCode(); + } + }; + + + // + // ResolveData + // + // This class is used to store data associated + // with a DNSService.Resolve() operation + // + public class ResolveData + { + public int InterfaceIndex; + public String FullName; + public String HostName; + public int Port; + public Byte[] TxtRecord; + + public override String + ToString() + { + return FullName; + } + }; + + // + // Populate() + // + // Populate this form with data associated with a + // DNSService.Resolve() call + // + public void + Populate(BrowseData browseData, ResolveData resolveData) + { + nameField.Text = browseData.Name; + typeField.Text = browseData.Type; + domainField.Text = browseData.Domain; + hostField.Text = resolveData.HostName; + portField.Text = resolveData.Port.ToString(); + + serviceTextField.Items.Clear(); + + if (resolveData.TxtRecord != null) + { + for (int idx = 0; idx < TextRecord.GetCount(resolveData.TxtRecord); idx++) + { + String key; + Byte[] bytes; + + bytes = TextRecord.GetItemAtIndex(resolveData.TxtRecord, idx, out key); + + if (key.Length > 0) + { + String val = ""; + + if (bytes != null) + { + val = Encoding.ASCII.GetString(bytes, 0, bytes.Length); + } + + serviceTextField.Items.Add(key + "=" + val); + } + } + } + } + + // + // called when the type field changes + // + private void typeBox_SelectedIndexChanged(object sender, System.EventArgs e) + { + browseList.Items.Clear(); + + if (browser != null) + { + browser.Dispose(); + } + + nameField.Text = ""; + typeField.Text = ""; + domainField.Text = ""; + hostField.Text = ""; + portField.Text = ""; + serviceTextField.Items.Clear(); + + try + { + browser = DNSService.Browse(0, 0, typeBox.SelectedItem.ToString(), null, new DNSService.BrowseReply(OnBrowseReply)); + } + catch + { + MessageBox.Show("Browse Failed", "Error"); + Application.Exit(); + } + } + + private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e) + { + if (resolver != null) + { + resolver.Dispose(); + } + + if (browseList.SelectedItem != null) + { + try + { + BrowseData data = (BrowseData) browseList.SelectedItem; + + resolver = DNSService.Resolve(0, 0, data.Name, data.Type, data.Domain, new DNSService.ResolveReply(OnResolveReply)); + } + catch + { + MessageBox.Show("Resolve Failed", "Error"); + Application.Exit(); + } + } + } + + // + // OnAddService + // + // This method is "Invoked" by OnBrowseReply. This call + // executes in the context of the main thread + // + private void OnAddService + ( + BrowseData data + ) + { + browseList.Items.Add(data); + browseList.Invalidate(); + } + + // + // OnRemoveService + // + // This method is "Invoked" by OnBrowseReply. This call + // executes in the context of the main thread + // + private void OnRemoveService + ( + BrowseData data + ) + { + browseList.Items.Remove(data); + browseList.Invalidate(); + } + + // + // OnResolveService + // + // This method is "Invoked" by OnResolveReply. This call + // executes in the context of the main thread + // + private void OnResolveService + ( + ResolveData data + ) + { + resolver.Dispose(); + resolver = null; + + Populate((BrowseData) browseList.SelectedItem, data); + } + + // + // OnBrowseReply + // + // This call is invoked by the DNSService core. It is + // executed in the context of a worker thread, not the + // main (GUI) thread. We create a BrowseData object + // and invoked the appropriate method in the GUI thread + // so we can update the UI + // + private void OnBrowseReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String name, + String type, + String domain + ) + { + if (errorCode == ErrorCode.NoError) + { + BrowseData data = new BrowseData(); + + data.InterfaceIndex = interfaceIndex; + data.Name = name; + data.Type = type; + data.Domain = domain; + + if ((flags & ServiceFlags.Add) != 0) + { + Invoke(addServiceCallback, new Object[]{data}); + + } + else if ((flags == 0) || ((flags & ServiceFlags.MoreComing) != 0)) + { + Invoke(removeServiceCallback, new Object[]{data}); + } + } + else + { + MessageBox.Show("OnBrowseReply returned an error code: " + errorCode, "Error"); + } + } + + private void OnResolveReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String fullName, + String hostName, + int port, + Byte[] txtRecord + ) + { + if (errorCode == ErrorCode.NoError) + { + ResolveData data = new ResolveData(); + + data.InterfaceIndex = interfaceIndex; + data.FullName = fullName; + data.HostName = hostName; + data.Port = port; + data.TxtRecord = txtRecord; + + Invoke(resolveServiceCallback, new Object[]{data}); + } + else + { + MessageBox.Show("OnResolveReply returned an error code: " + errorCode, "Error"); + } + } + } +} diff --git a/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.resx b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.resx new file mode 100755 index 0000000..e5b5a11 --- /dev/null +++ b/Clients/DNSServiceBrowser.NET/DNSServiceBrowser.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Form1 + + \ No newline at end of file diff --git a/Clients/DNSServiceBrowser.m b/Clients/DNSServiceBrowser.m index f2a38c8..30db5a8 100755 --- a/Clients/DNSServiceBrowser.m +++ b/Clients/DNSServiceBrowser.m @@ -3,8 +3,6 @@ * * @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/Clients/DNSServiceRegistration-Info.plist b/Clients/DNSServiceReg-Info.plist similarity index 95% rename from Clients/DNSServiceRegistration-Info.plist rename to Clients/DNSServiceReg-Info.plist index fafaa99..2569968 100644 --- a/Clients/DNSServiceRegistration-Info.plist +++ b/Clients/DNSServiceReg-Info.plist @@ -23,7 +23,7 @@ CFBundleVersion 1.0.0d1 NSMainNibFile - DNSServiceRegistration + DNSServiceReg NSPrincipalClass NSApplication diff --git a/Clients/DNSServiceRegistration.m b/Clients/DNSServiceReg.m similarity index 99% rename from Clients/DNSServiceRegistration.m rename to Clients/DNSServiceReg.m index 3b50a07..c4a3ee7 100644 --- a/Clients/DNSServiceRegistration.m +++ b/Clients/DNSServiceReg.m @@ -3,8 +3,6 @@ * * @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 @@ -24,7 +22,7 @@ Change History (most recent first): -$Log: DNSServiceRegistration.m,v $ +$Log: DNSServiceReg.m,v $ Revision 1.15 2004/06/05 02:01:08 cheshire Move DNSServiceRegistration from mDNSMacOSX directory to Clients directory diff --git a/Clients/DNSServiceRegistration.nib/classes.nib b/Clients/DNSServiceReg.nib/classes.nib similarity index 100% rename from Clients/DNSServiceRegistration.nib/classes.nib rename to Clients/DNSServiceReg.nib/classes.nib diff --git a/Clients/DNSServiceRegistration.nib/info.nib b/Clients/DNSServiceReg.nib/info.nib similarity index 100% rename from Clients/DNSServiceRegistration.nib/info.nib rename to Clients/DNSServiceReg.nib/info.nib diff --git a/Clients/DNSServiceRegistration.nib/objects.nib b/Clients/DNSServiceReg.nib/objects.nib similarity index 100% rename from Clients/DNSServiceRegistration.nib/objects.nib rename to Clients/DNSServiceReg.nib/objects.nib diff --git a/Clients/ExplorerPlugin/About.cpp b/Clients/ExplorerPlugin/About.cpp new file mode 100644 index 0000000..37d3281 --- /dev/null +++ b/Clients/ExplorerPlugin/About.cpp @@ -0,0 +1,63 @@ +// About.cpp : implementation file +// + +#include "stdafx.h" +#include "ExplorerPlugin.h" +#include "About.h" + + +// CAbout dialog + +IMPLEMENT_DYNAMIC(CAbout, CDialog) +CAbout::CAbout(CWnd* pParent /*=NULL*/) + : CDialog(CAbout::IDD, pParent) +{ + // Initialize brush with the desired background color + m_bkBrush.CreateSolidBrush(RGB(255, 255, 255)); +} + +CAbout::~CAbout() +{ +} + +void CAbout::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_COMPONENT, m_componentCtrl); + DDX_Control(pDX, IDC_LEGAL, m_legalCtrl); +} + + +BEGIN_MESSAGE_MAP(CAbout, CDialog) +ON_WM_CTLCOLOR() +END_MESSAGE_MAP() + + +// CAbout message handlers +HBRUSH CAbout::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) +{ + switch (nCtlColor) + { + case CTLCOLOR_STATIC: + + if ( pWnd->GetDlgCtrlID() == IDC_COMPONENT ) + { + pDC->SetTextColor(RGB(64, 64, 64)); + } + else + { + pDC->SetTextColor(RGB(0, 0, 0)); + } + + pDC->SetBkColor(RGB(255, 255, 255)); + return (HBRUSH)(m_bkBrush.GetSafeHandle()); + + case CTLCOLOR_DLG: + + return (HBRUSH)(m_bkBrush.GetSafeHandle()); + + default: + + return CDialog::OnCtlColor(pDC, pWnd, nCtlColor); + } +} diff --git a/Clients/ExplorerPlugin/About.h b/Clients/ExplorerPlugin/About.h new file mode 100644 index 0000000..f4d0d7e --- /dev/null +++ b/Clients/ExplorerPlugin/About.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Resource.h" +#include "afxwin.h" + +// CAbout dialog + +class CAbout : public CDialog +{ + DECLARE_DYNAMIC(CAbout) + +public: + CAbout(CWnd* pParent = NULL); // standard constructor + virtual ~CAbout(); + +// Dialog Data + enum { IDD = IDD_ABOUT }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + DECLARE_MESSAGE_MAP() +public: + CStatic m_componentCtrl; + CStatic m_legalCtrl; + CBrush m_bkBrush; +}; diff --git a/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.cpp b/Clients/ExplorerPlugin/ClassFactory.cpp similarity index 95% rename from mDNSWindows/Applications/ExplorerPlugin/ClassFactory.cpp rename to Clients/ExplorerPlugin/ClassFactory.cpp index 44c9fb2..b4e06c6 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.cpp +++ b/Clients/ExplorerPlugin/ClassFactory.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,14 @@ Change History (most recent first): $Log: ClassFactory.cpp,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ diff --git a/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.h b/Clients/ExplorerPlugin/ClassFactory.h similarity index 88% rename from mDNSWindows/Applications/ExplorerPlugin/ClassFactory.h rename to Clients/ExplorerPlugin/ClassFactory.h index 2361129..76e4b5e 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ClassFactory.h +++ b/Clients/ExplorerPlugin/ClassFactory.h @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,14 @@ Change History (most recent first): $Log: ClassFactory.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.cpp b/Clients/ExplorerPlugin/ExplorerBar.cpp similarity index 84% rename from mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.cpp rename to Clients/ExplorerPlugin/ExplorerBar.cpp index 4052d77..1c71c9b 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.cpp +++ b/Clients/ExplorerPlugin/ExplorerBar.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,17 @@ Change History (most recent first): $Log: ExplorerBar.cpp,v $ +Revision 1.3 2004/07/26 05:44:08 shersche +remove extraneous debug statement + +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ @@ -41,6 +48,7 @@ Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within #include "ExplorerBar.h" +#include "About.h" // MFC Debugging #ifdef _DEBUG @@ -52,7 +60,7 @@ static char THIS_FILE[] = __FILE__; //=========================================================================================================================== // Constants //=========================================================================================================================== - +#define LENGTHOF(a) (sizeof(a) == sizeof(&*a)) ? 0 : (sizeof(a) / sizeof(*a)) #define MIN_SIZE_X 10 #define MIN_SIZE_Y 10 @@ -134,6 +142,10 @@ STDMETHODIMP ExplorerBar::QueryInterface( REFIID inID, LPVOID *outResult ) { *outResult = (IPersistStream *) this; } + else if( IsEqualIID( inID, IID_IContextMenu ) ) // IContextMenu + { + *outResult = (IContextMenu *) this; + } else { *outResult = NULL; @@ -525,6 +537,93 @@ STDMETHODIMP ExplorerBar::GetSizeMax( ULARGE_INTEGER *outSizeMax ) return( E_NOTIMPL ); } + +//=========================================================================================================================== +// QueryContextMenu +//=========================================================================================================================== + +STDMETHODIMP ExplorerBar::QueryContextMenu(HMENU hShellContextMenu, UINT iContextMenuFirstItem, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) +{ + DEBUG_UNUSED( idCmdLast ); + DEBUG_UNUSED( uFlags ); + + CMenu menubar; + + menubar.LoadMenu(IDR_CONTEXT_MENU); + CMenu * menu = menubar.GetSubMenu(0); + + CMenu shellmenu; + + shellmenu.Attach(hShellContextMenu); + + UINT iShellItem = iContextMenuFirstItem; //! remove plus one + UINT idShellCmd = idCmdFirst; + + int n = menu->GetMenuItemCount(); + + for (int i=0; iGetMenuItemInfo(i, &mii, TRUE); + + mii.wID = idShellCmd++; + + shellmenu.InsertMenuItem(iShellItem++, &mii, TRUE); + } + + shellmenu.Detach(); + + return n; +} + + +//=========================================================================================================================== +// GetCommandString +//=========================================================================================================================== + +// Not called for explorer bars +STDMETHODIMP ExplorerBar::GetCommandString(UINT idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax) +{ + DEBUG_UNUSED( idCmd ); + DEBUG_UNUSED( uType ); + DEBUG_UNUSED( pwReserved ); + DEBUG_UNUSED( pszName ); + DEBUG_UNUSED( cchMax ); + + return E_NOTIMPL; +} + +//=========================================================================================================================== +// InvokeCommand +//=========================================================================================================================== + +// The shell sends either strings or indexes +// IE never sends strings +// The indexes are into an array of my commands +// So the verb for the first command I added to the menu is always 0x0000 +// Here - because I don't have any submenus - +// I can treat the 'verb' as an menu item position +STDMETHODIMP ExplorerBar::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) +{ + // IE doesn't send string commands + if (HIWORD(lpici->lpVerb) != 0) return 0; + + CAbout dlg; + + dlg.DoModal(); + + return S_OK; +} + #if 0 #pragma mark - #pragma mark == Other == diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.h b/Clients/ExplorerPlugin/ExplorerBar.h similarity index 83% rename from mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.h rename to Clients/ExplorerPlugin/ExplorerBar.h index 1ab9df9..fe58176 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBar.h +++ b/Clients/ExplorerPlugin/ExplorerBar.h @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,14 @@ Change History (most recent first): $Log: ExplorerBar.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ @@ -45,7 +49,8 @@ Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within class ExplorerBar : public IDeskBand, public IInputObject, public IObjectWithSite, - public IPersistStream + public IPersistStream, + public IContextMenu { protected: @@ -103,6 +108,12 @@ class ExplorerBar : public IDeskBand, STDMETHOD( Save )( LPSTREAM inStream, BOOL inClearDirty ); STDMETHOD( GetSizeMax )( ULARGE_INTEGER *outSizeMax ); + // IContextMenu methods + + STDMETHOD( QueryContextMenu )( HMENU hContextMenu, UINT iContextMenuFirstItem, UINT idCmdFirst, UINT idCmdLast, UINT uFlags ); + STDMETHOD( GetCommandString )( UINT idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax ); + STDMETHOD( InvokeCommand )( LPCMINVOKECOMMANDINFO lpici ); + // Other OSStatus SetupWindow( void ); diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.cpp b/Clients/ExplorerPlugin/ExplorerBarWindow.cpp similarity index 73% rename from mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.cpp rename to Clients/ExplorerPlugin/ExplorerBarWindow.cpp index 7ca9f2a..beb0b35 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.cpp +++ b/Clients/ExplorerPlugin/ExplorerBarWindow.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,46 @@ Change History (most recent first): $Log: ExplorerBarWindow.cpp,v $ +Revision 1.12 2004/10/26 00:56:03 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.11 2004/10/18 23:49:17 shersche + Remove trailing dot from hostname, because some flavors of Windows have difficulty parsing hostnames with a trailing dot. +Bug #: 3841564 + +Revision 1.10 2004/09/02 02:18:58 cheshire +Minor textual cleanup to improve readability + +Revision 1.9 2004/09/02 02:11:56 cheshire + Fix incorrect testing of MoreComing flag + +Revision 1.8 2004/07/26 05:47:31 shersche +use TXTRecord APIs, fix bug in locating service to be removed + +Revision 1.7 2004/07/22 16:08:20 shersche +clean up debug print statements, re-enable code inadvertently commented out + +Revision 1.6 2004/07/22 05:27:20 shersche + Check to make sure error isn't WSAEWOULDBLOCK when canceling browse +Bug #: 3735827 + +Revision 1.5 2004/07/20 06:49:18 shersche +clean up socket handling code + +Revision 1.4 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.3 2004/06/27 14:59:59 shersche +reference count service info to handle multi-homed hosts + +Revision 1.2 2004/06/23 16:09:34 shersche +Add the resolve DNSServiceRef to list of extant refs. This fixes the "doesn't resolve when double clicking" problem + +Submitted by: Scott Herscher + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. @@ -39,7 +77,7 @@ 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ @@ -47,7 +85,8 @@ Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within #include "CommonServices.h" #include "DebugServices.h" -#include "DNSSD.h" +#include "WinServices.h" +#include "dns_sd.h" #include "ExplorerBar.h" #include "LoginDialog.h" @@ -77,14 +116,11 @@ static char THIS_FILE[] = __FILE__; // 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 ) +#define WM_PRIVATE_SERVICE_EVENT ( WM_USER + 0x100 ) // TXT records -#define kTXTRecordKeyPath "path=" -#define kTXTRecordKeyPathSize sizeof_string( kTXTRecordKeyPath ) +#define kTXTRecordKeyPath "path" #if 0 #pragma mark == Prototypes == @@ -95,7 +131,6 @@ static char THIS_FILE[] = __FILE__; //=========================================================================================================================== 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 == @@ -110,9 +145,7 @@ BEGIN_MESSAGE_MAP( ExplorerBarWindow, CWnd ) 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 ) + ON_MESSAGE( WM_PRIVATE_SERVICE_EVENT, OnServiceEvent ) END_MESSAGE_MAP() #if 0 @@ -152,25 +185,16 @@ int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) OSStatus err; CRect rect; + CBitmap bitmap; 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, + mTree.Create( WS_TABSTOP | WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_NOHSCROLL , 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; @@ -194,6 +218,11 @@ int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); require_noerr( err, exit ); + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + m_serviceRefs.push_back(e->ref); + // FTP Site Handler e = new ServiceHandlerEntry; @@ -213,8 +242,29 @@ int ExplorerBarWindow::OnCreate( LPCREATESTRUCT inCreateStruct ) err = DNSServiceBrowse( &e->ref, 0, 0, e->type, NULL, BrowseCallBack, e ); require_noerr( err, exit ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(e->ref), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + m_serviceRefs.push_back(e->ref); + + m_imageList.Create(16, 16, ILC_COLORDDB, 1, 0); + bitmap.LoadBitmap(IDB_LOGO); + m_imageList.Add(&bitmap, (CBitmap*) NULL); + + mTree.SetImageList(&m_imageList, TVSIL_NORMAL); exit: + + // Cannot talk to the mDNSResponder service. Show the error message and exit (with kNoErr so they can see it). + if ( err != kNoErr ) + { + s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE ); + mTree.DeleteAllItems(); + mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST ); + err = kNoErr; + } + return( err ); } @@ -228,6 +278,20 @@ void ExplorerBarWindow::OnDestroy( void ) StopResolve(); + // Clean up the extant browses + while (m_serviceRefs.size() > 0) + { + // + // take the head of the list + // + DNSServiceRef ref = m_serviceRefs.front(); + + // + // Stop will remove it from the list + // + Stop( ref ); + } + // Clean up the service handlers. int i; @@ -239,7 +303,6 @@ void ExplorerBarWindow::OnDestroy( void ) delete mServiceHandlers[ i ]; } - DNSServiceFinalize(); CWnd::OnDestroy(); } @@ -278,6 +341,56 @@ exit: *outResult = 0; } + +//=========================================================================================================================== +// OnServiceEvent +//=========================================================================================================================== + +LONG +ExplorerBarWindow::OnServiceEvent(WPARAM inWParam, LPARAM inLParam) +{ + if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam))) + { + dlog( kDebugLevelError, "OnServiceEvent: window error\n" ); + } + else + { + SOCKET sock = (SOCKET) inWParam; + + // iterate thru list + ServiceRefList::iterator it; + + for (it = m_serviceRefs.begin(); it != m_serviceRefs.end(); it++) + { + DNSServiceRef ref = *it; + + check(ref != NULL); + + if ((SOCKET) DNSServiceRefSockFD(ref) == sock) + { + DNSServiceErrorType err; + + err = DNSServiceProcessResult(ref); + + if (err != 0) + { + CString s; + + s.LoadString( IDS_MDNSRESPONDER_NOT_AVAILABLE ); + mTree.DeleteAllItems(); + mTree.InsertItem( s, 0, 0, TVI_ROOT, TVI_LAST ); + + Stop(ref); + } + + break; + } + } + } + + return ( 0 ); +} + #if 0 #pragma mark - #endif @@ -286,7 +399,7 @@ exit: // BrowseCallBack //=========================================================================================================================== -void CALLBACK_COMPAT +void DNSSD_API ExplorerBarWindow::BrowseCallBack( DNSServiceRef inRef, DNSServiceFlags inFlags, @@ -303,6 +416,7 @@ void CALLBACK_COMPAT DEBUG_UNUSED( inRef ); + obj = NULL; service = NULL; require_noerr( inErrorCode, exit ); @@ -310,16 +424,19 @@ void CALLBACK_COMPAT check( obj ); check( obj->obj ); + // + // set the UI to hold off on updates + // + obj->obj->mTree.SetRedraw(FALSE); + 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 ); @@ -331,17 +448,13 @@ void CALLBACK_COMPAT service->ifi = inInterfaceIndex; service->handler = obj; + + service->refs = 1; - 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; - } + if (inFlags & kDNSServiceFlagsAdd) obj->obj->OnServiceAdd (service); + else obj->obj->OnServiceRemove(service); + + service = NULL; } catch( ... ) { @@ -349,6 +462,15 @@ void CALLBACK_COMPAT } exit: + // + // If no more coming, then update UI + // + if (obj && obj->obj && ((inFlags & kDNSServiceFlagsMoreComing) == 0)) + { + obj->obj->mTree.SetRedraw(TRUE); + obj->obj->mTree.Invalidate(); + } + if( service ) { delete service; @@ -359,16 +481,13 @@ exit: // OnServiceAdd //=========================================================================================================================== -LONG ExplorerBarWindow::OnServiceAdd( WPARAM inWParam, LPARAM inLParam ) +LONG ExplorerBarWindow::OnServiceAdd( ServiceInfo * service ) { - ServiceInfo * service; ServiceHandlerEntry * handler; int cmp; int index; - DEBUG_UNUSED( inWParam ); - service = reinterpret_cast < ServiceInfo * > ( inLParam ); check( service ); handler = service->handler; check( handler ); @@ -380,12 +499,10 @@ LONG ExplorerBarWindow::OnServiceAdd( WPARAM inWParam, LPARAM inLParam ) 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 ); + + handler->array[ index ]->refs++; + + delete service; } else { @@ -413,16 +530,13 @@ LONG ExplorerBarWindow::OnServiceAdd( WPARAM inWParam, LPARAM inLParam ) // OnServiceRemove //=========================================================================================================================== -LONG ExplorerBarWindow::OnServiceRemove( WPARAM inWParam, LPARAM inLParam ) +LONG ExplorerBarWindow::OnServiceRemove( ServiceInfo * service ) { - ServiceInfo * service; ServiceHandlerEntry * handler; int cmp; int index; - DEBUG_UNUSED( inWParam ); - service = reinterpret_cast < ServiceInfo * > ( inLParam ); check( service ); handler = service->handler; check( handler ); @@ -431,17 +545,22 @@ LONG ExplorerBarWindow::OnServiceRemove( WPARAM inWParam, LPARAM inLParam ) 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. - + // Possibly 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 ); + + if ( --handler->array[ index ]->refs == 0 ) + { + mTree.DeleteItem( handler->array[ index ]->item ); + delete handler->array[ index ]; + handler->array.RemoveAt( index ); + } } + delete service; return( 0 ); } @@ -465,11 +584,15 @@ OSStatus ExplorerBarWindow::StartResolve( ServiceInfo *inService ) StopResolve(); // Resolve the service. - - err = DNSServiceResolve( &mResolveServiceRef, kDNSServiceFlagsNone, inService->ifi, - inService->name, inService->type, inService->domain, ResolveCallBack, inService->handler ); + err = DNSServiceResolve( &mResolveServiceRef, 0, 0, + inService->name, inService->type, inService->domain, (DNSServiceResolveReply) ResolveCallBack, inService->handler ); + require_noerr( err, exit ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(mResolveServiceRef), m_hWnd, WM_PRIVATE_SERVICE_EVENT, FD_READ|FD_CLOSE); require_noerr( err, exit ); + m_serviceRefs.push_back(mResolveServiceRef); + exit: return( err ); } @@ -482,7 +605,7 @@ void ExplorerBarWindow::StopResolve( void ) { if( mResolveServiceRef ) { - DNSServiceRefDeallocate( mResolveServiceRef ); + Stop( mResolveServiceRef ); mResolveServiceRef = NULL; } } @@ -491,7 +614,7 @@ void ExplorerBarWindow::StopResolve( void ) // ResolveCallBack //=========================================================================================================================== -void CALLBACK_COMPAT +void DNSSD_API ExplorerBarWindow::ResolveCallBack( DNSServiceRef inRef, DNSServiceFlags inFlags, @@ -522,7 +645,7 @@ void CALLBACK_COMPAT try { ResolveInfo * resolve; - BOOL ok; + int idx; dlog( kDebugLevelNotice, "resolved %s on ifi %d to %s\n", inFullName, inInterfaceIndex, inHostName ); @@ -536,6 +659,19 @@ void CALLBACK_COMPAT require_action( resolve, exit, err = kNoMemoryErr ); UTF8StringToStringObject( inHostName, resolve->host ); + + // rdar://problem/3841564 + // + // strip trailing dot from hostname because some flavors of Windows + // have trouble parsing it. + + idx = resolve->host.ReverseFind('.'); + + if ((idx > 1) && ((resolve->host.GetLength() - 1) == idx)) + { + resolve->host.Delete(idx, 1); + } + resolve->port = ntohs( inPort ); resolve->ifi = inInterfaceIndex; resolve->handler = handler; @@ -543,12 +679,7 @@ void CALLBACK_COMPAT 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; - } + obj->OnResolve(resolve); } catch( ... ) { @@ -563,19 +694,16 @@ exit: // OnResolve //=========================================================================================================================== -LONG ExplorerBarWindow::OnResolve( WPARAM inWParam, LPARAM inLParam ) +LONG ExplorerBarWindow::OnResolve( ResolveInfo * resolve ) { - ResolveInfo * resolve; CString url; uint8_t * path; - size_t pathSize; + uint8_t pathSize; char * pathPrefix; CString username; CString password; - DEBUG_UNUSED( inWParam ); - resolve = reinterpret_cast < ResolveInfo * > ( inLParam ); check( resolve ); // Get login info if needed. @@ -595,30 +723,23 @@ LONG ExplorerBarWindow::OnResolve( WPARAM inWParam, LPARAM inLParam ) 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 != '/' ) + uint8_t * txtData; + uint16_t txtLen; + + resolve->txt.GetData( &txtData, &txtLen ); + + path = (uint8_t*) TXTRecordGetValuePtr(txtLen, txtData, kTXTRecordKeyPath, &pathSize); + + if (path == NULL) { - pathPrefix = "/"; + path = (uint8_t*) ""; + pathSize = 1; } } else { - path = (uint8_t *) ""; - pathSize = 1; + path = (uint8_t *) ""; + pathSize = 1; } // Build the URL in the following format: @@ -651,6 +772,19 @@ exit: return( 0 ); } +//=========================================================================================================================== +// Stop +//=========================================================================================================================== +void ExplorerBarWindow::Stop( DNSServiceRef ref ) +{ + m_serviceRefs.remove( ref ); + + WSAAsyncSelect(DNSServiceRefSockFD( ref ), m_hWnd, WM_PRIVATE_SERVICE_EVENT, 0); + + DNSServiceRefDeallocate( ref ); +} + + #if 0 #pragma mark - #endif @@ -674,10 +808,12 @@ DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const Se { mid = ( lo + hi ) / 2; result = inService.displayName.CompareNoCase( inArray[ mid ]->displayName ); +#if 0 if( result == 0 ) { result = ( (int) inService.ifi ) - ( (int) inArray[ mid ]->ifi ); } +#endif if( result == 0 ) { break; @@ -702,50 +838,3 @@ DEBUG_LOCAL int FindServiceArrayIndex( const ServiceInfoArray &inArray, const Se 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/Clients/ExplorerPlugin/ExplorerBarWindow.h similarity index 83% rename from mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.h rename to Clients/ExplorerPlugin/ExplorerBarWindow.h index ac234f5..afa4e99 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerBarWindow.h +++ b/Clients/ExplorerPlugin/ExplorerBarWindow.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,21 @@ Change History (most recent first): $Log: ExplorerBarWindow.h,v $ +Revision 1.5 2004/07/26 05:47:31 shersche +use TXTRecord APIs, fix bug in locating service to be removed + +Revision 1.4 2004/07/20 06:49:18 shersche +clean up socket handling code + +Revision 1.3 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.2 2004/06/27 14:59:59 shersche +reference count service info to handle multi-homed hosts + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. @@ -33,7 +46,7 @@ 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ @@ -44,7 +57,8 @@ Explorer Plugin to browse for Rendezvous-enabled Web and FTP servers from within #include "afxtempl.h" -#include "DNSSD.h" +#include "dns_sd.h" +#include //=========================================================================================================================== // Structures @@ -66,6 +80,7 @@ struct ServiceInfo uint32_t ifi; HTREEITEM item; ServiceHandlerEntry * handler; + DWORD refs; ServiceInfo( void ) { @@ -99,7 +114,7 @@ typedef CArray < ServiceInfo *, ServiceInfo * > ServiceInfoArray; struct TextRecord { uint8_t * mData; - size_t mSize; + uint16_t mSize; TextRecord( void ) { @@ -115,7 +130,7 @@ struct TextRecord } } - void GetData( void *outData, size_t *outSize ) + void GetData( void *outData, uint16_t *outSize ) { if( outData ) { @@ -127,7 +142,7 @@ struct TextRecord } } - OSStatus SetData( const void *inData, size_t inSize ) + OSStatus SetData( const void *inData, uint16_t inSize ) { OSStatus err; uint8_t * newData; @@ -186,11 +201,6 @@ struct ServiceHandlerEntry ~ServiceHandlerEntry( void ) { - if( ref ) - { - DNSServiceRefDeallocate( ref ); - } - int i; int n; @@ -233,10 +243,11 @@ class ExplorerBarWindow : public CWnd afx_msg void OnDestroy( void ); afx_msg void OnSize( UINT inType, int inX, int inY ); afx_msg void OnDoubleClick( NMHDR *inNMHDR, LRESULT *outResult ); + afx_msg LONG OnServiceEvent( WPARAM inWParam, LPARAM inLParam ); // Browsing - static void CALLBACK_COMPAT + static void DNSSD_API BrowseCallBack( DNSServiceRef inRef, DNSServiceFlags inFlags, @@ -246,15 +257,19 @@ class ExplorerBarWindow : public CWnd const char * inType, const char * inDomain, void * inContext ); - afx_msg LONG OnServiceAdd( WPARAM inWParam, LPARAM inLParam ); - afx_msg LONG OnServiceRemove( WPARAM inWParam, LPARAM inLParam ); + LONG OnServiceAdd( ServiceInfo * service ); + LONG OnServiceRemove( ServiceInfo * service ); // Resolving OSStatus StartResolve( ServiceInfo *inService ); void StopResolve( void ); - static void CALLBACK_COMPAT + + void Stop( DNSServiceRef ref ); + + + static void DNSSD_API ResolveCallBack( DNSServiceRef inRef, DNSServiceFlags inFlags, @@ -266,7 +281,7 @@ class ExplorerBarWindow : public CWnd uint16_t inTXTSize, const char * inTXT, void * inContext ); - afx_msg LONG OnResolve( WPARAM inWParam, LPARAM inLParam ); + LONG OnResolve( ResolveInfo * resolve ); // Accessors @@ -276,6 +291,12 @@ class ExplorerBarWindow : public CWnd void SetOwner( ExplorerBar *inOwner ) { mOwner = inOwner; } DECLARE_MESSAGE_MAP() + private: + + typedef std::list< DNSServiceRef > ServiceRefList; + + ServiceRefList m_serviceRefs; + CImageList m_imageList; }; #endif // __EXPLORER_BAR_WINDOW__ diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.cpp b/Clients/ExplorerPlugin/ExplorerPlugin.cpp similarity index 83% rename from mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.cpp rename to Clients/ExplorerPlugin/ExplorerPlugin.cpp index 9528577..5de124d 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.cpp +++ b/Clients/ExplorerPlugin/ExplorerPlugin.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,25 @@ Change History (most recent first): $Log: ExplorerPlugin.cpp,v $ +Revision 1.5 2004/09/15 10:33:54 shersche + Install XP toolbar button (8 bit mask) if running on XP platform, otherwise install 1 bit mask toolbar button +Bug #: 3721611 + +Revision 1.4 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.3 2004/06/26 14:12:07 shersche +Register the toolbar button + +Revision 1.2 2004/06/24 20:09:39 shersche +Change text +Submitted by: herscher + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ @@ -114,7 +129,7 @@ BOOL WINAPI DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) { case DLL_PROCESS_ATTACH: gInstance = inInstance; - debug_initialize( kDebugOutputTypeWindowsEventLog, "RendezvousBar", inInstance ); + debug_initialize( kDebugOutputTypeWindowsEventLog, "DNSServices Bar", inInstance ); debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelTrace ); dlog( kDebugLevelTrace, "\nDllMain: process attach\n" ); @@ -436,7 +451,53 @@ DEBUG_LOCAL OSStatus RegisterServer( HINSTANCE inInstance, CLSID inCLSID, LPCTST err = RegSetValueEx( key, clsidString, 0, REG_SZ, (LPBYTE) data, size ); RegCloseKey( key ); } - err = kNoErr; + + // register toolbar button + lstrcpyn( keyName, TEXT( "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\{7F9DB11C-E358-4ca6-A83D-ACC663939424}"), 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, L"Yes", sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"Default Visible", 0, REG_SZ, (LPBYTE) data, size ); + + lstrcpyn( data, inName, sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"ButtonText", 0, REG_SZ, (LPBYTE) data, size ); + + lstrcpyn( data, L"{E0DD6CAB-2D10-11D2-8F1A-0000F87ABD16}", sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"CLSID", 0, REG_SZ, (LPBYTE) data, size ); + + lstrcpyn( data, clsidString, sizeof_array( data ) ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"BandCLSID", 0, REG_SZ, (LPBYTE) data, size ); + + // check if we're running XP or later + if ( ( versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) && + ( versionInfo.dwMajorVersion == 5 ) && + ( versionInfo.dwMinorVersion >= 1 ) ) + { + wsprintf( data, L"%s,%d", moduleName, IDI_BUTTON_XP ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"Icon", 0, REG_SZ, (LPBYTE) data, size); + + wsprintf( data, L"%s,%d", moduleName, IDI_BUTTON_XP ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"HotIcon", 0, REG_SZ, (LPBYTE) data, size); + } + else + { + wsprintf( data, L"%s,%d", moduleName, IDI_BUTTON_2K ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"Icon", 0, REG_SZ, (LPBYTE) data, size); + + wsprintf( data, L"%s,%d", moduleName, IDI_BUTTON_2K ); + size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) ); + RegSetValueEx( key, L"HotIcon", 0, REG_SZ, (LPBYTE) data, size); + } + + RegCloseKey( key ); exit: return( err ); diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.def b/Clients/ExplorerPlugin/ExplorerPlugin.def similarity index 82% rename from mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.def rename to Clients/ExplorerPlugin/ExplorerPlugin.def index 028856c..e825180 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.def +++ b/Clients/ExplorerPlugin/ExplorerPlugin.def @@ -23,8 +23,14 @@ ; Change History (most recent first): ; ; $Log: ExplorerPlugin.def,v $ +; Revision 1.2 2004/07/13 21:24:21 rpantos +; Fix for . +; +; Revision 1.1 2004/06/18 04:34:59 rpantos +; Move to Clients from mDNSWindows +; ; 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. +; Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. ; ; diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.h b/Clients/ExplorerPlugin/ExplorerPlugin.h similarity index 86% rename from mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.h rename to Clients/ExplorerPlugin/ExplorerPlugin.h index e072af0..0acca3f 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.h +++ b/Clients/ExplorerPlugin/ExplorerPlugin.h @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,14 @@ Change History (most recent first): $Log: ExplorerPlugin.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.rc b/Clients/ExplorerPlugin/ExplorerPlugin.rc similarity index 66% rename from mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.rc rename to Clients/ExplorerPlugin/ExplorerPlugin.rc index f9e79ba..7879e58 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.rc +++ b/Clients/ExplorerPlugin/ExplorerPlugin.rc @@ -8,6 +8,7 @@ // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" +#include "WinVersRes.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS @@ -35,6 +36,7 @@ END 2 TEXTINCLUDE BEGIN "#include ""afxres.h""\r\n" + "#include ""WinVersRes.h""\r\n" "\0" END @@ -62,8 +64,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,1 - PRODUCTVERSION 1,0,0,1 + FILEVERSION MASTER_PROD_VERS + PRODUCTVERSION MASTER_PROD_VERS FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -76,16 +78,16 @@ VS_VERSION_INFO VERSIONINFO BEGIN BLOCK "StringFileInfo" BEGIN - BLOCK "040904B0" + BLOCK "040904b0" BEGIN VALUE "CompanyName", "Apple Computer, Inc." - VALUE "FileDescription", "Rendezvous Bar" - VALUE "FileVersion", "1.0.0.1" + VALUE "FileDescription", "DNSServices Explorer Bar" + VALUE "FileVersion", MASTER_PROD_VERS_STR 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" + VALUE "ProductName", MASTER_PROD_NAME + VALUE "ProductVersion", MASTER_PROD_VERS_STR END END BLOCK "VarFileInfo" @@ -116,6 +118,20 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,77,70,44,14 END +IDD_ABOUT DIALOGEX 0, 0, 219, 87 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR +CAPTION "About Rendezvous" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL 119,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE,0,0, + 220,86 + CONTROL MASTER_PROD_VERS_STR3,IDC_COMPONENT,"Static", + SS_SIMPLE | WS_GROUP,92,10,122,8 + LTEXT "Copyright (c) 2004 Apple Computer, Inc. All rights reserved. Apple and the Apple logo are trademarks of Apple Computer, Inc., registered in the U.S. and other countries.", + IDC_LEGAL,92,31,123,50,0,WS_EX_RTLREADING +END + ///////////////////////////////////////////////////////////////////////////// // @@ -129,10 +145,48 @@ BEGIN BEGIN BOTTOMMARGIN, 62 END + + IDD_ABOUT, DIALOG + BEGIN + BOTTOMMARGIN, 132 + END END #endif // APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_LOGO BITMAP "res\\logo.bmp" +IDB_ABOUT BITMAP "res\\about.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_BUTTON_2K ICON "res\\button-2k.ico" +IDI_BUTTON_XP ICON "res\\button-xp.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_CONTEXT_MENU MENU +BEGIN + POPUP "ContextMenu" + BEGIN + MENUITEM "About Rendezvous...", ID_Menu + MENUITEM SEPARATOR + END +END + + ///////////////////////////////////////////////////////////////////////////// // // String Table @@ -144,7 +198,7 @@ BEGIN IDS_WEB_SITES "Web Sites" IDS_FTP_SITES "FTP Sites" IDS_PRINTERS "Printers" - IDS_RENDEZVOUS_NOT_AVAILABLE "Rendezvous Not Available" + IDS_MDNSRESPONDER_NOT_AVAILABLE "Rendezvous Service Not Available" END #endif // English (U.S.) resources diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.vcproj b/Clients/ExplorerPlugin/ExplorerPlugin.vcproj similarity index 58% rename from mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.vcproj rename to Clients/ExplorerPlugin/ExplorerPlugin.vcproj index ec8b589..62f37fc 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.vcproj +++ b/Clients/ExplorerPlugin/ExplorerPlugin.vcproj @@ -22,34 +22,36 @@ + Culture="1033" + AdditionalIncludeDirectories="../../mDNSWindows"/> @@ -122,7 +125,7 @@ + Culture="1033" + AdditionalIncludeDirectories="../../mDNSWindows"/> + RelativePath="..\..\mDNSWindows\CommonServices.h"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RelativePath=".\about.bmp"> + RelativePath=".\res\about.bmp"> + RelativePath=".\res\cold.ico"> + RelativePath=".\cold.ico"> + RelativePath=".\hot.ico"> + RelativePath=".\res\hot.ico"> + RelativePath=".\logo.bmp"> + RelativePath=".\res\logo.bmp"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.cpp b/Clients/ExplorerPlugin/LoginDialog.cpp similarity index 93% rename from mDNSWindows/Applications/ExplorerPlugin/LoginDialog.cpp rename to Clients/ExplorerPlugin/LoginDialog.cpp index 1947947..ccee81c 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.cpp +++ b/Clients/ExplorerPlugin/LoginDialog.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,14 @@ Change History (most recent first): $Log: LoginDialog.cpp,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ diff --git a/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.h b/Clients/ExplorerPlugin/LoginDialog.h similarity index 88% rename from mDNSWindows/Applications/ExplorerPlugin/LoginDialog.h rename to Clients/ExplorerPlugin/LoginDialog.h index 11d9d1c..a89c3a6 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/LoginDialog.h +++ b/Clients/ExplorerPlugin/LoginDialog.h @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,14 @@ Change History (most recent first): $Log: LoginDialog.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ diff --git a/mDNSWindows/Applications/ExplorerPlugin/ReadMe.txt b/Clients/ExplorerPlugin/ReadMe.txt similarity index 82% rename from mDNSWindows/Applications/ExplorerPlugin/ReadMe.txt rename to Clients/ExplorerPlugin/ReadMe.txt index ed73c22..2fbe666 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/ReadMe.txt +++ b/Clients/ExplorerPlugin/ReadMe.txt @@ -1,4 +1,4 @@ -The Rendezvous Explorer Plugin is a vertical Explorer bar. It lets you browse for Rendezvous-enabled services directly within Internet Explorer. +The DNSServices Explorer Plugin is a vertical Explorer bar. It lets you browse for DNS-SD advertised 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): diff --git a/mDNSWindows/Applications/ExplorerPlugin/Resource.h b/Clients/ExplorerPlugin/Resource.h similarity index 57% rename from mDNSWindows/Applications/ExplorerPlugin/Resource.h rename to Clients/ExplorerPlugin/Resource.h index 744ae44..2e56708 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/Resource.h +++ b/Clients/ExplorerPlugin/Resource.h @@ -6,16 +6,25 @@ #define IDS_WEB_SITES 107 #define IDS_FTP_SITES 108 #define IDS_PRINTERS 109 -#define IDS_RENDEZVOUS_NOT_AVAILABLE 110 +#define IDS_MDNSRESPONDER_NOT_AVAILABLE 110 +#define IDB_LOGO 115 +#define IDI_BUTTON_2K 115 +#define IDD_ABOUT 118 +#define IDI_BUTTON_XP 118 +#define IDB_ABOUT 119 +#define IDR_CONTEXT_MENU 120 #define IDD_LOGIN 145 +#define IDC_COMPONENT 1001 +#define IDC_LEGAL 1002 #define IDC_LOGIN_USERNAME_TEXT 1182 #define IDC_LOGIN_PASSWORD_TEXT 1183 +#define ID_Menu 40001 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 115 +#define _APS_NEXT_RESOURCE_VALUE 119 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/mDNSWindows/Applications/ExplorerPlugin/StdAfx.cpp b/Clients/ExplorerPlugin/StdAfx.cpp similarity index 81% rename from mDNSWindows/Applications/ExplorerPlugin/StdAfx.cpp rename to Clients/ExplorerPlugin/StdAfx.cpp index 673073f..b97d864 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/StdAfx.cpp +++ b/Clients/ExplorerPlugin/StdAfx.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,14 @@ Change History (most recent first): $Log: StdAfx.cpp,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ diff --git a/mDNSWindows/Applications/ExplorerPlugin/StdAfx.h b/Clients/ExplorerPlugin/StdAfx.h similarity index 86% rename from mDNSWindows/Applications/ExplorerPlugin/StdAfx.h rename to Clients/ExplorerPlugin/StdAfx.h index 45020c1..ca625a1 100644 --- a/mDNSWindows/Applications/ExplorerPlugin/StdAfx.h +++ b/Clients/ExplorerPlugin/StdAfx.h @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,14 @@ Change History (most recent first): $Log: StdAfx.h,v $ +Revision 1.2 2004/07/13 21:24:21 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:34:59 rpantos +Move to Clients from mDNSWindows + 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. +Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer. */ diff --git a/Clients/ExplorerPlugin/res/about.bmp b/Clients/ExplorerPlugin/res/about.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ab5e430bfc16000acc8546eb60f01f03b3fc76d4 GIT binary patch literal 137944 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 literal 0 HcmV?d00001 diff --git a/Clients/ExplorerPlugin/res/button-2k.ico b/Clients/ExplorerPlugin/res/button-2k.ico new file mode 100755 index 0000000000000000000000000000000000000000..3699b44180f893e870c4be2e61e865de834bce06 GIT binary patch literal 10342 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 literal 0 HcmV?d00001 diff --git a/Clients/ExplorerPlugin/res/button-xp.ico b/Clients/ExplorerPlugin/res/button-xp.ico new file mode 100755 index 0000000000000000000000000000000000000000..69d41dd943641702dc5e1659212d4e8d78b0db70 GIT binary patch literal 7574 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 literal 0 HcmV?d00001 diff --git a/Clients/ExplorerPlugin/res/hot.ico b/Clients/ExplorerPlugin/res/hot.ico new file mode 100644 index 0000000000000000000000000000000000000000..95b03d051a292cff9428174135f2c726da147404 GIT binary patch literal 10342 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 wGL1^2(. + Revision 1.3 2004/05/26 01:41:58 cheshire Pass proper flags to DNSSD.enumerateDomains @@ -67,19 +68,34 @@ class DNSSDUnitTest String a; txtRecord.set( "path", "~/names"); + txtRecord.set( "rw", (String) null); + txtRecord.set( "empty", ""); txtRecord.set( "ttl", "4"); byte[] rawBytes = txtRecord.getRawBytes(); System.out.println( ( new String( rawBytes, 0, rawBytes.length)) + " has count " + String.valueOf( txtRecord.size())); + System.out.println( txtRecord); 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)); + txtRecord.set( "path", "~/numbers"); + System.out.println( txtRecord); + + txtRecord.remove( "ttl"); + System.out.println( txtRecord); + + txtRecord.remove( "path"); + System.out.println( txtRecord); + + txtRecord.remove( "at"); + System.out.println( txtRecord); + + txtRecord.set( "rw", "1"); + System.out.println( txtRecord); } public void run() throws DNSSDException diff --git a/Clients/Java/SimpleChat.java b/Clients/Java/SimpleChat.java index 07f58d2..745b9a0 100644 --- a/Clients/Java/SimpleChat.java +++ b/Clients/Java/SimpleChat.java @@ -1,42 +1,45 @@ /* * 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. + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SimpleChat is a simple peer-to-peer chat program that demonstrates - DNSSD registration, browsing, resolving and record-querying. + DNS-SD registration, browsing, resolving and record-querying. To do: - - remove TXTRecord tests - - remove diagnostic printf's - implement better coloring algorithm */ @@ -94,11 +97,7 @@ class SimpleChat implements ResolveListener, RegisterListener, QueryListener, 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); + registration = DNSSD.register( 0, 0, ourName, kChatExampleRegType, "", "", inSocket.getLocalPort(), null, this); new ListenerThread( this, inSocket, dataPacket).start(); } @@ -155,7 +154,6 @@ class SimpleChat implements ResolveListener, RegisterListener, QueryListener, 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) @@ -166,10 +164,6 @@ System.out.println( "ServiceRegisterCallback() invoked as " + serviceName); 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 diff --git a/Clients/Java/SimpleChat.manifest b/Clients/Java/SimpleChat.manifest new file mode 100644 index 0000000..45c0202 --- /dev/null +++ b/Clients/Java/SimpleChat.manifest @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Main-Class: SimpleChat diff --git a/Clients/Java/SwingBrowseListener.java b/Clients/Java/SwingBrowseListener.java index 4855dde..78bfe4f 100644 --- a/Clients/Java/SwingBrowseListener.java +++ b/Clients/Java/SwingBrowseListener.java @@ -1,36 +1,40 @@ /* * 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. - + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ diff --git a/Clients/Java/SwingDomainListener.java b/Clients/Java/SwingDomainListener.java index 2c5840f..c6c380b 100644 --- a/Clients/Java/SwingDomainListener.java +++ b/Clients/Java/SwingDomainListener.java @@ -1,36 +1,40 @@ /* * 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. - + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ diff --git a/Clients/Java/SwingQueryListener.java b/Clients/Java/SwingQueryListener.java index 4b58e67..5f2de1d 100644 --- a/Clients/Java/SwingQueryListener.java +++ b/Clients/Java/SwingQueryListener.java @@ -1,36 +1,40 @@ /* * 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. - + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ diff --git a/Clients/Java/SwingResolveListener.java b/Clients/Java/SwingResolveListener.java index 1422a6e..f2be4b0 100644 --- a/Clients/Java/SwingResolveListener.java +++ b/Clients/Java/SwingResolveListener.java @@ -1,36 +1,40 @@ /* * 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 - + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ diff --git a/Clients/Java/nmakefile b/Clients/Java/nmakefile new file mode 100644 index 0000000..4c655d8 --- /dev/null +++ b/Clients/Java/nmakefile @@ -0,0 +1,106 @@ +# 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@ +# +# +# This Makefile builds .jar files for the DNS-SD Java sample apps. +# You must have the Java support installed. +# +# 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\Common7\tools\vsvars32.bat" +# +# The default location of the JDK is \javasdk. You can override this on the +# command line (e.g. 'nmake JDK=\j2dk1.4.2_03'). + +############################################################################ + +JDK = \javasdk + +CP = copy +RM = del /Q +RMDIR = rmdir /S /Q +JAVAC = $(JDK)\bin\javac +JAVAH = $(JDK)\bin\javah +JAR = $(JDK)\bin\jar + +# Set up diverging paths for debug vs. prod builds +DEBUG=0 +!if $(DEBUG) == 1 +JFLAGS = -g +OBJDIR = objects\debug +BUILDDIR = build\debug +!else +JFLAGS = +OBJDIR = objects\prod +BUILDDIR = build\prod +!endif + +SCOBJ = $(OBJDIR)\SimpleChat +BAOBJ = $(OBJDIR)\BrowserApp + +############################################################################# + +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 $(SCOBJ) mkdir $(SCOBJ) + @if not exist $(BAOBJ) mkdir $(BAOBJ) + @if not exist $(BUILDDIR) mkdir $(BUILDDIR) + +# clean removes targets and objects +clean: + @if exist $(OBJDIR) $(RMDIR) $(OBJDIR) + @if exist $(BUILDDIR) $(RMDIR) $(BUILDDIR) + +############################################################################# + +Java: setup $(BUILDDIR)\SimpleChat.jar $(BUILDDIR)\BrowserApp.jar + @echo "Build complete" + +SIMPLECHATOBJ = $(SCOBJ)\SwingBrowseListener.class \ + $(SCOBJ)\SwingQueryListener.class \ + $(SCOBJ)\SimpleChat.class +SIMPLECHATMAN = SimpleChat.manifest + +$(BUILDDIR)\SimpleChat.jar: $(SIMPLECHATOBJ) $(SIMPLECHATMAN) + $(JAR) -cfm $@ $(SIMPLECHATMAN) -C $(SCOBJ) . + +BROWSERAPPOBJ = $(BAOBJ)\SwingResolveListener.class \ + $(BAOBJ)\BrowserApp.class +BROWSERAPPMAN = BrowserApp.manifest + +$(BUILDDIR)\BrowserApp.jar: $(BROWSERAPPOBJ) $(BROWSERAPPMAN) + $(JAR) -cfm $@ $(BROWSERAPPMAN) -C $(BAOBJ) . + +JAVASRC = . +.SUFFIXES : .java +{$(JAVASRC)}.java{$(BAOBJ)}.class: + $(JAVAC) -d $(BAOBJ) -classpath $(BAOBJ) $< +{$(JAVASRC)}.java{$(SCOBJ)}.class: + $(JAVAC) -d $(SCOBJ) -classpath $(SCOBJ) $< + diff --git a/Clients/Makefile b/Clients/Makefile index 11f4a83..709bd2b 100755 --- a/Clients/Makefile +++ b/Clients/Makefile @@ -20,6 +20,12 @@ # @APPLE_LICENSE_HEADER_END@ # # $Log: Makefile,v $ +# Revision 1.6 2004/09/24 21:15:26 cheshire +# Library "libmdns" misnamed; should be "libdns_sd" +# +# Revision 1.5 2004/09/02 17:32:45 cheshire +# Look for headers in ../mDNSShared before we go to /usr/include +# # Revision 1.4 2004/05/21 17:25:56 cheshire # Fixes to make sample client work on Linux # @@ -43,9 +49,9 @@ ############################################################################# -# If library /usr/lib/libmdns.* exists, then link it -ifneq "$(wildcard /usr/lib/libmdns.*)" "" -LIBS = -lmdns +# If library /usr/lib/libdns_sd.* exists, then link it +ifneq "$(wildcard /usr/lib/libdns_sd.*)" "" +LIBS = -ldns_sd else LIBS = endif @@ -59,4 +65,4 @@ build: mkdir build build/dns-sd: build dns-sd.c - cc $(filter %.c %.o, $+) $(LIBS) -o $@ + cc $(filter %.c %.o, $+) $(LIBS) -I../mDNSShared -o $@ diff --git a/Clients/PrinterSetupWizard/About.cpp b/Clients/PrinterSetupWizard/About.cpp new file mode 100644 index 0000000..6fda710 --- /dev/null +++ b/Clients/PrinterSetupWizard/About.cpp @@ -0,0 +1,31 @@ +// About.cpp : implementation file +// + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "About.h" + + +// CAbout dialog + +IMPLEMENT_DYNAMIC(CAbout, CDialog) +CAbout::CAbout(CWnd* pParent /*=NULL*/) + : CDialog(CAbout::IDD, pParent) +{ +} + +CAbout::~CAbout() +{ +} + +void CAbout::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); +} + + +BEGIN_MESSAGE_MAP(CAbout, CDialog) +END_MESSAGE_MAP() + + +// CAbout message handlers diff --git a/Clients/PrinterSetupWizard/About.h b/Clients/PrinterSetupWizard/About.h new file mode 100644 index 0000000..1d84b7f --- /dev/null +++ b/Clients/PrinterSetupWizard/About.h @@ -0,0 +1,21 @@ +#pragma once + + +// CAbout dialog + +class CAbout : public CDialog +{ + DECLARE_DYNAMIC(CAbout) + +public: + CAbout(CWnd* pParent = NULL); // standard constructor + virtual ~CAbout(); + +// Dialog Data + enum { IDD = IDD_DIALOG1 }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +}; diff --git a/Clients/PrinterSetupWizard/FirstPage.cpp b/Clients/PrinterSetupWizard/FirstPage.cpp new file mode 100644 index 0000000..c71faf5 --- /dev/null +++ b/Clients/PrinterSetupWizard/FirstPage.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1997-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: FirstPage.cpp,v $ +Revision 1.2 2004/07/13 20:15:04 shersche + Load large font name from resource +Bug #: 3726363 + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "FirstPage.h" + + +// CFirstPage dialog + +IMPLEMENT_DYNAMIC(CFirstPage, CPropertyPage) +CFirstPage::CFirstPage() + : CPropertyPage(CFirstPage::IDD) +{ + CString fontName; + + m_psp.dwFlags &= ~(PSP_HASHELP); + m_psp.dwFlags |= PSP_DEFAULT|PSP_HIDEHEADER; + + fontName.LoadString(IDS_LARGE_FONT); + + // create the large font + m_largeFont.CreateFont(-16, 0, 0, 0, + FW_BOLD, FALSE, FALSE, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, fontName); +} + +CFirstPage::~CFirstPage() +{ +} + +void CFirstPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + DDX_Control(pDX, IDC_GREETING, m_greeting); +} + + +BOOL +CFirstPage::OnSetActive() +{ + CPropertySheet* psheet = (CPropertySheet*) GetParent(); + + psheet->SetWizardButtons(PSWIZB_NEXT); + + m_greeting.SetFont(&m_largeFont); + + CString greetingText; + + greetingText.LoadString(IDS_GREETING); + m_greeting.SetWindowText(greetingText); + + return CPropertyPage::OnSetActive(); +} + + +BEGIN_MESSAGE_MAP(CFirstPage, CPropertyPage) +END_MESSAGE_MAP() + + +// CFirstPage message handlers diff --git a/Clients/PrinterSetupWizard/FirstPage.h b/Clients/PrinterSetupWizard/FirstPage.h new file mode 100644 index 0000000..5c23bdf --- /dev/null +++ b/Clients/PrinterSetupWizard/FirstPage.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1997-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: FirstPage.h,v $ +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#pragma once +#include "afxwin.h" + + +// CFirstPage dialog + +class CFirstPage : public CPropertyPage +{ + DECLARE_DYNAMIC(CFirstPage) + +public: + CFirstPage(); + virtual ~CFirstPage(); + +// Dialog Data + enum { IDD = IDD_FIRST_PAGE }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnSetActive(); + + + DECLARE_MESSAGE_MAP() + +private: + + CFont m_largeFont; + +public: + + CStatic m_greeting; +}; diff --git a/Clients/PrinterSetupWizard/FourthPage.cpp b/Clients/PrinterSetupWizard/FourthPage.cpp new file mode 100644 index 0000000..c4ccc52 --- /dev/null +++ b/Clients/PrinterSetupWizard/FourthPage.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1997-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: FourthPage.cpp,v $ +Revision 1.4 2004/07/13 20:15:04 shersche + Load large font name from resource +Bug #: 3726363 + +Revision 1.3 2004/07/12 06:59:03 shersche + Use resource strings for Yes/No +Bug #: 3723695 + +Revision 1.2 2004/06/26 23:27:12 shersche +support for installing multiple printers of the same name + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "FourthPage.h" + + +// CFourthPage dialog + +IMPLEMENT_DYNAMIC(CFourthPage, CPropertyPage) +CFourthPage::CFourthPage() + : CPropertyPage(CFourthPage::IDD), + m_initialized(false) +{ + CString fontName; + + m_psp.dwFlags &= ~(PSP_HASHELP); + m_psp.dwFlags |= PSP_DEFAULT|PSP_HIDEHEADER; + + fontName.LoadString(IDS_LARGE_FONT); + + // create the large font + m_largeFont.CreateFont(-16, 0, 0, 0, + FW_BOLD, FALSE, FALSE, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, fontName); +} + +CFourthPage::~CFourthPage() +{ +} + +void CFourthPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + DDX_Control(pDX, IDC_GOODBYE, m_goodbye); + DDX_Control(pDX, IDC_PRINTER_NAME, m_printerNameCtrl); + DDX_Control(pDX, IDC_PRINTER_MANUFACTURER, m_printerManufacturerCtrl); + DDX_Control(pDX, IDC_PRINTER_MODEL, m_printerModelCtrl); + DDX_Control(pDX, IDC_PRINTER_PORT, m_printerPortCtrl); + DDX_Control(pDX, IDC_PRINTER_DEFAULT, m_printerDefault); +} + + +BEGIN_MESSAGE_MAP(CFourthPage, CPropertyPage) +END_MESSAGE_MAP() + + +// CFourthPage message handlers +OSStatus +CFourthPage::OnInitPage() +{ + return kNoErr; +} + + +BOOL +CFourthPage::OnSetActive() +{ + CPrinterSetupWizardSheet * psheet; + CString goodbyeText; + Printer * printer; + CString defaultText; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + printer = psheet->GetSelectedPrinter(); + require_quiet( psheet, exit ); + + psheet->SetWizardButtons(PSWIZB_BACK|PSWIZB_FINISH); + + if (m_initialized == false) + { + m_initialized = true; + OnInitPage(); + } + + m_goodbye.SetFont(&m_largeFont); + + goodbyeText.LoadString(IDS_GOODBYE); + m_goodbye.SetWindowText(goodbyeText); + + m_printerNameCtrl.SetWindowText( printer->actualName ); + m_printerManufacturerCtrl.SetWindowText ( printer->manufacturer ); + m_printerModelCtrl.SetWindowText ( printer->model ); + m_printerPortCtrl.SetWindowText ( printer->portName ); + + if (printer->deflt) + { + defaultText.LoadString(IDS_YES); + } + else + { + defaultText.LoadString(IDS_NO); + } + + m_printerDefault.SetWindowText ( defaultText ); + +exit: + + return CPropertyPage::OnSetActive(); +} diff --git a/Clients/PrinterSetupWizard/FourthPage.h b/Clients/PrinterSetupWizard/FourthPage.h new file mode 100644 index 0000000..78bbf52 --- /dev/null +++ b/Clients/PrinterSetupWizard/FourthPage.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1997-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: FourthPage.h,v $ +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#pragma once +#include "afxwin.h" + + +// CFourthPage dialog + +class CFourthPage : public CPropertyPage +{ + DECLARE_DYNAMIC(CFourthPage) + +public: + CFourthPage(); + virtual ~CFourthPage(); + +// Dialog Data + enum { IDD = IDD_FOURTH_PAGE }; + + virtual BOOL OnSetActive(); + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() + +private: + + OSStatus OnInitPage(); + CFont m_largeFont; + bool m_initialized; + + +public: + CStatic m_goodbye; +private: + CStatic m_printerNameCtrl; + CStatic m_printerManufacturerCtrl; + CStatic m_printerModelCtrl; + CStatic m_printerPortCtrl; + CStatic m_printerDefault; +}; diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizard.ncb b/Clients/PrinterSetupWizard/PrinterSetupWizard.ncb new file mode 100644 index 0000000..9057eba --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizard.ncb @@ -0,0 +1 @@ +Microsoft C/C++ MSF 7.00 diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizard.rc b/Clients/PrinterSetupWizard/PrinterSetupWizard.rc new file mode 100644 index 0000000..f099a44 --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizard.rc @@ -0,0 +1,346 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "WinVersRes.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Russian resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +#ifdef _WIN32 +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT +#pragma code_page(1251) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "#include ""WinVersRes.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 ""res\\PrinterSetupWizard.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Russian resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// 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 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON "res\\Print.ico" +IDI_INFO ICON "res\\Info.ico" +IDI_PRINTER ICON "res\\NetworkPrinter.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_WATERMARK BITMAP "res\\watermark.bmp" +IDB_BANNER_ICON BITMAP "res\\banner_icon.bmp" +IDB_ABOUT BITMAP "res\\about.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOGEX 0, 0, 235, 55 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "About Printer Setup Wizard" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20 + LTEXT "Printer Setup Wizard Version 1.0",IDC_STATIC,40,10,119, + 8,SS_NOPREFIX + LTEXT "Copyright (C) 2002",IDC_STATIC,40,25,119,8 + DEFPUSHBUTTON "OK",IDOK,178,7,50,16,WS_GROUP +END + +IDD_PRINTERSETUPWIZARD_DIALOG DIALOGEX 0, 0, 320, 200 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +CAPTION "Printer Setup Wizard" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,263,7,50,16 + PUSHBUTTON "Cancel",IDCANCEL,263,25,50,16 + PUSHBUTTON "Start Wizard",IDC_BUTTON1,100,86,109,35 +END + +IDD_SECOND_PAGE DIALOGEX 0, 0, 290, 154 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Rendezvous Printer Wizard" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Shared Printers:",IDC_STATIC,3,0,171,8 + CONTROL "",IDC_BROWSE_LIST,"SysTreeView32",TVS_DISABLEDRAGDROP | + TVS_SHOWSELALWAYS | TVS_FULLROWSELECT | WS_BORDER | + WS_TABSTOP,2,11,286,142 +END + +IDD_FIRST_PAGE DIALOGEX 0, 0, 290, 199 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Rendezvous Printer Wizard" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Welcome to the Rendezvous Printer Setup Wizard",IDC_GREETING, + 114,7,171,46 + LTEXT "Click next to continue.",IDC_STATIC,115,188,143,8 + LTEXT "This wizard helps you connect to a shared printer using Rendezvous. Make sure your printer is turned on and connected to your network.", + IDC_STATIC,146,60,139,62 + ICON IDI_INFO,IDC_STATIC,118,60,21,20,SS_REALSIZEIMAGE, + WS_EX_TRANSPARENT +END + +IDD_THIRD_PAGE DIALOGEX 0, 0, 290, 154 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Rendezvous Printer Wizard" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_PRINTER_MODEL,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | + LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,110,58,178,76 + CONTROL "",IDC_PRINTER_MANUFACTURER,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | + LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,2,58,105,76 + ICON IDI_PRINTER,IDC_PRINTER_IMAGE,1,0,21,20,SS_REALSIZEIMAGE + LTEXT "",IDC_PRINTER_NAME,40,5,173,8 + LTEXT "The Rendezvous Printer Wizard has auto-selected the following printer settings. Click 'Next' to continue installing this printer.", + IDC_PRINTER_SELECTION_TEXT,40,23,243,26 + CONTROL "Use this printer as the default printer", + IDC_DEFAULT_PRINTER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,142,224,10 + PUSHBUTTON "Have Disk...",IDC_HAVE_DISK,238,140,50,14 +END + +IDD_FOURTH_PAGE DIALOGEX 0, 0, 290, 199 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Rendezvous Printer Wizard" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Completing the Rendezvous Printer Wizard",IDC_GOODBYE, + 116,7,171,27 + LTEXT "You have successfully completed the Rendezvous Printer Wizard. The printer has the following settings:", + IDC_STATIC,116,42,158,31 + LTEXT "Name:",IDC_STATIC,116,78,22,8 + LTEXT "Manufacturer:",IDC_STATIC,116,91,47,8 + LTEXT "Model:",IDC_STATIC,116,104,22,8 + LTEXT "Port:",IDC_STATIC,116,117,17,8 + LTEXT "Default:",IDC_STATIC,116,130,27,8 + LTEXT "",IDC_PRINTER_NAME,172,79,113,8 + LTEXT "",IDC_PRINTER_MANUFACTURER,172,91,113,8 + LTEXT "",IDC_PRINTER_MODEL,172,103,113,8 + LTEXT "",IDC_PRINTER_PORT,172,116,113,8 + LTEXT "",IDC_PRINTER_DEFAULT,172,130,113,8 + LTEXT "To close this wizard, click Finish.",IDC_STATIC,116,187, + 103,8 +END + +IDD_DIALOG1 DIALOGEX 0, 0, 265, 130 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL 138,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE,0,0, + 265,131 + LTEXT "Static",IDC_STATIC,77,12,19,8 + LTEXT "Static",IDC_STATIC,71,46,19,8 + LTEXT "Static",IDC_STATIC,71,89,19,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION MASTER_PROD_VERS + PRODUCTVERSION MASTER_PROD_VERS + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Apple Computer, Inc." + VALUE "FileDescription", "Rendezvous Printer Wizard" + VALUE "FileVersion", MASTER_PROD_VERS_STR + VALUE "InternalName", "RendezvousPrinterWizard.exe" + VALUE "LegalCopyright", "Copyright (C) 2003-2004 Apple Computer, Inc." + VALUE "OriginalFilename", "RendezvousPrinterWizard.exe" + VALUE "ProductName", MASTER_PROD_NAME + VALUE "ProductVersion", MASTER_PROD_VERS_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_SECOND_PAGE, DIALOG + BEGIN + BOTTOMMARGIN, 153 + END + + IDD_FIRST_PAGE, DIALOG + BEGIN + RIGHTMARGIN, 285 + BOTTOMMARGIN, 196 + END + + IDD_FOURTH_PAGE, DIALOG + BEGIN + RIGHTMARGIN, 285 + BOTTOMMARGIN, 197 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +IDR_MANIFEST RT_MANIFEST "res\\PrinterSetupWizard.manifest" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ABOUTBOX "&About Printer Setup Wizard..." + IDS_GOODBYE "Completing the Rendezvous Printer Setup Wizard." + IDS_GREETING "Welcome to the Rendezvous Printer Setup Wizard" + IDS_BROWSE_TITLE "Browse for Rendezvous Printers" + IDS_BROWSE_SUBTITLE "Select the printer you want to use from the list below." + IDS_CAPTION "Rendezvous Printer Wizard" + IDS_GOODBYE_GOOD1 "You have successfully completed the Rendezvous Printer Wizard. The printer has the following settings:" + IDS_SEARCHING "Searching for printers..." + IDS_GOODBYTE_GOOD2 "To close this wizard, click Finish." + IDS_INSTALL_TITLE "Install Rendezvous Printer" + IDS_INSTALL_SUBTITLE "The manufacturer and model determine which printer software to use." +END + +STRINGTABLE +BEGIN + IDS_ERROR_SELECTING_PRINTER_TEXT + "There was an error selecting this printer." + IDS_ERROR_SELECTING_PRINTER_CAPTION "Error" + IDS_INSTALL_ERROR_CAPTION "Error" + IDS_INSTALL_ERROR_MESSAGE + "You do not have sufficient access to your computer to connect to the selected printer." + IDS_MANUFACTURER_HEADING "Manufacturer" + IDS_MODEL_HEADING "Model" + IDS_NO_RENDEZVOUS_PRINTERS "No Rendezvous Printers are available" + IDS_NO_MDNSRESPONDER_SERVICE_TEXT "Rendezvous Service is not available" + IDS_NO_MDNSRESPONDER_SERVICE_CAPTION "Error" + IDS_PRINTER_MATCH_GOOD "The Rendezvous Printer Wizard has auto-selected the following printer settings. Click 'Next' to continue installing this printer." + IDS_PRINTER_MATCH_BAD "The Rendezvous Printer Wizard cannot find a driver for this printer. Manually select from the list, or click 'Have Disk' if your printer came with an installation disk. " + IDS_YES "Yes" + IDS_NO "No" + IDS_LARGE_FONT "MS Sans Serif" +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 "res\PrinterSetupWizard.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj b/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj new file mode 100644 index 0000000..280c398 --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp new file mode 100644 index 0000000..295b4a9 --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1997-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: PrinterSetupWizardApp.cpp,v $ +Revision 1.3 2004/07/13 21:24:23 rpantos +Fix for . + +Revision 1.2 2004/06/24 20:12:08 shersche +Clean up source code +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "DebugServices.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +// CPrinterSetupWizardApp + +BEGIN_MESSAGE_MAP(CPrinterSetupWizardApp, CWinApp) + ON_COMMAND(ID_HELP, CWinApp::OnHelp) +END_MESSAGE_MAP() + + +// CPrinterSetupWizardApp construction + +CPrinterSetupWizardApp::CPrinterSetupWizardApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + + +// The one and only CPrinterSetupWizardApp object + +CPrinterSetupWizardApp theApp; + + +// CPrinterSetupWizardApp initialization + +BOOL CPrinterSetupWizardApp::InitInstance() +{ + // + // initialize the debugging framework + // + debug_initialize( kDebugOutputTypeWindowsDebugger, "PrinterSetupWizard", NULL ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelTrace ); + + + // InitCommonControls() is required on Windows XP if an application + // manifest specifies use of ComCtl32.dll version 6 or later to enable + // visual styles. Otherwise, any window creation will fail. + InitCommonControls(); + + CWinApp::InitInstance(); + + AfxEnableControlContainer(); + + CPrinterSetupWizardSheet dlg(IDS_CAPTION); + + m_pMainWnd = &dlg; + + try + { + INT_PTR nResponse = dlg.DoModal(); + + if (nResponse == IDOK) + { + // TODO: Place code here to handle when the dialog is + // dismissed with OK + } + else if (nResponse == IDCANCEL) + { + // TODO: Place code here to handle when the dialog is + // dismissed with Cancel + } + } + catch (CPrinterSetupWizardSheet::WizardException & exc) + { + MessageBox(NULL, exc.text, exc.caption, MB_OK|MB_ICONEXCLAMATION); + } + + // Since the dialog has been closed, return FALSE so that we exit the + // application, rather than start the application's message pump. + return FALSE; +} diff --git a/mDNSWindows/Applications/mdnsNSP/Prefix.h b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.h similarity index 62% rename from mDNSWindows/Applications/mdnsNSP/Prefix.h rename to Clients/PrinterSetupWizard/PrinterSetupWizardApp.h index b1b7369..aa3f233 100644 --- a/mDNSWindows/Applications/mdnsNSP/Prefix.h +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardApp.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * 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 @@ -24,22 +22,39 @@ Change History (most recent first): -$Log: Prefix.h,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. +$Log: PrinterSetupWizardApp.h,v $ +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + */ -#ifndef __PREFIX__ -#define __PREFIX__ +#pragma once -#if( defined( _DEBUG ) ) - #define DEBUG 1 - #define DNS_SD_DIRECT_ENABLED 0 -#else - #define DEBUG 0 - #define DNS_SD_DIRECT_ENABLED 0 +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH #endif -#endif // __PREFIX__ +#include "resource.h" // main symbols + + +// CWiz97_3App: +// See Wiz97_3.cpp for the implementation of this class +// + +class CPrinterSetupWizardApp : public CWinApp +{ +public: + CPrinterSetupWizardApp(); + +// Overrides + public: + virtual BOOL InitInstance(); + +// Implementation + + DECLARE_MESSAGE_MAP() +}; + + +extern CPrinterSetupWizardApp theApp; diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp new file mode 100644 index 0000000..02b2952 --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.cpp @@ -0,0 +1,1085 @@ +/* + * Copyright (c) 1997-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: PrinterSetupWizardSheet.cpp,v $ +Revision 1.17 2004/10/12 18:02:53 shersche + Escape '/', '@', '"' characters in printui command. +Bug #: 3764873 + +Revision 1.16 2004/09/13 21:27:22 shersche + Pass the moreComing flag to OnAddPrinter and OnRemovePrinter callbacks +Bug #: 3796483 + +Revision 1.15 2004/09/11 05:59:06 shersche + Fix code that generates unique printer names based on currently installed printers +Bug #: 3785766 + +Revision 1.14 2004/09/02 01:57:58 cheshire + Fix incorrect testing of MoreComing flag + +Revision 1.13 2004/07/26 21:06:29 shersche + Removing trailing '.' in hostname +Bug #: 3739200 + +Revision 1.12 2004/07/13 21:24:23 rpantos +Fix for . + +Revision 1.11 2004/06/28 00:51:47 shersche +Move call to EnumPrinters out of browse callback into standalone function + +Revision 1.10 2004/06/27 23:06:47 shersche +code cleanup, make sure EnumPrinters returns non-zero value + +Revision 1.9 2004/06/27 15:49:31 shersche +clean up some cruft in the printer browsing code + +Revision 1.8 2004/06/27 08:04:51 shersche +copy selected printer to prevent printer being deleted out from under + +Revision 1.7 2004/06/26 23:27:12 shersche +support for installing multiple printers of the same name + +Revision 1.6 2004/06/26 21:22:39 shersche +handle spaces in file names + +Revision 1.5 2004/06/26 03:19:57 shersche +clean up warning messages + +Submitted by: herscher + +Revision 1.4 2004/06/25 02:26:52 shersche +Normalize key fields in text record entries +Submitted by: herscher + +Revision 1.3 2004/06/24 20:12:07 shersche +Clean up source code +Submitted by: herscher + +Revision 1.2 2004/06/23 17:58:21 shersche + eliminated memory leaks on exit + installation of a printer that is already installed results in a no-op +Bug #: 3701837, 3701926 +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "CommonServices.h" +#include "DebugServices.h" +#include "WinServices.h" +#include "About.h" +#include +#include +#include + +// unreachable code +#pragma warning(disable:4702) + + +#if( !TARGET_OS_WINDOWS_CE ) +# include +# include +#endif + +// Private Messages + +#define WM_SERVICE_EVENT ( WM_USER + 0x100 ) +#define WM_PROCESS_EVENT ( WM_USER + 0x101 ) + +// Service Types + +#define kPDLDataStreamServiceType "_pdl-datastream._tcp" +#define kLPRServiceType "_printer._tcp" +#define kIPPServiceType "_ipp._tcp" + + +// CPrinterSetupWizardSheet + +IMPLEMENT_DYNAMIC(CPrinterSetupWizardSheet, CPropertySheet) +CPrinterSetupWizardSheet::CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) + :CPropertySheet(nIDCaption, pParentWnd, iSelectPage), + m_selectedPrinter(NULL) +{ + m_arrow = LoadCursor(0, IDC_ARROW); + m_wait = LoadCursor(0, IDC_APPSTARTING); + m_active = m_arrow; + + Init(); +} + + +CPrinterSetupWizardSheet::~CPrinterSetupWizardSheet() +{ + // + // rdar://problem/3701837 memory leaks + // + // Clean up the ServiceRef and printer list on exit + // + if (m_pdlBrowser != NULL) + { + DNSServiceRefDeallocate(m_pdlBrowser); + m_pdlBrowser = NULL; + } + + while (m_printerList.size() > 0) + { + Printer * printer = m_printerList.front(); + + m_printerList.pop_front(); + + delete printer; + } + + if (m_selectedPrinter != NULL) + { + delete m_selectedPrinter; + } +} + + +// ------------------------------------------------------ +// InstallEventHandler +// +// Installs an event handler for DNSService events. +// +int +CPrinterSetupWizardSheet::InstallEventHandler(EventHandler * handler) +{ + PrinterList::iterator iter; + + m_eventHandlerList.push_back(handler); + + iter = m_printerList.begin(); + + while (iter != m_printerList.end()) + { + Printer * printer = *iter++; + + handler->OnAddPrinter(printer, iter != m_printerList.end()); + } + + return kNoErr; +} + + +// ------------------------------------------------------ +// RemoveEventHandler +// +// Removes an event handler for DNSService events. +// +int +CPrinterSetupWizardSheet::RemoveEventHandler(EventHandler * handler) +{ + m_eventHandlerList.remove(handler); + + return kNoErr; +} + + + +// ------------------------------------------------------ +// SetSelectedPrinter +// +// Manages setting a printer as the printer to install. Stops +// any pending resolves. +// +OSStatus +CPrinterSetupWizardSheet::SetSelectedPrinter(Printer * printer) +{ + OSStatus err; + + // + // we only want one resolve going on at a time, so we check + // state of the m_selectedPrinter + // + if (m_selectedPrinter != NULL) + { + // + // if we're currently resolving, then stop the resolve + // + if (m_selectedPrinter->serviceRef) + { + err = StopResolve(m_selectedPrinter); + require_noerr(err, exit); + } + + delete m_selectedPrinter; + m_selectedPrinter = NULL; + } + + check( m_selectedPrinter == NULL ); + + try + { + m_selectedPrinter = new Printer; + } + catch (...) + { + m_selectedPrinter = NULL; + } + + require_action( m_selectedPrinter, exit, err = E_OUTOFMEMORY ); + + m_selectedPrinter->window = printer->window; + m_selectedPrinter->serviceRef = NULL; + m_selectedPrinter->item = NULL; + m_selectedPrinter->ifi = printer->ifi; + m_selectedPrinter->name = printer->name; + m_selectedPrinter->displayName = printer->displayName; + m_selectedPrinter->actualName = printer->actualName; + m_selectedPrinter->type = printer->type; + m_selectedPrinter->domain = printer->domain; + m_selectedPrinter->installed = printer->installed; + m_selectedPrinter->deflt = printer->deflt; + m_selectedPrinter->refs = 1; + + err = StartResolve(m_selectedPrinter); + require_noerr(err, exit); + +exit: + + return err; +} + + +// ------------------------------------------------------ +// InstallPrinter +// +// Installs a printer with Windows. +// +// NOTE: this works one of two ways, depending on whether +// there are drivers already installed for this printer. +// If there are, then we can just create a port with XcvData, +// and then call AddPrinter. If not, we use the printui.dll +// to install the printer. Actually installing drivers that +// are not currently installed is painful, and it's much +// easier and less error prone to just let printui.dll do +// the hard work for us. +// + +OSStatus +CPrinterSetupWizardSheet::InstallPrinter(Printer * printer) +{ + PRINTER_DEFAULTS printerDefaults = { NULL, NULL, SERVER_ACCESS_ADMINISTER }; + DWORD dwStatus; + DWORD cbInputData = 100; + PBYTE pOutputData = NULL; + DWORD cbOutputNeeded = 0; + PORT_DATA_1 portData; + HANDLE hXcv = NULL; + HANDLE hPrinter = NULL; + BOOL ok; + OSStatus err; + + check(printer != NULL); + check(printer->installed == false); + + ok = OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hXcv, &printerDefaults); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + // + // BUGBUG: MSDN said this is not required, but my experience shows it is required + // + try + { + pOutputData = new BYTE[cbInputData]; + } + catch (...) + { + pOutputData = NULL; + } + + require_action( pOutputData, exit, err = kNoMemoryErr ); + + // + // setup the port + // + ZeroMemory(&portData, sizeof(PORT_DATA_1)); + wcscpy(portData.sztPortName, printer->portName); + + portData.dwPortNumber = printer->portNumber; + portData.dwVersion = 1; + + portData.dwProtocol = PROTOCOL_RAWTCP_TYPE; + portData.cbSize = sizeof PORT_DATA_1; + portData.dwReserved = 0L; + + wcscpy(portData.sztQueue, printer->hostname); + wcscpy(portData.sztIPAddress, printer->hostname); + wcscpy(portData.sztHostAddress, printer->hostname); + + ok = XcvData(hXcv, L"AddPort", (PBYTE) &portData, sizeof(PORT_DATA_1), pOutputData, cbInputData, &cbOutputNeeded, &dwStatus); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + if (printer->driverInstalled) + { + PRINTER_INFO_2 pInfo; + + ZeroMemory(&pInfo, sizeof(pInfo)); + + pInfo.pPrinterName = printer->actualName.GetBuffer(); + pInfo.pServerName = NULL; + pInfo.pShareName = NULL; + pInfo.pPortName = printer->portName.GetBuffer(); + pInfo.pDriverName = printer->model.GetBuffer(); + pInfo.pComment = printer->model.GetBuffer(); + pInfo.pLocation = L""; + pInfo.pDevMode = NULL; + pInfo.pDevMode = NULL; + pInfo.pSepFile = L""; + pInfo.pPrintProcessor = L"winprint"; + pInfo.pDatatype = L"RAW"; + pInfo.pParameters = L""; + pInfo.pSecurityDescriptor = NULL; + pInfo.Attributes = PRINTER_ATTRIBUTE_QUEUED; + pInfo.Priority = 0; + pInfo.DefaultPriority = 0; + pInfo.StartTime = 0; + pInfo.UntilTime = 0; + + hPrinter = AddPrinter(NULL, 2, (LPBYTE) &pInfo); + err = translate_errno( hPrinter, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + } + else + { + DWORD dwResult; + HANDLE hThread; + unsigned threadID; + + + m_processFinished = false; + + // + // create the thread + // + hThread = (HANDLE) _beginthreadex_compat( NULL, 0, InstallPrinterThread, printer, 0, &threadID ); + err = translate_errno( hThread, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + // + // go modal + // + while (!m_processFinished) + { + MSG msg; + + GetMessage( &msg, m_hWnd, 0, 0 ); + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // + // Wait until child process exits. + // + dwResult = WaitForSingleObject( hThread, INFINITE ); + err = translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr ); + require_noerr( err, exit ); + } + + printer->installed = true; + + // + // if the user specified a default printer, set it + // + if (printer->deflt) + { + ok = SetDefaultPrinter(printer->actualName); + err = translate_errno( ok, errno_compat(), err = kUnknownErr ); + require_noerr( err, exit ); + } + +exit: + + if (hPrinter != NULL) + { + ClosePrinter(hPrinter); + } + + if (hXcv != NULL) + { + ClosePrinter(hXcv); + } + + if (pOutputData != NULL) + { + delete [] pOutputData; + } + + return err; +} + + +BEGIN_MESSAGE_MAP(CPrinterSetupWizardSheet, CPropertySheet) +ON_MESSAGE( WM_SERVICE_EVENT, OnServiceEvent ) +ON_MESSAGE( WM_PROCESS_EVENT, OnProcessEvent ) +ON_WM_SETCURSOR() +END_MESSAGE_MAP() + + +// ------------------------------------------------------ +// OnCommand +// +// Traps when the user hits Finish +// +BOOL CPrinterSetupWizardSheet::OnCommand(WPARAM wParam, LPARAM lParam) +{ + // + // Check if this is OK + // + if (wParam == ID_WIZFINISH) // If OK is hit... + { + OnOK(); + } + + return CPropertySheet::OnCommand(wParam, lParam); +} + + +// ------------------------------------------------------ +// OnInitDialog +// +// Initializes this Dialog object. We start the browse here, +// so that printers show up instantly when the user clicks +// the next button. +// +BOOL CPrinterSetupWizardSheet::OnInitDialog() +{ + OSStatus err; + + CPropertySheet::OnInitDialog(); + + // + // setup the DNS-SD browsing + // + err = DNSServiceBrowse( &m_pdlBrowser, 0, 0, kPDLDataStreamServiceType, NULL, OnBrowse, this ); + require_noerr( err, exit ); + + m_serviceRefList.push_back(m_pdlBrowser); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(m_pdlBrowser), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + LoadPrinterNames(); + +exit: + + if (err != kNoErr) + { + WizardException exc; + + exc.text.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_TEXT); + exc.caption.LoadString(IDS_NO_MDNSRESPONDER_SERVICE_CAPTION); + + throw(exc); + } + + return TRUE; +} + + +// ------------------------------------------------------ +// OnSetCursor +// +// This is called when Windows wants to know what cursor +// to display. So we tell it. +// +BOOL +CPrinterSetupWizardSheet::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message) +{ + DEBUG_UNUSED(pWnd); + DEBUG_UNUSED(nHitTest); + DEBUG_UNUSED(message); + + SetCursor(m_active); + return TRUE; +} + + +// ------------------------------------------------------ +// OnContextMenu +// +// This is not fully implemented yet. +// + +void +CPrinterSetupWizardSheet::OnContextMenu(CWnd * pWnd, CPoint pos) +{ + DEBUG_UNUSED(pWnd); + DEBUG_UNUSED(pos); + + CAbout dlg; + + dlg.DoModal(); +} + + +// ------------------------------------------------------ +// OnOK +// +// This is called when the user hits the "Finish" button +// +void +CPrinterSetupWizardSheet::OnOK() +{ + check ( m_selectedPrinter != NULL ); + + SetWizardButtons( PSWIZB_DISABLEDFINISH ); + + if ( InstallPrinter( m_selectedPrinter ) != kNoErr ) + { + CString caption; + CString message; + + caption.LoadString(IDS_INSTALL_ERROR_CAPTION); + message.LoadString(IDS_INSTALL_ERROR_MESSAGE); + + MessageBox(message, caption, MB_OK|MB_ICONEXCLAMATION); + } +} + + +// CPrinterSetupWizardSheet message handlers + +void CPrinterSetupWizardSheet::Init(void) +{ + AddPage(&m_pgFirst); + AddPage(&m_pgSecond); + AddPage(&m_pgThird); + AddPage(&m_pgFourth); + + m_psh.dwFlags &= (~PSH_HASHELP); + + m_psh.dwFlags |= PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER; + m_psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK); + m_psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER_ICON); + + m_psh.hInstance = AfxGetInstanceHandle(); + + SetWizardMode(); +} + + +LONG +CPrinterSetupWizardSheet::OnServiceEvent(WPARAM inWParam, LPARAM inLParam) +{ + if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam))) + { + dlog( kDebugLevelError, "OnServiceEvent: window error\n" ); + } + else + { + SOCKET sock = (SOCKET) inWParam; + + // iterate thru list + ServiceRefList::iterator begin = m_serviceRefList.begin(); + ServiceRefList::iterator end = m_serviceRefList.end(); + + while (begin != end) + { + DNSServiceRef ref = *begin++; + + check(ref != NULL); + + if ((SOCKET) DNSServiceRefSockFD(ref) == sock) + { + DNSServiceProcessResult(ref); + break; + } + } + } + + return ( 0 ); +} + + +LONG +CPrinterSetupWizardSheet::OnProcessEvent(WPARAM inWParam, LPARAM inLParam) +{ + DEBUG_UNUSED(inWParam); + DEBUG_UNUSED(inLParam); + + m_processFinished = true; + + return 0; +} + + +void DNSSD_API +CPrinterSetupWizardSheet::OnBrowse( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ) +{ + DEBUG_UNUSED(inRef); + + CPrinterSetupWizardSheet * self; + Printer * printer; + EventHandlerList::iterator it; + DWORD printerNameCount; + bool moreComing = (bool) (inFlags & kDNSServiceFlagsMoreComing); + + require_noerr( inErrorCode, exit ); + + self = reinterpret_cast ( inContext ); + require_quiet( self, exit ); + + printer = self->LookUp(inName); + + if (inFlags & kDNSServiceFlagsAdd) + { + OSStatus err; + + if (printer != NULL) + { + printer->refs++; + } + else + { + try + { + printer = new Printer; + } + catch (...) + { + printer = NULL; + } + + require_action( printer, exit, err = E_OUTOFMEMORY ); + + printer->window = self; + printer->ifi = inInterfaceIndex; + printer->name = inName; + err = UTF8StringToStringObject(inName, printer->displayName); + check_noerr( err ); + printer->actualName = printer->displayName; + + // + // Compare this name against printers that are already installed + // to avoid name clashes. Rename as necessary + // to come up with a unique name. + // + printerNameCount = 2; + + for (;;) + { + PrinterNameMap::iterator it; + + it = self->m_printerNames.find(printer->actualName); + + if (it != self->m_printerNames.end()) + { + printer->actualName.Format(L"%s (%d)", printer->displayName, printerNameCount); + } + else + { + break; + } + + printerNameCount++; + } + + printer->type = inType; + printer->domain = inDomain; + printer->installed = false; + printer->deflt = false; + printer->refs = 1; + + self->m_printerList.push_back( printer ); + + // + // now invoke event handlers for AddPrinter event + // + for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++) + { + EventHandler * handler = *it; + + handler->OnAddPrinter(printer, moreComing); + } + } + } + else + { + if ((printer != NULL) && (--printer->refs == 0)) + { + // + // now invoke event handlers for RemovePrinter event + // + for (it = self->m_eventHandlerList.begin(); it != self->m_eventHandlerList.end(); it++) + { + EventHandler * handler = *it; + + handler->OnRemovePrinter(printer, moreComing); + } + + self->m_printerList.remove(printer); + + // + // check to see if we've selected this printer + // + if (self->m_selectedPrinter == printer) + { + // + // this guy is being removed while we're resolving it...so let's + // stop the resolve + // + if (printer->serviceRef != NULL) + { + self->StopResolve(printer); + } + + self->m_selectedPrinter = NULL; + } + + delete printer; + } + } + +exit: + + return; +} + + +void DNSSD_API +CPrinterSetupWizardSheet::OnResolve( + 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 ) +{ + DEBUG_UNUSED(inFullName); + DEBUG_UNUSED(inInterfaceIndex); + DEBUG_UNUSED(inFlags); + DEBUG_UNUSED(inRef); + + Printer * printer; + CPrinterSetupWizardSheet * self; + EventHandlerList::iterator it1; + EventHandlerList::iterator it2; + int idx; + OSStatus err; + + require_noerr( inErrorCode, exit ); + + printer = reinterpret_cast( inContext ); + require_quiet( printer, exit); + + self = printer->window; + require_quiet( self, exit ); + + err = self->StopResolve(printer); + require_noerr(err, exit); + + // + // hold on to the hostname... + // + err = UTF8StringToStringObject( inHostName, printer->hostname ); + require_noerr( err, exit ); + + // + // remove the trailing dot on hostname + // + idx = printer->hostname.ReverseFind('.'); + + if ((idx > 1) && ((printer->hostname.GetLength() - 1) == idx)) + { + printer->hostname.Delete(idx, 1); + } + + // + // hold on to the port + // + printer->portNumber = ntohs(inPort); + + // + // parse the text record. we create a stringlist of text record + // entries that can be interrogated later + // + while (inTXTSize) + { + char buf[256]; + + unsigned char num = *inTXT; + check( (int) num < inTXTSize ); + + memset(buf, 0, sizeof(buf)); + memcpy(buf, inTXT + 1, num); + + inTXTSize -= (num + 1); + inTXT += (num + 1); + + CString elem; + + err = UTF8StringToStringObject( buf, elem ); + require_noerr( err, exit ); + + int curPos = 0; + + CString key = elem.Tokenize(L"=", curPos); + CString val = elem.Tokenize(L"=", curPos); + + key.MakeLower(); + + if ((key == L"usb_mfg") || (key == L"usb_manufacturer")) + { + printer->usb_MFG = val; + } + else if ((key == L"usb_mdl") || (key == L"usb_model")) + { + printer->usb_MDL = val; + } + else if (key == L"description") + { + printer->description = val; + } + else if (key == L"product") + { + printer->product = val; + } + } + + // + // now invoke event handlers for Resolve event + // + it1 = self->m_eventHandlerList.begin(); + it2 = self->m_eventHandlerList.end(); + + while (it1 != it2) + { + EventHandler * handler = *it1++; + + handler->OnResolvePrinter(printer); + } + +exit: + + return; +} + + +OSStatus +CPrinterSetupWizardSheet::LoadPrinterNames() +{ + PBYTE buffer = NULL; + OSStatus err = 0; + + // + // rdar://problem/3701926 - Printer can't be installed twice + // + // First thing we want to do is make sure the printer isn't already installed. + // If the printer name is found, we'll try and rename it until we + // find a unique name + // + DWORD dwNeeded = 0, dwNumPrinters = 0; + + BOOL ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwNumPrinters); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + + if ((err == ERROR_INSUFFICIENT_BUFFER) && (dwNeeded > 0)) + { + try + { + buffer = new unsigned char[dwNeeded]; + } + catch (...) + { + buffer = NULL; + } + + require_action( buffer, exit, kNoMemoryErr ); + ok = EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, buffer, dwNeeded, &dwNeeded, &dwNumPrinters); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + for (DWORD index = 0; index < dwNumPrinters; index++) + { + PRINTER_INFO_4 * lppi4 = (PRINTER_INFO_4*) (buffer + index * sizeof(PRINTER_INFO_4)); + + m_printerNames[lppi4->pPrinterName] = lppi4->pPrinterName; + } + } + +exit: + + if (buffer != NULL) + { + delete [] buffer; + } + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::StartResolve(Printer * printer) +{ + OSStatus err; + + check( printer ); + + err = DNSServiceResolve( &printer->serviceRef, 0, 0, printer->name.c_str(), printer->type.c_str(), printer->domain.c_str(), (DNSServiceResolveReply) OnResolve, printer ); + require_noerr( err, exit); + + m_serviceRefList.push_back(printer->serviceRef); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, WM_SERVICE_EVENT, FD_READ|FD_CLOSE); + require_noerr( err, exit ); + + // + // set the cursor to arrow+hourglass + // + m_active = m_wait; + SetCursor(m_active); + +exit: + + return err; +} + + +OSStatus +CPrinterSetupWizardSheet::StopResolve(Printer * printer) +{ + OSStatus err; + + check( printer ); + check( printer->serviceRef ); + + m_serviceRefList.remove( printer->serviceRef ); + + err = WSAAsyncSelect((SOCKET) DNSServiceRefSockFD(printer->serviceRef), m_hWnd, 0, 0); + check(err == 0); + + DNSServiceRefDeallocate( printer->serviceRef ); + + printer->serviceRef = NULL; + + // + // set the cursor back to normal + // + m_active = m_arrow; + SetCursor(m_active); + + return kNoErr; +} + + +Printer* +CPrinterSetupWizardSheet::LookUp(const char * inName) +{ + PrinterList::iterator it1 = m_printerList.begin(); + PrinterList::iterator it2 = m_printerList.end(); + + while (it1 != it2) + { + Printer * printer = *it1++; + + if (printer->name == inName) + { + return printer; + } + } + + return NULL; +} + + +unsigned WINAPI +CPrinterSetupWizardSheet::InstallPrinterThread( LPVOID inParam ) +{ + check( inParam ); + + Printer * printer = (Printer*) inParam; + CString actualName; + CString command; + DWORD exitCode = 0; + DWORD dwResult; + OSStatus err; + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL ok; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + + // + // Escape '\', '@', '"' characters which seem to cause problems for printui + // + + actualName = printer->actualName; + + actualName.Replace(L"\\", L"\\\\"); + actualName.Replace(L"@", L"\\@"); + actualName.Replace(L"\"", L"\\\""); + + command.Format(L"rundll32.exe printui.dll,PrintUIEntry /if /b \"%s\" /f \"%s\" /r \"%s\" /m \"%s\"", (LPCTSTR) actualName, (LPCTSTR) printer->infFileName, (LPCTSTR) printer->portName, (LPCTSTR) printer->model); + + ok = CreateProcess(NULL, command.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + dwResult = WaitForSingleObject( pi.hProcess, INFINITE ); + translate_errno( dwResult == WAIT_OBJECT_0, errno_compat(), err = kUnknownErr ); + require_noerr( err, exit ); + + ok = GetExitCodeProcess( pi.hProcess, &exitCode ); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + // + // Close process and thread handles. + // + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + +exit: + + // + // alert the main thread + // + printer->window->PostMessage( WM_PROCESS_EVENT, err, exitCode ); + + return 0; +} diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h new file mode 100644 index 0000000..e1077aa --- /dev/null +++ b/Clients/PrinterSetupWizard/PrinterSetupWizardSheet.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1997-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: PrinterSetupWizardSheet.h,v $ +Revision 1.4 2004/07/13 21:24:23 rpantos +Fix for . + +Revision 1.3 2004/06/28 00:51:47 shersche +Move call to EnumPrinters out of browse callback into standalone function + +Revision 1.2 2004/06/24 20:12:07 shersche +Clean up source code +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#pragma once + + +#include "firstpage.h" +#include "secondpage.h" +#include "thirdpage.h" +#include "fourthpage.h" +#include "UtilTypes.h" +#include "dns_sd.h" +#include +#include + +using namespace PrinterSetupWizard; + +// CPrinterSetupWizardSheet + +class CPrinterSetupWizardSheet : public CPropertySheet +{ + DECLARE_DYNAMIC(CPrinterSetupWizardSheet) + +public: + + struct WizardException + { + CString text; + CString caption; + }; + +public: + + CPrinterSetupWizardSheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0); + virtual ~CPrinterSetupWizardSheet(); + + int + InstallEventHandler(EventHandler * handler); + + int + RemoveEventHandler(EventHandler * handler); + + OSStatus + SetSelectedPrinter(Printer * printer); + + Printer* + GetSelectedPrinter(); + + OSStatus + LoadPrinterDriver(const CString & filename); + + HCURSOR + GetCursor(); + + // + // handles socket events for DNSService operations + // + virtual LONG + OnServiceEvent(WPARAM inWParam, LPARAM inLParam); + + // + // handles end of process event + // + virtual LONG + OnProcessEvent(WPARAM inWParam, LPARAM inLParam); + + virtual BOOL + OnCommand(WPARAM wParam, LPARAM lParam); + + virtual BOOL + OnInitDialog(); + + virtual BOOL + OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message); + + virtual void + OnContextMenu(CWnd * pWnd, CPoint pos); + + afx_msg void + OnOK(); + +protected: + DECLARE_MESSAGE_MAP() + CFirstPage m_pgFirst; + CSecondPage m_pgSecond; + CThirdPage m_pgThird; + CFourthPage m_pgFourth; + + static void DNSSD_API + OnBrowse( + DNSServiceRef inRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inErrorCode, + const char * inName, + const char * inType, + const char * inDomain, + void * inContext ); + + static void DNSSD_API + OnResolve( + 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 ); + + void Init(void); + +private: + + OSStatus + LoadPrinterNames(); + + OSStatus + StartResolve(Printer * printer); + + OSStatus + StopResolve(Printer * printer); + + Printer* + LookUp(const char * inName); + + OSStatus + InstallPrinter(Printer * printer); + + static unsigned WINAPI + InstallPrinterThread( LPVOID inParam ); + + + typedef std::list PrinterList; + typedef std::list EventHandlerList; + typedef std::list ServiceRefList; + typedef std::map PrinterNameMap; + + Printer * m_selectedPrinter; + + PrinterNameMap m_printerNames; + PrinterList m_printerList; + EventHandlerList m_eventHandlerList; + ServiceRefList m_serviceRefList; + + bool m_processFinished; + + HCURSOR m_active; + HCURSOR m_arrow; + HCURSOR m_wait; + + DNSServiceRef m_pdlBrowser; +}; + + +inline Printer* +CPrinterSetupWizardSheet::GetSelectedPrinter() +{ + return m_selectedPrinter; +} + + +inline HCURSOR +CPrinterSetupWizardSheet::GetCursor() +{ + return m_active; +} diff --git a/Clients/PrinterSetupWizard/ReadMe.txt b/Clients/PrinterSetupWizard/ReadMe.txt new file mode 100644 index 0000000..9b5eb41 --- /dev/null +++ b/Clients/PrinterSetupWizard/ReadMe.txt @@ -0,0 +1,94 @@ +================================================================================ + MICROSOFT FOUNDATION CLASS LIBRARY : Wiz97_3 Project Overview +=============================================================================== + +The application wizard has created this Wiz97_3 application for +you. This application not only demonstrates the basics of using the Microsoft +Foundation Classes but is also a starting point for writing your application. + +This file contains a summary of what you will find in each of the files that +make up your Wiz97_3 application. + +Wiz97_3.vcproj + This is the main project file for VC++ projects generated using an application wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + application wizard. + +Wiz97_3.h + This is the main header file for the application. It includes other + project specific headers (including Resource.h) and declares the + CWiz97_3App application class. + +Wiz97_3.cpp + This is the main application source file that contains the application + class CWiz97_3App. + +Wiz97_3.rc + This is a listing of all of the Microsoft Windows resources that the + program uses. It includes the icons, bitmaps, and cursors that are stored + in the RES subdirectory. This file can be directly edited in Microsoft + Visual C++. Your project resources are in 1033. + +res\Wiz97_3.ico + This is an icon file, which is used as the application's icon. This + icon is included by the main resource file Wiz97_3.rc. + +res\Wiz97_3.rc2 + This file contains resources that are not edited by Microsoft + Visual C++. You should place all resources not editable by + the resource editor in this file. + +///////////////////////////////////////////////////////////////////////////// + +The application wizard creates one dialog class: +Wiz97_3Dlg.h, Wiz97_3Dlg.cpp - the dialog + These files contain your CWiz97_3Dlg class. This class defines + the behavior of your application's main dialog. The dialog's template is + in Wiz97_3.rc, which can be edited in Microsoft Visual C++. +///////////////////////////////////////////////////////////////////////////// + +Other Features: + +ActiveX Controls + The application includes support to use ActiveX controls. + +Printing and Print Preview support + The application wizard has generated code to handle the print, print setup, and print preview + commands by calling member functions in the CView class from the MFC library. +///////////////////////////////////////////////////////////////////////////// + +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named Wiz97_3.pch and a precompiled types file named StdAfx.obj. + +Resource.h + This is the standard header file, which defines new resource IDs. + Microsoft Visual C++ reads and updates this file. + +Wiz97_3.manifest + Application manifest files are used by Windows XP to describe an applications + dependency on specific versions of Side-by-Side assemblies. The loader uses this + information to load the appropriate assembly from the assembly cache or private + from the application. The Application manifest maybe included for redistribution + as an external .manifest file that is installed in the same folder as the application + executable or it may be included in the executable in the form of a resource. +///////////////////////////////////////////////////////////////////////////// + +Other notes: + +The application wizard uses "TODO:" to indicate parts of the source code you +should add to or customize. + +If your application uses MFC in a shared DLL, and your application is in a +language other than the operating system's current language, you will need +to copy the corresponding localized resources MFC70XXX.DLL from the Microsoft +Visual C++ CD-ROM under the Win\System directory to your computer's system or +system32 directory, and rename it to be MFCLOC.DLL. ("XXX" stands for the +language abbreviation. For example, MFC70DEU.DLL contains resources +translated to German.) If you don't do this, some of the UI elements of +your application will remain in the language of the operating system. + +///////////////////////////////////////////////////////////////////////////// diff --git a/Clients/PrinterSetupWizard/SecondPage.cpp b/Clients/PrinterSetupWizard/SecondPage.cpp new file mode 100644 index 0000000..abf4fa0 --- /dev/null +++ b/Clients/PrinterSetupWizard/SecondPage.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (c) 1997-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: SecondPage.cpp,v $ +Revision 1.3 2004/09/13 21:26:15 shersche + Use the moreComing flag to determine whether drawing should take place in OnAddPrinter and OnRemovePrinter callbacks +Bug #: 3796483 + +Revision 1.2 2004/06/26 03:19:57 shersche +clean up warning messages + +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "SecondPage.h" +#include "DebugServices.h" + +// local variable is initialize but not referenced +#pragma warning(disable:4189) + + +// CSecondPage dialog + +IMPLEMENT_DYNAMIC(CSecondPage, CPropertyPage) +CSecondPage::CSecondPage() + : CPropertyPage(CSecondPage::IDD) +{ + m_psp.dwFlags &= ~(PSP_HASHELP); + m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE; + + m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_BROWSE_TITLE); + m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_BROWSE_SUBTITLE); + + m_resolver = NULL; + m_emptyListItem = NULL; + m_initialized = false; + m_waiting = false; +} + +CSecondPage::~CSecondPage() +{ +} + + +void +CSecondPage::InitBrowseList() +{ + CPrinterSetupWizardSheet * psheet; + CString text; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + // + // load the no rendezvous printers message until something shows up in the browse list + // + text.LoadString(IDS_NO_RENDEZVOUS_PRINTERS); + + m_emptyListItem = m_browseList.InsertItem( text, 0, 0, NULL, TVI_FIRST ); + + // + // this will remove everything else in the list...we might be navigating + // back to this window, and the browse list might have changed since + // we last displayed it. + // + if ( m_emptyListItem ) + { + HTREEITEM item = m_browseList.GetNextVisibleItem( m_emptyListItem ); + + while ( item ) + { + m_browseList.DeleteItem( item ); + item = m_browseList.GetNextVisibleItem( m_emptyListItem ); + } + } + + // + // disable the next button until there's a printer to select + // + psheet->SetWizardButtons(PSWIZB_BACK); + + // + // disable the window until there's a printer to select + // + m_browseList.EnableWindow( FALSE ); + +exit: + + return; +} + + +void CSecondPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + DDX_Control(pDX, IDC_BROWSE_LIST, m_browseList); +} + + +afx_msg BOOL +CSecondPage::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message) +{ + DEBUG_UNUSED(pWnd); + DEBUG_UNUSED(nHitTest); + DEBUG_UNUSED(message); + + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + SetCursor(psheet->GetCursor()); + +exit: + + return TRUE; +} + + +BOOL +CSecondPage::OnSetActive() +{ + CString noPrinters; + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + // + // initialize the browse list...this will remove everything currently + // in it, and add the no rendezvous printers item + // + InitBrowseList(); + + // + // this will invoke OnAddPrinter for all the printers that we have + // browsed + // + psheet->InstallEventHandler(this); + +exit: + + return CPropertyPage::OnSetActive(); +} + + +BOOL +CSecondPage::OnKillActive() +{ + CPrinterSetupWizardSheet * psheet; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + // + // we don't want our event handlers called when we don't have + // anywhere to put the data + // + psheet->RemoveEventHandler(this); + +exit: + + return CPropertyPage::OnKillActive(); +} + + +BEGIN_MESSAGE_MAP(CSecondPage, CPropertyPage) + ON_NOTIFY(TVN_SELCHANGED, IDC_BROWSE_LIST, OnTvnSelchangedBrowseList) + ON_WM_SETCURSOR() +END_MESSAGE_MAP() + + +// Printer::EventHandler implementation +void +CSecondPage::OnAddPrinter( + Printer * printer, + bool moreComing) +{ + check( IsWindow( m_hWnd ) ); + + m_browseList.SetRedraw(FALSE); + + printer->item = m_browseList.InsertItem(printer->displayName); + + m_browseList.SetItemData( printer->item, (DWORD_PTR) printer ); + + m_browseList.SortChildren(TVI_ROOT); + + // + // if the searching item is still in the list + // get rid of it + // + // note that order is important here. Insert the printer + // item before removing the placeholder so we always have + // an item in the list to avoid experiencing the bug + // in Microsoft's implementation of CTreeCtrl + // + if (m_emptyListItem != NULL) + { + m_browseList.DeleteItem(m_emptyListItem); + m_emptyListItem = NULL; + m_browseList.EnableWindow(TRUE); + } + + if (!moreComing) + { + m_browseList.SetRedraw(TRUE); + m_browseList.Invalidate(); + } +} + + +void +CSecondPage::OnRemovePrinter( + Printer * printer, + bool moreComing) +{ + check( IsWindow( m_hWnd ) ); + + m_browseList.SetRedraw(FALSE); + + // + // check to make sure if we're the only item in the control...i.e. + // the list size is 1. + // + if (m_browseList.GetCount() > 1) + { + // + // if we're not the only thing in the list, then + // simply remove it from the list + // + m_browseList.DeleteItem( printer->item ); + } + else + { + // + // if we're the only thing in the list, then redisplay + // it with the no rendezvous printers message + // + InitBrowseList(); + } + + if (!moreComing) + { + m_browseList.SetRedraw(TRUE); + m_browseList.Invalidate(); + } +} + + +void +CSecondPage::OnResolvePrinter( + Printer * printer) +{ + DEBUG_UNUSED(printer); + + check( IsWindow( m_hWnd ) ); + + CPrinterSetupWizardSheet * psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + // + // setup the sheet to enable the next button if we've successfully + // resolved + // + psheet->SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT ); + +exit: + + return; +} + + +void CSecondPage::OnTvnSelchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult) +{ + LPNMTREEVIEW pNMTreeView = reinterpret_cast(pNMHDR); + CPrinterSetupWizardSheet * psheet; + int err = 0; + + HTREEITEM item = m_browseList.GetSelectedItem(); + require_quiet( item, exit ); + + psheet = reinterpret_cast(GetParent()); + require_action( psheet, exit, err = kUnknownErr ); + + Printer * printer; + + printer = reinterpret_cast(m_browseList.GetItemData( item ) ); + require_quiet( printer, exit ); + + // + // this call will trigger a resolve. When the resolve is complete, + // our OnResolve will be called. + // + err = psheet->SetSelectedPrinter(printer); + require_noerr( err, exit ); + + // + // setup the sheet to disable the next button until we've successfully + // resolved this printer + // + psheet->SetWizardButtons( PSWIZB_BACK ); + +exit: + + if (err != 0) + { + CString text; + CString caption; + + text.LoadString(IDS_ERROR_SELECTING_PRINTER_TEXT); + caption.LoadString(IDS_ERROR_SELECTING_PRINTER_CAPTION); + + MessageBox(text, caption, MB_OK|MB_ICONEXCLAMATION); + } + + *pResult = 0; +} diff --git a/Clients/PrinterSetupWizard/SecondPage.h b/Clients/PrinterSetupWizard/SecondPage.h new file mode 100644 index 0000000..45c55c9 --- /dev/null +++ b/Clients/PrinterSetupWizard/SecondPage.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1997-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: SecondPage.h,v $ +Revision 1.2 2004/09/13 21:23:42 shersche + Add moreComing argument to OnAddPrinter and OnRemovePrinter callbacks +Bug #: 3796483 + +Revision 1.1 2004/06/18 04:36:57 rpantos +First checked in + + +*/ + +#pragma once + +#include "PrinterSetupWizardSheet.h" +#include "CommonServices.h" +#include "UtilTypes.h" +#include "afxcmn.h" +#include "dns_sd.h" +#include "afxwin.h" +#include + +using namespace PrinterSetupWizard; + +// CSecondPage dialog + +class CSecondPage : public CPropertyPage, public EventHandler +{ + DECLARE_DYNAMIC(CSecondPage) + +public: + CSecondPage(); + virtual ~CSecondPage(); + +// Dialog Data + enum { IDD = IDD_SECOND_PAGE }; + + virtual void + OnAddPrinter( + Printer * printer, + bool moreComing); + + virtual void + OnRemovePrinter( + Printer * printer, + bool moreComing); + + virtual void + OnResolvePrinter( + Printer * printer); + +protected: + + void InitBrowseList(); + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + afx_msg BOOL OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message); + virtual BOOL OnSetActive(); + virtual BOOL OnKillActive(); + + DECLARE_MESSAGE_MAP() + +public: + + HTREEITEM m_emptyListItem; + bool m_selectOkay; + CTreeCtrl m_browseList; + DNSServiceRef m_resolver; + bool m_initialized; + bool m_waiting; + + afx_msg void OnTvnSelchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult); +}; diff --git a/Clients/PrinterSetupWizard/ThirdPage.cpp b/Clients/PrinterSetupWizard/ThirdPage.cpp new file mode 100644 index 0000000..6828f3c --- /dev/null +++ b/Clients/PrinterSetupWizard/ThirdPage.cpp @@ -0,0 +1,1224 @@ +/* + * Copyright (c) 1997-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: ThirdPage.cpp,v $ +Revision 1.10 2004/10/11 22:55:34 shersche + Use the IP port number when deriving the printer port name. +Bug #: 3827624 + +Revision 1.9 2004/06/27 23:08:00 shersche +code cleanup, make sure EnumPrintDrivers returns non-zero value, ignore comments in inf files + +Revision 1.8 2004/06/27 08:06:45 shersche +Parse [Strings] section of inf file + +Revision 1.7 2004/06/26 04:00:05 shersche +fix warnings compiling in debug mode +Submitted by: herscher + +Revision 1.6 2004/06/26 03:19:57 shersche +clean up warning messages + +Submitted by: herscher + +Revision 1.5 2004/06/25 05:06:02 shersche +Trim whitespace from key/value pairs when parsing inf files +Submitted by: herscher + +Revision 1.4 2004/06/25 02:44:13 shersche +Tweaked code to handle Xerox Phaser printer identification +Submitted by: herscher + +Revision 1.3 2004/06/25 02:27:58 shersche +Do a CListCtrl::FindItem() before calling CListCtrl::SetItemState(). +Submitted by: herscher + +Revision 1.2 2004/06/23 18:09:23 shersche +Normalize tag names when parsing inf files. +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#include "stdafx.h" +#include "PrinterSetupWizardApp.h" +#include "PrinterSetupWizardSheet.h" +#include "ThirdPage.h" +#include "StdioFileEx.h" +#include +#include +#include + +// local variable is initialize but not referenced +#pragma warning(disable:4189) + + +// +// This is the printer description file that is shipped +// with Windows +// +#define kNTPrintFile L"inf\\ntprint.inf" + +// +// These are pre-defined names for Generic manufacturer and model +// +#define kGenericManufacturer L"Generic" +#define kGenericModel L"Generic / Text Only" + +// +// states for parsing ntprint.inf +// +enum PrinterParsingState +{ + Looking, + ParsingManufacturers, + ParsingModels, + ParsingStrings +}; + + +// CThirdPage dialog + +IMPLEMENT_DYNAMIC(CThirdPage, CPropertyPage) +CThirdPage::CThirdPage() + : CPropertyPage(CThirdPage::IDD), + m_initialized(false) +{ + m_psp.dwFlags &= ~(PSP_HASHELP); + m_psp.dwFlags |= PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE; + + m_psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_INSTALL_TITLE); + m_psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_INSTALL_SUBTITLE); +} + + +CThirdPage::~CThirdPage() +{ + // + // clean up all the printer manufacturers + // + while (m_manufacturers.size()) + { + Manufacturers::iterator iter = m_manufacturers.begin(); + + while (iter->second->models.size()) + { + Models::iterator it = iter->second->models.begin(); + + Model * model = *it; + + delete model; + + iter->second->models.erase(it); + } + + delete iter->second; + + m_manufacturers.erase(iter); + } +} + + +// ---------------------------------------------------- +// SelectMatch +// +// SelectMatch will do all the UI work associated with +// selected a manufacturer and model of printer. It also +// makes sure the printer object is update with the +// latest settings +// +// ---------------------------------------------------- +void +CThirdPage::SelectMatch(Printer * printer, Manufacturer * manufacturer, Model * model) +{ + LVFINDINFO info; + int nIndex; + + check( printer != NULL ); + check( manufacturer != NULL ); + check( model != NULL ); + + Manufacturers manufacturers; + manufacturers[manufacturer->name] = manufacturer; + + PopulateUI( manufacturers ); + + // + // select the manufacturer + // + info.flags = LVFI_STRING; + info.psz = manufacturer->name; + + nIndex = m_manufacturerListCtrl.FindItem(&info); + + if (nIndex != -1) + { + m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); + m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE); + } + + // + // select the model + // + info.flags = LVFI_STRING; + info.psz = model->name; + + nIndex = m_modelListCtrl.FindItem(&info); + + if (nIndex != -1) + { + m_modelListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); + m_modelListCtrl.EnsureVisible(nIndex, FALSE); + + m_modelListCtrl.SetFocus(); + } + + CopyPrinterSettings( printer, manufacturer, model ); +} + + +// -------------------------------------------------------- +// CopyPrinterSettings +// +// This function makes sure that the printer object has the +// latest settings from the manufacturer and model objects +// -------------------------------------------------------- + +void +CThirdPage::CopyPrinterSettings( Printer * printer, Manufacturer * manufacturer, Model * model ) +{ + printer->manufacturer = manufacturer->name; + printer->model = model->name; + printer->driverInstalled = model->driverInstalled; + printer->infFileName = model->infFileName; + printer->portName.Format(L"IP_%s.%d", static_cast(printer->hostname), printer->portNumber); +} + + +// ------------------------------------------------------ +// LoadPrintDriverDefsFromFile +// +// This function does all the heavy lifting in parsing inf +// files. It is called to parse both ntprint.inf, and driver +// files that might be shipped on a printer's installation +// disk +// +// The inf file is not totally parsed. I only want to determine +// the manufacturer and models that are involved. I leave it +// to printui.dll to actually copy the driver files to the +// right places. +// +// I was aiming to parse as little as I could so as not to +// duplicate the parsing code that is contained in Windows. There +// are no public APIs for parsing inf files. +// +// That part of the inf file that we're interested in has a fairly +// easy format. Tags are strings that are enclosed in brackets. +// We are only interested in [MANUFACTURERS] and models. +// +// The only potentially opaque thing about this function is the +// checkForDuplicateModels flag. The problem here is that ntprint.inf +// doesn't contain duplicate models, and it has hundreds of models +// listed. You wouldn't check for duplicates there. But oftentimes, +// loading different windows print driver files contain multiple +// entries for the same printer. You don't want the UI to display +// the same printer multiple times, so in that case, you would ask +// this function to check for multiple models. + +OSStatus +CThirdPage::LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels ) +{ + PrinterParsingState state = Looking; + Manufacturers::iterator iter = manufacturers.end(); + CStdioFileEx file; + CFileException feError; + CString s; + OSStatus err; + BOOL ok; + + typedef std::map StringMap; + + StringMap strings; + + ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + check ( state == Looking ); + check ( iter == manufacturers.end() ); + + // + // first, parse the file looking for string sections + // + while (file.ReadString(s)) + { + // + // check for comment + // + if (s.Find(';') == 0) + { + continue; + } + + // + // check for tag + // + else if (s.Find('[') == 0) + { + // + // handle any capitalization issues here + // + CString tag = s; + + tag.MakeLower(); + + if (tag == L"[strings]") + { + state = ParsingStrings; + } + else + { + state = Looking; + } + } + else + { + switch (state) + { + case ParsingStrings: + { + int curPos = 0; + + if (s.GetLength() > 0) + { + CString key = s.Tokenize(L"=",curPos); + CString val = s.Tokenize(L"=",curPos); + + // + // get rid of all delimiters + // + val.Remove('"'); + + // + // and store it + // + strings[key] = val; + } + } + break; + } + } + } + + file.Close(); + + ok = file.Open( filename, CFile::modeRead|CFile::typeText, &feError); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + state = Looking; + + check ( iter == manufacturers.end() ); + + while (file.ReadString(s)) + { + // + // check for comment + // + if (s.Find(';') == 0) + { + continue; + } + + // + // check for tag + // + else if (s.Find('[') == 0) + { + // + // handle any capitalization issues here + // + CString tag = s; + + tag.MakeLower(); + + if (tag == L"[manufacturer]") + { + state = ParsingManufacturers; + } + else + { + // remove the leading and trailing delimiters + // + s.Remove('['); + s.Remove(']'); + + // check to see if this is a printer entry + // + iter = manufacturers.find(s); + + if (iter != manufacturers.end()) + { + state = ParsingModels; + } + else + { + state = Looking; + } + } + } + // + // only look at this if the line isn't empty, or + // if it isn't a comment + // + else if ((s.GetLength() > 0) && (s.Find(';') != 0)) + { + switch (state) + { + // + // if we're parsing manufacturers, then we will parse + // an entry of the form key=val, where key is a delimited + // string specifying a manufacturer name, and val is + // a tag that is used later in the file. the key is + // delimited by either '"' (quotes) or '%' (percent sign). + // + // the tag is used further down the file when models are + // declared. this allows multiple manufacturers to exist + // in a single inf file. + // + case ParsingManufacturers: + { + Manufacturer * manufacturer; + int curPos = 0; + + CString key = s.Tokenize(L"=",curPos); + CString val = s.Tokenize(L"=",curPos); + + try + { + manufacturer = new Manufacturer; + } + catch (...) + { + manufacturer = NULL; + } + + require_action( manufacturer, exit, err = kNoMemoryErr ); + + // + // if it's a variable, look it up + // + if (key.Find('%') == 0) + { + StringMap::iterator it; + + key.Remove('%'); + + it = strings.find(key); + + if (it != strings.end()) + { + key = it->second; + } + } + else + { + key.Remove('"'); + } + + val.TrimLeft(); + val.TrimRight(); + + // + // why is there no consistency in inf files? + // + if (val.GetLength() == 0) + { + val = key; + } + + // + // fix the manufacturer name if necessary + // + curPos = 0; + val = val.Tokenize(L",", curPos); + + manufacturer->name = NormalizeManufacturerName( key ); + manufacturer->tag = val; + + manufacturers[val] = manufacturer; + } + break; + + case ParsingModels: + { + check( iter != manufacturers.end() ); + + Model * model; + int curPos = 0; + + CString name = s.Tokenize(L"=",curPos); + CString description = s.Tokenize(L"=",curPos); + + name.Remove('"'); + name.Trim(); + description.Trim(); + + // + // If true, see if we've seen this guy before + // + if (checkForDuplicateModels == true) + { + if ( MatchModel( iter->second, name ) != NULL ) + { + continue; + } + } + + try + { + model = new Model; + } + catch (...) + { + model = NULL; + } + + require_action( model, exit, err = kNoMemoryErr ); + + model->infFileName = filename; + model->name = name; + model->driverInstalled = false; + + iter->second->models.push_back(model); + } + break; + + default: + { + // pay no attention if we are in any other state + } + break; + } + } + } + +exit: + + file.Close(); + + return (err); +} + + +// ------------------------------------------------------- +// LoadPrintDriverDefs +// +// This function is responsible for loading the print driver +// definitions of all print drivers that have been installed +// on this machine. +// ------------------------------------------------------- +OSStatus +CThirdPage::LoadPrintDriverDefs( Manufacturers & manufacturers ) +{ + BYTE * buffer = NULL; + DWORD bytesReceived = 0; + DWORD numPrinters = 0; + OSStatus err = 0; + BOOL ok; + + // + // like a lot of win32 calls, we call this first to get the + // size of the buffer we need. + // + EnumPrinterDrivers(NULL, L"all", 6, NULL, 0, &bytesReceived, &numPrinters); + + if (bytesReceived > 0) + { + try + { + buffer = new BYTE[bytesReceived]; + } + catch (...) + { + buffer = NULL; + } + + require_action( buffer, exit, err = kNoMemoryErr ); + + // + // this call gets the real info + // + ok = EnumPrinterDrivers(NULL, L"all", 6, buffer, bytesReceived, &bytesReceived, &numPrinters); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + DRIVER_INFO_6 * info = (DRIVER_INFO_6*) buffer; + + for (DWORD i = 0; i < numPrinters; i++) + { + Manufacturer * manufacturer; + Model * model; + CString name; + + // + // skip over anything that doesn't have a manufacturer field. This + // fixes a bug that I noticed that occurred after I installed + // ProComm. This program add a print driver with no manufacturer + // that screwed up this wizard. + // + if (info[i].pszMfgName == NULL) + { + continue; + } + + // + // look for manufacturer + // + Manufacturers::iterator iter; + + // + // save the name + // + name = NormalizeManufacturerName( info[i].pszMfgName ); + + iter = manufacturers.find(name); + + if (iter != manufacturers.end()) + { + manufacturer = iter->second; + } + else + { + try + { + manufacturer = new Manufacturer; + } + catch (...) + { + manufacturer = NULL; + } + + require_action( manufacturer, exit, err = kNoMemoryErr ); + + manufacturer->name = name; + + manufacturers[name] = manufacturer; + } + + // + // now look to see if we have already seen this guy. this could + // happen if we have already installed printers that are described + // in ntprint.inf. the extant drivers will show up in EnumPrinterDrivers + // but we have already loaded their info + // + // + if ( MatchModel( manufacturer, ConvertToModelName( info[i].pName ) ) == NULL ) + { + try + { + model = new Model; + } + catch (...) + { + model = NULL; + } + + require_action( model, exit, err = kNoMemoryErr ); + + model->name = info[i].pName; + model->driverInstalled = true; + + manufacturer->models.push_back(model); + } + } + } + +exit: + + if (buffer != NULL) + { + delete [] buffer; + } + + return err; +} + + +// ------------------------------------------------------ +// ConvertToManufacturerName +// +// This function is responsible for tweaking the +// name so that subsequent string operations won't fail because +// of capitalizations/different names for the same manufacturer +// (i.e. Hewlett-Packard/HP/Hewlett Packard) +// +CString +CThirdPage::ConvertToManufacturerName( const CString & name ) +{ + // + // first we're going to convert all the characters to lower + // case + // + CString lower = name; + lower.MakeLower(); + + // + // now we're going to check to see if the string says "hewlett-packard", + // because sometimes they refer to themselves as "hewlett-packard", and + // sometimes they refer to themselves as "hp". + // + if ( lower == L"hewlett-packard") + { + lower = "hp"; + } + + // + // tweak for Xerox Phaser, which doesn't announce itself + // as a xerox + // + else if ( lower.Find( L"phaser", 0 ) != -1 ) + { + lower = "xerox"; + } + + return lower; +} + + +// ------------------------------------------------------ +// ConvertToModelName +// +// This function is responsible for ensuring that subsequent +// string operations don't fail because of differing capitalization +// schemes and the like +// ------------------------------------------------------ + +CString +CThirdPage::ConvertToModelName( const CString & name ) +{ + // + // convert it to lowercase + // + CString lower = name; + lower.MakeLower(); + + return lower; +} + + +// ------------------------------------------------------ +// NormalizeManufacturerName +// +// This function is responsible for tweaking the manufacturer +// name so that there are no aliases for vendors +// +CString +CThirdPage::NormalizeManufacturerName( const CString & name ) +{ + CString normalized = name; + + // + // now we're going to check to see if the string says "hewlett-packard", + // because sometimes they refer to themselves as "hewlett-packard", and + // sometimes they refer to themselves as "hp". + // + if ( normalized == L"Hewlett-Packard") + { + normalized = "HP"; + } + + return normalized; +} + + +// ------------------------------------------------------- +// MatchPrinter +// +// This function is responsible for matching a printer +// to a list of manufacturers and models. It calls +// MatchManufacturer and MatchModel in turn. +// + +OSStatus CThirdPage::MatchPrinter(Manufacturers & manufacturers, Printer * printer) +{ + CString normalizedProductName; + Manufacturer * manufacturer = NULL; + Model * model = NULL; + bool found = false; + CString text; + OSStatus err = kNoErr; + + // + // first look to see if we have a usb_MFG descriptor + // + if (printer->usb_MFG.GetLength() > 0) + { + manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( printer->usb_MFG ) ); + } + + if ( manufacturer == NULL ) + { + printer->product.Remove('('); + printer->product.Remove(')'); + + manufacturer = MatchManufacturer( manufacturers, ConvertToManufacturerName ( printer->product ) ); + } + + // + // if we found the manufacturer, then start looking for the model + // + if ( manufacturer != NULL ) + { + if (printer->usb_MDL.GetLength() > 0) + { + model = MatchModel ( manufacturer, ConvertToModelName ( printer->usb_MDL ) ); + } + + if ( model == NULL ) + { + printer->product.Remove('('); + printer->product.Remove(')'); + + model = MatchModel ( manufacturer, ConvertToModelName ( printer->product ) ); + } + + if ( model != NULL ) + { + SelectMatch(printer, manufacturer, model); + found = true; + } + } + + // + // display a message to the user based on whether we could match + // this printer + // + if (found) + { + text.LoadString(IDS_PRINTER_MATCH_GOOD); + } + else + { + text.LoadString(IDS_PRINTER_MATCH_BAD); + + // + // if there was any crud in this list from before, get rid of it now + // + m_modelListCtrl.DeleteAllItems(); + + // + // select the manufacturer if we found one + // + if (manufacturer != NULL) + { + LVFINDINFO info; + int nIndex; + + // + // select the manufacturer + // + info.flags = LVFI_STRING; + info.psz = manufacturer->name; + + nIndex = m_manufacturerListCtrl.FindItem(&info); + + if (nIndex != -1) + { + m_manufacturerListCtrl.SetItemState(nIndex, LVIS_SELECTED, LVIS_SELECTED); + m_manufacturerListCtrl.EnsureVisible(nIndex, FALSE); + } + } + } + + m_printerSelectionText.SetWindowText(text); + + return err; +} + + +// ------------------------------------------------------ +// MatchManufacturer +// +// This function is responsible for finding a manufacturer +// object from a string name. It does a CString::Find, which +// is like strstr, so it doesn't have to do an exact match +// +// If it can't find a match, NULL is returned +// ------------------------------------------------------ + +Manufacturer* +CThirdPage::MatchManufacturer( Manufacturers & manufacturers, const CString & name) +{ + Manufacturers::iterator iter; + + for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++) + { + // + // we're going to convert all the manufacturer names to lower case, + // so we match the name passed in. + // + CString lower = iter->second->name; + lower.MakeLower(); + + // + // now try and find the lowered string in the name passed in. + // + if (name.Find(lower) == 0) + { + return iter->second; + } + } + + return NULL; +} + + +// ------------------------------------------------------- +// MatchModel +// +// This function is responsible for matching a model from +// a name. It does a CString::Find(), which works like strstr, +// so it doesn't rely on doing an exact string match. +// + +Model* +CThirdPage::MatchModel(Manufacturer * manufacturer, const CString & name) +{ + Models::iterator iter; + + iter = manufacturer->models.begin(); + + for (iter = manufacturer->models.begin(); iter != manufacturer->models.end(); iter++) + { + Model * model = *iter; + + // + // convert the model name to lower case + // + CString lowered = model->name; + lowered.MakeLower(); + + if (lowered.Find( name ) != -1) + { + return model; + } + } + + return NULL; +} + + + +// ----------------------------------------------------------- +// OnInitPage +// +// This function is responsible for doing initialization that +// only occurs once during a run of the wizard +// + +OSStatus CThirdPage::OnInitPage() +{ + static const int bufferSize = 32768; + TCHAR windowsDirectory[bufferSize]; + CString header; + CString ntPrint; + OSStatus err; + BOOL ok; + + + // + // The CTreeCtrl widget automatically sends a selection changed + // message which initially we want to ignore, because the user + // hasn't selected anything + // + // this flag gets reset in the message handler. Every subsequent + // message gets handled. + // + + // + // we have to make sure that we only do this once. Typically, + // we would do this in something like OnInitDialog, but we don't + // have this in Wizards, because the window is a PropertySheet. + // We're considered fully initialized when we receive the first + // selection notice + // + header.LoadString(IDS_MANUFACTURER_HEADING); + m_manufacturerListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 138); + m_manufacturerSelected = NULL; + + header.LoadString(IDS_MODEL_HEADING); + m_modelListCtrl.InsertColumn(0, header, LVCFMT_LEFT, 247); + m_modelSelected = NULL; + + // + // load printers from ntprint.inf + // + ok = GetWindowsDirectory( windowsDirectory, bufferSize ); + err = translate_errno( ok, errno_compat(), kUnknownErr ); + require_noerr( err, exit ); + + ntPrint.Format(L"%s\\%s", windowsDirectory, kNTPrintFile); + err = LoadPrintDriverDefsFromFile( m_manufacturers, ntPrint, false ); + require_noerr(err, exit); + + // + // load printer drivers that have been installed on this machine + // + err = LoadPrintDriverDefs( m_manufacturers ); + require_noerr(err, exit); + +exit: + + return (err); +} + + +void CThirdPage::DoDataExchange(CDataExchange* pDX) +{ + CPropertyPage::DoDataExchange(pDX); + DDX_Control(pDX, IDC_PRINTER_MANUFACTURER, m_manufacturerListCtrl); + DDX_Control(pDX, IDC_PRINTER_MODEL, m_modelListCtrl); + DDX_Control(pDX, IDC_PRINTER_NAME, m_printerName); + DDX_Control(pDX, IDC_DEFAULT_PRINTER, m_defaultPrinterCtrl); + DDX_Control(pDX, IDC_PRINTER_SELECTION_TEXT, m_printerSelectionText); +} + + +// ---------------------------------------------------------- +// OnSetActive +// +// This function is called by MFC after the window has been +// activated. +// + +BOOL +CThirdPage::OnSetActive() +{ + CPrinterSetupWizardSheet * psheet; + Printer * printer; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + if ((m_manufacturerListCtrl.GetFirstSelectedItemPosition() != NULL) && + (m_modelListCtrl.GetFirstSelectedItemPosition() != NULL)) + { + psheet->SetWizardButtons( PSWIZB_BACK|PSWIZB_NEXT ); + } + else + { + psheet->SetWizardButtons( PSWIZB_BACK ); + } + + printer = psheet->GetSelectedPrinter(); + require_quiet( printer, exit ); + + // + // call OnInitPage once + // + if (!m_initialized) + { + OnInitPage(); + m_initialized = true; + } + + // + // update the UI with the printer name + // + m_printerName.SetWindowText(printer->displayName); + + // + // populate the list controls with the manufacturers and models + // from ntprint.inf + // + PopulateUI( m_manufacturers ); + + // + // and try and match the printer + // + MatchPrinter( m_manufacturers, printer ); + +exit: + + return CPropertyPage::OnSetActive(); +} + + +// ------------------------------------------------------- +// PopulateUI +// +// This function is called to populate the list of manufacturers +// +OSStatus +CThirdPage::PopulateUI(Manufacturers & manufacturers) +{ + Manufacturers::iterator iter; + + m_manufacturerListCtrl.DeleteAllItems(); + + for (iter = manufacturers.begin(); iter != manufacturers.end(); iter++) + { + int nIndex; + + Manufacturer * manufacturer = iter->second; + + nIndex = m_manufacturerListCtrl.InsertItem(0, manufacturer->name); + + m_manufacturerListCtrl.SetItemData(nIndex, (DWORD_PTR) manufacturer); + } + + return 0; +} + + +BEGIN_MESSAGE_MAP(CThirdPage, CPropertyPage) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MANUFACTURER, OnLvnItemchangedManufacturer) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_PRINTER_MODEL, OnLvnItemchangedPrinterModel) + ON_BN_CLICKED(IDC_DEFAULT_PRINTER, OnBnClickedDefaultPrinter) + ON_BN_CLICKED(IDC_HAVE_DISK, OnBnClickedHaveDisk) +END_MESSAGE_MAP() + + +// CThirdPage message handlers +void CThirdPage::OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult) +{ + LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR); + + POSITION p = m_manufacturerListCtrl.GetFirstSelectedItemPosition(); + int nSelected = m_manufacturerListCtrl.GetNextSelectedItem(p); + + if (nSelected != -1) + { + m_manufacturerSelected = (Manufacturer*) m_manufacturerListCtrl.GetItemData(nSelected); + + m_modelListCtrl.SetRedraw(FALSE); + + m_modelListCtrl.DeleteAllItems(); + m_modelSelected = NULL; + + Models::iterator iter; + + for (iter = m_manufacturerSelected->models.begin(); iter != m_manufacturerSelected->models.end(); iter++) + { + Model * model = *iter; + + int nItem = m_modelListCtrl.InsertItem(0, model->name); + + m_modelListCtrl.SetItemData(nItem, (DWORD_PTR) model); + } + + m_modelListCtrl.SetRedraw(TRUE); + } + + *pResult = 0; +} + +void CThirdPage::OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult) +{ + LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR); + + CPrinterSetupWizardSheet * psheet; + Printer * printer; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + printer = psheet->GetSelectedPrinter(); + require_quiet( printer, exit ); + + check ( m_manufacturerSelected ); + + POSITION p = m_modelListCtrl.GetFirstSelectedItemPosition(); + int nSelected = m_modelListCtrl.GetNextSelectedItem(p); + + if (nSelected != -1) + { + m_modelSelected = (Model*) m_modelListCtrl.GetItemData(nSelected); + + CopyPrinterSettings( printer, m_manufacturerSelected, m_modelSelected ); + + psheet->SetWizardButtons(PSWIZB_BACK|PSWIZB_NEXT); + } + else + { + psheet->SetWizardButtons(PSWIZB_BACK); + } + +exit: + + *pResult = 0; +} + + +void CThirdPage::OnBnClickedDefaultPrinter() +{ + CPrinterSetupWizardSheet * psheet; + Printer * printer; + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + printer = psheet->GetSelectedPrinter(); + require_quiet( printer, exit ); + + printer->deflt = m_defaultPrinterCtrl.GetState() ? true : false; + +exit: + + return; +} + +void CThirdPage::OnBnClickedHaveDisk() +{ + CPrinterSetupWizardSheet * psheet; + Printer * printer; + + CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, L"Setup Information (*.inf)|*.inf||", this); + + psheet = reinterpret_cast(GetParent()); + require_quiet( psheet, exit ); + + printer = psheet->GetSelectedPrinter(); + require_quiet( printer, exit ); + + if ( dlg.DoModal() == IDOK ) + { + Manufacturers manufacturers; + CString filename = dlg.GetPathName(); + + LoadPrintDriverDefsFromFile( manufacturers, filename, true ); + + PopulateUI( manufacturers ); + + MatchPrinter( manufacturers, printer ); + } + +exit: + + return; +} diff --git a/Clients/PrinterSetupWizard/ThirdPage.h b/Clients/PrinterSetupWizard/ThirdPage.h new file mode 100644 index 0000000..dfdb69d --- /dev/null +++ b/Clients/PrinterSetupWizard/ThirdPage.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1997-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: ThirdPage.h,v $ +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#pragma once +#include "afxcmn.h" +#include "UtilTypes.h" +#include +#include +#include +#include +#include "afxwin.h" + + +// CThirdPage dialog + +class CThirdPage : public CPropertyPage +{ + DECLARE_DYNAMIC(CThirdPage) + +public: + CThirdPage(); + virtual ~CThirdPage(); + +// Dialog Data + enum { IDD = IDD_THIRD_PAGE }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnSetActive(); + + DECLARE_MESSAGE_MAP() + +private: + + typedef std::map Manufacturers; + + // + // LoadPrintDriverDefsFromFile + // + // Parses INF file and populates manufacturers + // + OSStatus LoadPrintDriverDefsFromFile(Manufacturers & manufacturers, const CString & filename, bool checkForDuplicateModels ); + + // + // LoadPrintDriverDefs + // + // Loads extant print driver definitions + // + OSStatus LoadPrintDriverDefs(Manufacturers & manufacturers); + + // + // PopulateUI + // + // Load print driver defs into UI for browsing/selection + // + OSStatus PopulateUI(Manufacturers & manufacturers); + + // + // MatchPrinter + // + // Tries to match printer based on manufacturer and model + // + OSStatus MatchPrinter(Manufacturers & manufacturers, Printer * printer); + + // + // OnInitPage + // + // Called first time page is activated. + OSStatus OnInitPage(); + + // + // these functions will tweak the names so that everything is + // consistent + // + CString ConvertToManufacturerName( const CString & name ); + CString ConvertToModelName( const CString & name ); + CString NormalizeManufacturerName( const CString & name ); + + Manufacturer * MatchManufacturer( Manufacturers & manufacturer, const CString & name ); + Model * MatchModel( Manufacturer * manufacturer, const CString & name ); + void SelectMatch(Printer * printer, Manufacturer * manufacturer, Model * model); + void CopyPrinterSettings(Printer * printer, Manufacturer * manufacturer, Model * model); + + Manufacturers m_manufacturers; + + CListCtrl m_manufacturerListCtrl; + Manufacturer * m_manufacturerSelected; + + CListCtrl m_modelListCtrl; + Model * m_modelSelected; + + bool m_initialized; + +public: + + afx_msg void OnLvnItemchangedManufacturer(NMHDR *pNMHDR, LRESULT *pResult); + CStatic m_printerName; + afx_msg void OnLvnItemchangedPrinterModel(NMHDR *pNMHDR, LRESULT *pResult); + afx_msg void OnBnClickedDefaultPrinter(); +private: + CButton m_defaultPrinterCtrl; +public: + CStatic m_printerSelectionText; + afx_msg void OnBnClickedHaveDisk(); +}; diff --git a/Clients/PrinterSetupWizard/UtilTypes.h b/Clients/PrinterSetupWizard/UtilTypes.h new file mode 100644 index 0000000..f702ebc --- /dev/null +++ b/Clients/PrinterSetupWizard/UtilTypes.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1997-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: UtilTypes.h,v $ +Revision 1.4 2004/09/13 21:22:44 shersche + Add moreComing argument to OnAddPrinter and OnRemovePrinter callbacks +Bug #: 3796483 + +Revision 1.3 2004/06/26 23:27:12 shersche +support for installing multiple printers of the same name + +Revision 1.2 2004/06/25 02:25:59 shersche +Remove item field from manufacturer and model structures +Submitted by: herscher + +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#pragma once + +#include +#include + +class CPrinterSetupWizardSheet; + +namespace PrinterSetupWizard +{ + struct Printer; + struct Manufacturer; + struct Model; + + typedef std::list TextRecord; + typedef std::list Models; + + struct Printer + { + DNSServiceRef serviceRef; + CPrinterSetupWizardSheet * window; + HTREEITEM item; + DWORD refs; + + // + // these are from the browse reply + // + uint32_t ifi; + std::string name; + CString displayName; + CString actualName; + std::string type; + std::string domain; + + // + // these are from the resolve + // + CString hostname; + unsigned short portNumber; + CString usb_MFG; + CString usb_MDL; + CString description; + CString product; + + // + // these are derived from the printer matching code + // + // if driverInstalled is false, then infFileName should + // have an absolute path to the printers inf file. this + // is used to install the printer from printui.dll + // + // if driverInstalled is true, then model is the name + // of the driver to use in AddPrinter + // + bool driverInstalled; + CString infFileName; + CString manufacturer; + CString model; + CString portName; + bool deflt; + + // + // state + // + bool installed; + }; + + + struct Manufacturer + { + CString name; + CString tag; + Models models; + }; + + + struct Model + { + bool driverInstalled; + CString infFileName; + CString name; + }; + + + class EventHandler + { + public: + + virtual void + OnAddPrinter( + Printer * printer, + bool moreComing) = 0; + + virtual void + OnRemovePrinter( + Printer * printer, + bool moreComing) = 0; + + virtual void + OnResolvePrinter( + Printer * printer) = 0; + }; +} diff --git a/Clients/PrinterSetupWizard/res/Info.ico b/Clients/PrinterSetupWizard/res/Info.ico new file mode 100644 index 0000000000000000000000000000000000000000..1a5321892cd0a0ba55a7a9a828242064d9644940 GIT binary patch literal 23558 zcmeHPXnsKKtM>~ z5+X|i34}c?F(8DUK*+r$Au9<%Anc2f1rT!H_f>b@+X+F?=bJY_C_J~jtEeT64 z&Z%e^wTy;F%a)CCt!wPBZ5Uq}hS9jO`P{0OVf3qG828?5sB-bw7)E4W1LdvfhKBL@ zbu4c_2h}r-l4iPmOC$a-hSA|xT^`RUSN@DH-_i&IukSh;Mk~k!OQS7_I8*|bnp3D@ z;6EcY)V$yqx1dlXB_#zfMEz%o@D7B3%;L`_rDq(DUbce0g9nF)edNvP9UP2|aGgO3tuaa z4JgWb4(@Ll?SoNZs0alHg8`lg2M<>KgZWHw!NFiS7#BcPV1q|X1+(>Y*iZoUJ%^tH zL#w>vPr!r26+b1P04NXg6+U1itl;iu^9VIuYaj?u3UPS2Qoysm=V5Gbed}4m!%-cH zErm)>spjLkfmt9t9MV*Jb~NC~DOFo#^bQ|=%Bz0n)O9%8sy7$?AtyQegh)yqWfGN! zG4!lqJaNh}ZaHok*OH2Xfs|qxbuNWimc+W`Ln2RQa-p?EiW?}g{GpvU2DJofI}gQu z%_$T{jW?mNZ(jHhH^0u#>-PSrEh^WAn!|AOY&aA@%rKyF=$XAi)9l_LIBYa*u788z z;Gr-LTnC2>uY=L|;bHw%uMI|zp%Ro&KDdI90b9W%1Ox|zy-rl_@oNpExPf7$VEhQJ zWs-icuR5P)SoYK{{7%jB;9eB!4&wNv(iP@K{emA}sBbzA6U2$ci>IhUhei zNdwC?R8K?EG-RV>8uCFdkOSloLI&_4ALIf#K>i>U0v_apTp$O?AB2j)gM5$+3Jje&RKn{>U2te=(`5+g_0rCgo0eFxPa)BHm ze-H`)5As1SkOSloLM7lqK8XJuAb$`T0T0lDTp$O?9|Y#WgM5$+b{o5h-9gpk0r_YiRY-MFHB-FBPNqzU9Ac?Z`2(wovsk2@h&r3AO9vhmY(w@%s^o$6RruKbH7xh z)RPb!t;z-l1O}=)37!j9s7z3NLoj`WggBjn0Z0^+P$R0+O1B9kFrea8U?3}?hLYi~ zslrKV;pKs^Le?1eoek`Q9e`8msaH z%2oOD3iags@iw_K!4Ah6XqGSY^A%Nh(OQ6KMwRl;027eG{<5X$86Hzbmfd(?=`OZm zSMOroHJ1;xy7)rnZOXS~m@2jRFJHvQn*3v9tYY2^Ocbe1S)4c18KcYFn;RGaL)KkT zwZ+jjl@Dm`7Z~a^jT$y#Pu}uX@W2QHLn>4Ys&#=EkG5r#S$Vl%sM!T&Hv7ao-Cnmf zCTQ6Ktxf%)#X5`EXrKP=4dy4-+U((~{JrHpRqY3?tP15-Ovq~wrS2FA;OJ2`Ih`C- zn5RCQ7`OgnJZwPKvMhsy3T8gxTjnOB47ytRKxfFQQ>V~(-tyX#M{dq}VvM@=X)PCmhhBwLR(b zWz6po;Qe^b@>1z>R;}P?*>pLWbO>;Is*Z=yQHZmufYz$LWvYB&NcG}WYHWZVQ0vwh z4_Uf(0A>sehW%1GClrR@O)Smw0nX4lH5ErUVASoPxSC~E`3@LqwH?^GtNT(;GEg9~ zu$j<$LiL%VYNg6^m`_7#eNJa+?2Lr-^is7ro=QC)w>e+CJ-#9Nykh@d8G0SW>#1AR z7Kj^Lt{b@-<&~RLUQ@8C^1uGA#I8^64QrfM&D9z9gIJd(c$Y|8sM=a!2Ng98^)e5}@G*{PTHadlP|6hG_Lg_57O-(v zQX3WfDx3&)`%p%01ibH|8KokhfU56X`-M8mK*O(I--!i25o-Nd@7><^In4!qnT8jw z@Y&jDZqdA}akG7;ScU7=EmyNxee)5`j4>BnSk^P&Oo3QFn~P^>wADeC(fTE>YS(D8 zGt~!-w)#w_EGZ4RXLbQ~- zZle*1fspG#H-K>R!nhgK!lLF@9^bJH%d$>=5PAHk5@#36s|O1qcGfw>wTanLG3{=fI5M? zfL^z#vz5npEW@&_L$?9jL0*h%llT7xex9IVUtbBLJT6Qhu2akCdTqnngKue4fBZ9d zUbCXh->=);zulkmCcbg!(HRcE(naszdxjM2xT}$k8h>s-^YPB3|p7w9e! zc~b`a*gVW_Y{S_ z`M}`k!>ecSH z&ClH3G?6^W3p`&XZ_2P_Q6^;*!*5NjOPl2P2CtdXrfzNH^`=+V{rtY>SHy?>`_`f; z$KQ{peEFQL9Mwfuj_och$2(-hln-Ugj1ci8gi2mwnB*@SCix3KlkE6`k~yufc%pjB zrV$-vN}mUhg*<;-5qXg(c~gcZi!v$uxAp_;vM;Ik`y1A))$LE$)C=$PMJ&wEJR@ZR#n|NqzJSl@k_~@Geojy^q0+JUXclJKX8z|DdUQIwceymOExhO%dbh)JNu90!=JBi zP&??(o30$&!T-kOsJ@RKTr#Xikf%buP#Khqq1c1)aSw`SYODIH# znD~Y4SoFDSN6yR-BqRE59J6@f@M{k@UrU*kZDAmmOZgUvU5_(uu4+)b=bcR(PK^8D z(Gx4eUpkdJtq=MlgR{SGy}a zd0h7IE|7wZ4YFy`Bv~E%r7WM&Up(<4vONKq=c648`w+!5C+-8;Jn^lwE5lztP1$$c z*k~lN5EJNP-vZ~4KY&`?cy-;O|Gu~BlE_|vJD4%$ZP_0CuH?@85cW#^W*=nE8Z6tF zek<8)=T<}Qd;ICh0Xd;)zwFLUlhnjXGXI-*Bz5{A*%t4V+`LL zkWA?1fAHV;-msWhhzUeHBE}1)YtT7<7{{IqYTInvgx(MCPmkyArt^smnca9v~nIR?lTV?g6ezIlShmtkR zw8cF1sp3igvj!+x?g_8U#Gr@w6ALi`8|OIA)zuSiir+`mdUa|&ef^bn2SmJe-~L4( zw?CN?^_FDMd{1)X(1v)l0e#?Y17v2-{#drghsxQX&&auRXRDzX*|#fS(vxFk$+-8V za9y12*|tIs?c65rdBY_GW9oMFp{aN11A0;X75@JCo_oonq0f~@^tf+7G1Uh)VzjW@ zzXRUW_FG`wdcDn`u87(g{+v+v+o!*S@#bAy8_)+<8>p|0nS*4@)B$q3qD-}+dIM{vEJ@)v#v%oc9TW{5jyWhxb&*5#8-jK}b9+EZ9A=&H$ z-3IWs+fp|0XT=SWoK-Vm7a29;-&>d^o0DhBstJ8y^U&>VeE$oEq8%aU*-W;=j{c^; zAUA7DFUgqPO^E3UU?WCi1rf7ObR;#w+&$#=N1Ok!?u#eU?zd34r({jV*fTxI*9P`) zH*2axGSP;0!=4giBSvBc*-w#jT)m;~U#^}rq~o32Q%C+Ay6-Mox((=qEZC81hwcY_ z6C9UvX3|#sOB&Wf{63Ez`0>0pRGvGBZ{uf~GorVwi0mV2Q{S^NTeeO z?36pTm(t_rQP0cAFSW`hMq&kVp3;f;H@$M-bt{K=ZBw*)bO+UE-Zr2QsE4d+_!exo z#P5sqWoFEKl0LPsLudc_upkwlD45j==w>uMNtMpa*43WxBRqsL0qBLXF3Qdh}{dPB8?_4z%J zm+DWp12VQu>M83Yy32|&Z^)X6w`A=Ehpcn;k)O|;mUCy%p9@Ng^JL3nmn;nLES@Pr zk^w!Nm~n-^P29a;?>&_)B{#a8!r+eVBnt;WUPg?>3L<8m6#f`T>l4d9e&Uo{;qPK= z19(_%Fu!TDFH&7^N$S-8vMFw;EF0NXmXGcxE5Gd_NA?y-`3Z9^s&&un$>WD5V`ZEy z9Mx0OqTWUu%`?56l;u^*tLO%u9F`s~jn>xbzwXV|NFUW%akO%N7^LIAAw1!#=@NeRm4?8$!9;TwN3f>NMr^r!@ZR z_OHgDX}ww>UpC~iiu7?E#e?$pHt4>{RM`0~^pp(rKiBbvKe%Ln!B)vinI)@dd?_=- zVH+v)<;>|*cIfK<$i7`t;9etZ#$!x@4Q-DJ!dg??J8Z_LbK1Z**K10T>EmA#U^_*O z&~^3kM~(mEy4YI%Qe(GEi^iH|38|rcE zw=c?qLH{TvM&PeL{;T;n=bMX7ho@hfJU?*s3>NpTEt$@vEmKecX${|03#k zG_hlzRI;$<%AZFXf;}tgW9(l^*hhi#@Vql+iX6&aC*{WvREICe_vT6QmPENKD-BkKj&qRr_{T$17O$~)=q+7xZO>Rz+Qd*sn-A3Usbo~s}J0`aLv$vNZOd^ z(RYDr94o*cd&i>B;4`rN28)MD;bQne7Jp^Z5?sAR|BmVBDO?;T1<4WEV@61NVuUQ4 zI9L+LeIWBj_fhKz`Xe&uVE+z($_~ZP^s5xYC$R%-YmQ5_-K}F^kTpXe5n>}o;OAb& zzW&FfQ42o2Cmz22nsJeDwl3L%Z-8T0c5ENh=d?6jiWGe9Ty`uSj`HBS6omHE$Hn!W z2XoMtZ=MrkdieHhza>UuwTQUwsoAa9HyqTzUGwzKU;h*1z?;f0i@*Oyijy^s#{C#6 zUT&wccm}Qb)<=8>ZB6)6lB0)6+&3S{l6aRan-wKV3DGib!~mH;{v%ltjXl8P5!law zKY1pTKj|CwZDmAtlg%TZ5n=+ifdI>~|3azW74>RA`R6}2_+)M9Blw0K3d8Qzlfft6Yog`H)stnz?BSBLafuw*TU_N^pkF9&>l#Ul86{bZ$I6c76TEzP zua1JeD5Z;S@bj$=eN>2v*Z|zV@2cL8I`C(=!XEgA5wG1@wD9v+juyiIx@Y}VMSIsz zladY7FF-NK_tD<3cGg8%H7qlQ&gbAadf^A=!gAWG|1BqP5d4p5RY8pu9PsN(3+v z3o!v3*O|olONsAmUwcKJm!I-$8WrJq=*Qh_r-JuvIk;`{1!##J%&3`)HqDbGyRvHZ zO_akoab#by>`I+0C0iGId6z&IWm5K&eoZG43o(H%hF!O-7W_qi&Hr%4JJ0{^rnuCY zAr-kvs~5HindDW;k{DHeHe&|*V1DL<_w3o*z@9<$ya;ju)kZHv4EN?$4tw$lD^vNGN->c&i-yF#~gOHty5q zXc6)YO+MsF-josWV9Pklq-;?2dFt0TqgMU8wH~~dd_e!@FNKYHZE?hHAxXS*E7{SnkXl(ihTK3!Hug&ZYs%DDNu zMnRMbpw!>5v6&0Ctv1K5Ya7-H=<&oYD85J z$2C(Fo?`_YhD~znxZpn^@ob;?|8I+)vD=Yf49?;+>Fr1>hfEEIOlVJ&2fc(8t#``Sj2ng@g2+j z)^!c4R!N*MxUZp4nzHT#JqdaN)Ct6x4T<;6<2#mNS=M1)^7t)@^DO;mGMST zERjA(q|XrP^Fy|?L##_4zjn^1{-{fJu124!@t&u#&eC|#(O73_^!XWmb_VMO>ZXmw z>r(rK<i5KL4T5e&}-_wlg2(eWAXfov^Rk=(8Q(a~<$K@k~ce=Q+4v=lL3* z;n3$d^w|yitQd#VXEw;&l2MaQ(pJ^^27R`H{zvm%1I_~=9$wRV2J;+)d4@rsU+|t? zu+A;$GYhu!3N`r(u7SO%^9kOw305pwpGm0cJOa-l=raiV{DJrE0sN0T_RTW~`n&WZbbMaN=5;KUJcvQZf$7*gE{Ob__b6ZkNw{9Yfdgb8qb2@*S%A78M(m9bGcF0=y$^#G6Sm^P)nmar2JDOfB6h6e zr?z-CV`1RSc&{zitL3X0sg92_USr1Ns0YOVteB{dht?DeWel`R{1Y(@9ryIc-@upg zPFt)~%U3Z@Z+sJeeidW1;+i_9spFYCmTB(?#xQmK(igi#n^hdjjI}a;z<8xCR_T?` z7@04=XU15)aXl-hr{j4#mS>mGdmX>C$L`D+m=&`_{DppJTdYpYS1~#rpHuNAl+|%L z9h1}XI30_tE?>vre6crQ+|7)+bymK2OTOLTdIRG>-uM*aD=J0=AG3-}SurUckJ7QI z>TxI?f1-a{#h$!zCo|@x<4v|$lWu<##&3M_B{R0h0I@BX8`8 z@e1_6H)dqTi)^tXEx!@t7dpP8V=KP6iWO6_;wd_oQaz5M<0m?HqT?psn28lHvBgTX z{5p(3==ccjjC*42 zeXfvpvGu10qLSS+I*&(|D{}OYrmrQDbhE1F<&Cf zXy2js8$#HH*1Q+Pq5Oi{C#d~_2X?6nrb1La~SbHKgnDS`1!vS-{Vs{-2?g zrT*Wf)_~E3eADZZlIQNZaV7pvqaP|azW@XOc6a?T$vyFnN;l3}ZCyE6)-IYN ztEPuaF81O3l7`_-N{`BRcQs8yed4jFR$U%xv9UOzKVr~0rSAM3fn=?kz zvCk}C{JHF!^PzNkw8chLriz&A-Fai9i94qE#JP@v*q8K|yrl7R&WfAm;BO7`*3Odb zrDG*$)*vaE8!S6w9Kt%R3xzx|`JIOj6vW~DEA~RUas6<&*x-)+%U<&dCXj# z3+Ea*8)ulZCj<)XHodz3XZcaDs&f}Sf0}`{HRY|G_^zBhQlfa|r7n~8fDn$H-@$Jj^uH&X+fmqY+j-2A3IcnJz{^!!a84_E5rIR zf6^<$y6Eq2nPZ-n44ge9cCInFPTToIB;sDVs{T(0cS-hK{M`cAdDwSlOnOZ+$37>l z3mx7b(fzJn1qlOihGL}b#5w7mJZFKk7hJ2PE&NuJXO5BO(H~3B!Y^f4@+jGn@S#M! z)n*s#f{a_QZ}4eJ(pR#73u4fSx9??KePfL5T;-C&^yR{nk^AA-*q%ID4yMhLlB6(U z9oDtdH60(ib>2@I3o!n!MQrg0Ih?&5G2&%%bf+21K69!}4lz!%ZJw|!>Q&!sH-7Mr zrjfae$Da9VXR4I#%a-y31yZ&@Ps$D!AfA?na}#6Fung*6$YWRwv5`lfyz3@c*xT(r ziKF`-NF3AmK-k;Qc%JmT(ZzSDd$DiOYSN`$s{g-sf2md|B$HAw+ZV$J%m2U8{{YU% BmIMF* literal 0 HcmV?d00001 diff --git a/Clients/PrinterSetupWizard/res/NetworkPrinter.ico b/Clients/PrinterSetupWizard/res/NetworkPrinter.ico new file mode 100644 index 0000000000000000000000000000000000000000..22130b3de3ee242e78b347bc07d177cf3cd44a10 GIT binary patch literal 77214 zcmeI52Y^&Xw)YzoZc@xyKon3E!-!cy$vKbYC>Az%6%{b-nsr^$^z_{N{=d`rG?%6a!FAuK&TBYRbt~UGp-!EuTa`*xOI1(Rsvd{D6pQEr^wNi@RHg(^&sZ{Y6#Aly}ZJkO*KToBmP2;&%s{Xds zQYDJZrmvN{PyVZSuNJ4Tl^V8HDs|L>)l&6Uu8K@G5l}C`Y4&%+KOqDQtQNvF!3#-f z972ZhEkZb7P^^S%UGA6g?{bQ&KIx=XYRZ%lrg)0DOv>E=Qq-$(+)OIfcx@_G(?QQQ zr+FdY!6k1D)hN3VM5)wN2l`80X?^DX)kM#Qe%@c+*#2tt_USlfqDRKI$cAnIwc_*O zg)LL5<$_K9Ew8Th30NP$A{u@|K<)?11+;aEpj1#IC=$@$VTr;8rGgSckw6Jkp;Y05 zQbCEJNTAHAP@-@_sh~tqBv65=P^54{sh~tqBv2(O{w)!d3Q7b;0#&T4m#CU0DsqXk zFHzDZ3RcP`3Kx_LN(4m$1*km=7nBN01VsWRQhO9GC>4|liUi82_9$FXDku>Y2~?PB zR=EC^3Q7b;0#%^)C`3>yC=nD1RH@pda6ze{L{KD9LlmWOL8+ibP$bZU+M{qmsh~tq zBv1miN8y4}L5ZM9piF9y!Ug;*5flkjklLfx2}%Vef+B$mS9=sLC>4|liUg`q?NPX( zR8S%)5~vZ%qHsZ}phQq4kVoxNxS&)}A}A6lQteT=wM60iS0qpdwMRWAC>4|liUcY| z?NPX(R8S%)5~yIcN8y4}L5ZM9pvu%9g$qgrC4wS>8la*SE+`e02#N&yqxLA=dPU)K z`KJiAN7V=*OGprMQ)0D8;R475QUL8L091lDPy{;YdOavyuSXZsU33lIz$^25yb8^x zi8P0*sfHrSPEvwXsV@cZ3Z4}_Ah=X;j-b1snP5M`4&QgO)R$j=nR@r#cT>+k`)ulg z2OdaWdg-O9bIv&@)xCT7RI_HyQv2<1u0T0mq^@t@4< z4Swlle7Tji-k;(`{L1O6RD)HkS5Zg<{wQT}vV!#L)aq4=StTdMztpO=71;tR_)TAJ zVQ~eDR(^{JpH5#>ytb@tO-hBYTBSPdzv9cvipz@A%cJ!4>C?;8Yg4OKEdTAN_;e;y zo|#?|O;0ODnWQrj6oI4WrWKJcQ#|?0%2fv?@<+e2;_1cdvUFM5Ls2>9q~|J;{nwxJ za``jLmY!Z#QK9%uc}2A0j|Az=-11C$d3tVC5&v7~u?A!+D6f3_+)QS!{fnKsb5*+9 zr+8kYoKn{RQN7BKlz|qSgI9k0M|-EIABvQhr2OZNdFkvwy+Wp#_gBdDXl}*ab-#+a z6;Wml9b6Hq6N=ZYtx&oARI0g|in3IL1`XCc6cvN*)zgdDu3fW6Aq1{jvvzHIje30b znu=h}SdW+t?<+HDS{Q}7lsY}izQDS_737;S$aNUce-M+oIKEAdO^^;jlL*;(G9AIg%|OGl&E6Tb$Y|A(bl#d-?5iU0mjPtiE@ ze|pOQ=_yrSPtlNS;|o%spGJ`EuK`aL(OSt^M(@<>iYR2tH0FDJxyD(IhO4vV&#L0- zWsJLw5B#Evbo%_c8e?rJVE~?9rUBQ+F&oh|&}10kZ6sx=Rr*YWR9Qx2pT^4abZXW4 z71Pt@Dj zfKvQ@!;UJ`VW))O!*HpScJL0oJN=;9d`i|G>!XC#Kp{W!pK$HC$0AW(kKlZJx%9=Z zf;|L#3icB0E!gLKfPGa)KGakgQ0u<{wN)m-BrgYgNB7rBgp-bT@UFc3j!FkD{7m39 zP)+`y$`79By`b}7>0Ni0ySH5W1k=?ag2Mzy2;v!b>PY#&4``%vjuIT5fMewUzTsHC z&vAm|RW@)!B|zM90@}qpA1HuEb{8O{kO%Y?efDFg0ouQn`ed7(cG~HevgPa{sDI#r z2Od_ZPMs6?*=L{Td+xbshuwGIz1wcP?bbukOVE4QU3cxX%Pzb0k=yq_fPTu`U*!M; z1OpQ=NdE5yRL5YIHF)QpcRo!pL{JjLP`QPmg!H_Z-n)3Oz4mHft5&Th2OMy~(T5&- zXoC|@IAK4*F8lAlKjQ)N^2bU8Tg2_>J+{+3)tNPG*2w$ryYH4e?zrRbn{U4P!RxNO z?y(s&W;}J(RaZTG#T8dPCs=g(<(I#3*=3h42IM!_pEuKeQRxk24|~z!l1namQSg$( z#TQ@v()R(Br*eJ+Ty)VzFJE}!g}==Qh5wd#+M;(`bmf&-K09;f%=tImaKj_F-g@ht zyYIgHHrelPxaF2xPV3mQBV!>GWN3iC^xuzx9*pnR;BCeG=s9~fZQAtMg$oxx_OE~a zD_Xg7W%&2M{~d6@`R1FTu<-TQUmM_0Kt#N!Nv8Ak-^ERopRDUY@^7Rak>Y?t?`QAE z`>8+dUFny_ix)r8p+kp*uD{2QJMMUF zrw$$Fj6ZYCzaDwu!E)J2^4_24Y*^d{N}^}}Cv5H>mzQ~?kKZK^1$sVrH(E&|y{Geh z?s|A(H`1%Fdr|Vtd-&n>8K)tloBQm2E?wY*N%KFp8-Suqw&x98WD-@ngm-KttmAT2ZNq=SO z3)TJn!i8b_$VNlkG~T5T;Rt*1GE+V@3YVDd$bb%U+mkXXBabjw8^&| zwngjSN4SiAUwrX}>0z!X;6fixBc$_Z;yj)AFY#K_%%vx-*1>`nay^OF>E-1D5A6LplES_=Eo;tFZCy zRcGItZHEpT{D&T0yH)h+(ZlrJ+`Ac~1wbv}o+i$;Z;e`G3pZ^SBef5>)BQMV%eDFc|;~)PR zh=23VH^b7UOUC+#}4_y`g zZ}<9j>$Vv_bm$VjOVqk$%kYVC;_vF6!ZWaT?OMYf{r>5vpN8ejmmAKY3&Ox9Kf(cI z1!2$%&)}Z<6MYYmk9imL^YOm~3GZs=!lr9BXa zS8z=~@%-6mpBccb;1xWBd+K_&qp79WNCO#jybs(%=6G*ez6<9FFF?Qhba>#hBHTvda+@{hTtDw zDun+ZsxCmgn6vKKrBkQokIkF+`ZZTw6%Ib=pzxa)U$pnlAGa+~f9Oy62O0YE%P(8s z!-L3O@CLuX^wLY=MU?}8(&vl;o(4XIR-SwAx!l-69C8{L+(KIb@)}+z4|VJusi&R_vT=up^B%0 z{d+&byYM9Z1%E>)@H}(@p6Gl0#KGHyQ5O6UPCPH^oL32lCWxba!l5PdcsPD!0QC}w ztiVqm;_xFUpbhFI-06U}Q!X-wx@a5jrtrVNx4SQi?!fw8z%)Oq-BHw9DUvytDy*6TJ~S*qk5z@YG|EhtmcPiu4|l%3d&K^5lA3 zR;!l8e?HQ`FT8oVUS870zqw#@z#ct%G!$;0lAMgd|5aC9VFMm`rEeYg?!r^_Be(8U*bI~e)qfI-QBxq&ziyoYe0qiA04YucrwiK2R@TDq(}QX&Qmds zuwGAo=A77_*sH`o9riM94(y`zdmMDoL5FMXzfZi9(Oe+#Zv2qnK0YS<44DjHgKP9a zlxileG`_x{f^NKvKAd3o<#?PMk@PJm)GZRe%i{y^V21tze}D({z!fm z5rvsoUKK`;7!ir?%QXhg)_#fo)sO6#*_^&lre*z=H6-Q`;ML|4F|PHP`?>spZ^^@c zlsW?JkB17-Lk`~zVBXS5`y86zdFP$?Ngifo&ko(Xbg|b)t^(d~?z&yg`z9$viObKI zlpn4qGOi?lUbef$74j42kj%d^e=>ct%#Gzq#(AFDjV_!zEsQ(stVrWX`91gCbG!DC zoG4r|2gvn((!UXWfG_ZNpwfd!@VTA*$PB3zCg0`F{g4gN2RvVEtF5*=e#oT0QMfA+JVopa%Z7e1{qwH*6Ti)PLAs*w9Sd#gX=x}FH$I#Sw9nN9OnYCg5VY@_@Gi!eeLQ z^jHCV4%vSPp84fuzx<5(+x+4ezu2>W{raaKcGzKe$oBA@IHfcMe6 z`1!n#c=(;TWID6Irs_N zjGuJ`c}5LCJ-Y0YOKi?_`st^CSi5%Z|4|>lq5X2NYfl*c`35eau#Lg%ihot<{vf<8 z)ZF`a$>JWuGy6@E@kx6UbO8VVOndU`YwyV%;cT@abH*8GWJZh_QK7a}3>!8q0`&c8 z#n7QcadR+a$dJh4w9`(D1`Zq;4Hz(>qHEW#k>;@0UnQrVW^+K^lYT1nLx;nrf^Lmo zhu-dX_GQa7{%gL)9Duo>#uU^0NQ<6`zQ8<=XK=tgZ_%PfHqXPh>v|%1M$g10jQJXP zWUT?+g0z%H`OGP(mpKQtM46P2zCk+Xk))xG%ng`tppQ`g=KkoHAp-`6>#n_){))zp z(;86w_R*=Qo*MP+*)!_fx37V0LQxWW_wH@axTFJ!SNe*6{rUkJeG9tkkV6i+?^nP2 z)q%n@_GIv#`-%5?-c~$cG-%MEm(M@{{2=)grcRw|01hWlo*X7jm=N#-_|HA}Tyyok zsrhl$?l5-j*l^~VX9m@6@$|)6XPp)H-h1yH?|J_uX#w7X2cZM{n;-Kk=49Aq&Ce}@mvu98H^k`GKe38_%h%J zP9HuZ3?Dw+!ijS@W8?^nS2+>+PdxF&m$jdOJ=NepDf^)V&?AZG<@~dSBLY?J1=O@qrD$ARij1?@l;~;s>;M*Wmv9@6WmJr_Bz8&6zXD=tp~gt-ZYWHP>7dqz_sj zC;K7kr=O6c%=_RY=KA=V+aYU_{{Uk<{Yo4(0S#aS#C8O(kkjA`TvHyn#x8@Lh92-U z&!i6IH)(Jwk1>J#)J;Cd0@6|sV+1r;k%__w?|*1zkro;Ou>X-3KW!urV-mFGKYw3$ zJ$#jD1s(Z`C!R1}0=$Ahcy9LW*(Mimzx{UO$=Tw|TO=oLpDp*6TMS5ZlkBTE-*ij3 z^|sleRD28nY93+y+rNMRKWdLTdLa7W2K+Dj*tT1@Zs$w3e2#o){6UT*hsKN;ad{p;e!PtX(i4L80OPT{?z$^TA2s~&&b)tq9}o^M;UDxV`kKCkkLge1T+f2% z@dNzeO9$7%NaMO1`Mn(Y9iV>ldwt|fK9k?|Mf{Y{Ge61${25+H?j+Mt9(Csb3cWjP zUy=QWh(3C0!yx}J?CjZ=`2Dp4cmXja6 z;fD`-#*dB(FH;uf!uPoRkQ>MV{mw`kEKv}xToG;h%?;3vLyn^uAN zB!CCuqeAgF9)JAt*!Ge?qXFstyD81@z(3;-ybOpbZCT1Aq=X zckXQc#`=nM45RPFYp>@8SM)i08@SHtVd_`<9UQ=`$N~D7XVUWwuYo7>;)foP@BHwC zQ}6}O$xHe0CUSshm(h$F;1?PsJ>w4LQjfQXI>0q;f+i>*+M*rc7n%f;Ki-XBW%z$X z^M(_TKi=ww=GwMx8`_F@PE{Xj9%Uf;9Hh(Vo=+|A5eQo^zFTqV+O3O)69-QcALHW1 zcJ12n&87cx{4YMODL#G;o$~Cn&olJ#=}(@k0aXn9u?82;B3G3r)DJz(sy_ z>eMMW&r+q~-h1y&cFP8R;_{aWbTjxBd?HtgqyLb*&;htY{vzW^2QG;t9eKbH>F@)j z^Zq9dGMoJ5p?vDY1t=RA-14JrPfs}c$>;TXK4`$xk%#Bx&+AWy;eSqRizgg+oY5t7 zNcbK6fwx}0dgTDQ%Gdx9$N1V`{frC-`u7_U1`Zlv;Vz?bNh8{MP2)V@7Uln{Nq!up zG;f2;c>IoB2ISK~ZjO;1D-8e81@z!F0WB~d!T;!Q$YoV(x@z*>^ZNrnh3DX1WHx;P z?~xyUivEVT;6dWyLwFDQf*i*W@4}bt!=N0}kQaFYp7_Db)I%6R8?gmYzsqmJ$q(;R zKYU3TV+Zw+4xJPEL4MlBGwTzCk;mmq@|_F$@4oZS(D=j?P3AC`NFLjm3Lnvr;1JiJ z2g^THaQe{Uxp4X%I8Awmo}TrC7s8+ujqgh}2Jp>oj_;iigr_3m|9$w}^*iuBYV^o( z=IAlGF<|5wqb$tjz=$&>2YlWsKXSq81DOE+(aq@(*&>Zbk-L=vJWHSBB7c#+@Z~be zYy9AkK84@?8Tz1n^1yS16A!M)OI%;|k2VCZS#PfW?^y#MmQ3vZ-;2(UmW=21b z9C3Om88$qeE?j!w4;PNW?NdM8tNgxcq zhL_+Y#$m?`yb9kD4v)g)@E<%$dia~P#Cu-oh;+=+NC!XS_cGp6STZeTpnG{4l#iV7 zeI3+G`)C*C5lfk6e~6@K4bd-==TC{~Bvr2>)M@&S}Z(<{4V> z=cJ4M$#|Z&LL1Pc=)>A8KjYZEdGm}fWPc3$=Gk}^esvj&-+?~G4okn$?;g(H8)Q6m zfP5tldOWAnY1#~3%AN&Gmj*;v5Ju7Qp1*c=> zHyn=>uFoEa-WJA>J3CCAFv)ay^my#FxZqFv!RM0yEjD0&(Sz{cMf16T!L!0=1Ri}n zsmw(-InUz)qriU*)X6*s+Yx${zV8p)Z@<0u9erL1uH<6+bVqw= z7vX+ZwrgldXGH|&EZm{RzS^Vfv=Rkz6>e*=$OYkYwQNCylK6MO=7nczd`OE1BG&B&4D25y4?@4b{d0Cc zXoGpgzyVp?AMG{z5FHR_bCkhZ`d|)%tT^eUlfoqzT@=!qFE>2+;GoSX*7v@?go|B! zQ^3=Lo66D`it~JKKTke;Ii8O=_j}y8xykhS^UF_$CF49F^u5bYJDG0J+z|N<|DSu# zgsi+5&oj;&&npbq<-PMh_=nHQ3(xbcwVWLPlTQ1w%cPHw7S8kaziPt&*j_z*uhksU z>-mN6Z|9dZ9OF(_s0wG=Zqg`G{M^YIcJ}nllwm2qx%^xOrB`+pKy3z_>YAD zwVIEQbDmGSvJp-_wMTJz@u_BCjJkG_T~W41Y*6SW=E{y(Ed1lg4hjCD2Z!#mJwi7; zlMZ>HHK)MdkJbPBCLDLn(Lt1F^N8uw&Nn(h*0DAWt>W^W1j3Wg-^E{9+22+cb8^y5 zja|yg4=tB4urT&}VYk#Cvd~BFN6S})V~-a8#sAO;`Ute0 z1l|iBQ*>&w1HHoa&U}}29_D&!^4a6vW=5I#Jq@nMkv1QQO9JuUF2a#*JVWEA2gT`m zpF;1Je9wG8GIzUQ{?hQj;+=Pc&ddnfi(zv=pI0%bV&08xusNaT`lB>2WNrvu0A8-? zjLZdvd;I>ac4maXOC9G)SK(K6FYcCU+iu-D6KhLt+O`huSbJ*K#?~X+w(k(ywQn0b zv~3^Si6)>C))%@9I<@Z@j6OsookSmu7vR6U=;QT2EwS|t4Lv5?&=0|Z1G)l!XoE7W zOvxDJ4DtrLqMwNOFkFBz+J}`(81aNt9*|7$eq>u0)oXRjJym&F%jl%G zc^aN+D`nym?(efHuG1*8eh1Bq&k|05r#WSF*@2Ea<`}c>cp9H~K@UE^_PMs(fshs2 zKWubxR*ZM?6nn{x22@r?ynJ;M@9vjexKi(sp)IlzM7GA*s%2=^vUO;o^~9F(x?C);y2wRdbnIYjmM$BLG_N`0utV*e*25Y$3|nu#b&^ul;M%Q#; z{Q`MIdCY&IEo2XVT}?}Q zYn={zk?4SOkf+cFF0w#nROmgfjB%dS0k;r8%o0?9e}`r*TH4w&`%+ppZ)xw}vW4*9 zMl>RM09_y(u<^BTE&QmD+O*5|72`#>?p?#NTGPbtS970zLhahML#^61L(Q5s!#)D+ z0ZFiMrDLC9GFF=?&T))e4F3P@_M~|f8JYVW6QL%^)C1+lYICA z{2K$$Lz!N#_UQ(ln-yA$*1)^Zt8nvafVBW<06RRu*uXg}J|5tLe;)^s|DxM#g#RS& zQ#zY)YlYvgaXo5!N;BJcpu0mtiaZP-_KR->Oe4Z!iQdXh7 z$uvp7r=@N90chZt(gl&J;MnKVj{l?#7&cscFSPFB>#s@vM;CP2Z}tMs1*AJf8Yk}& zzS#qo`^EUb2mWs%IS-Cow$k3Bcps6C|H8A6cevn~Hafm>J9g?|W3A~z!aerP6J`I~ zahIJ8Plm7k_78QTfjYHwVc^N}=s2y+^>l??@_+-+TUnYy;rU^dlMFLDfrer_LN>Te z1E-|LwRCcmc&^N!pO!MfKWilL)sUgX%+@w&pyn>x=VyEUWM{>`I!x>SHvY$a53X4+ zIBSf?eF19)jQi+*@IEx4^x%IE_|73;9-f82d!*Y&$a?tRa1GAev^Cx~-3gscI0n~@ z^_{zA^>pyfd=MQB9T>o`a0az%8O z#Gm`Eex=E!Qy!1oT$i#c%L8t$-MDIJ?OJkGFLdwu3gI6apndu_j-vCkw{w_qKI}B@ zt&u!n%(pp!)>vKEGtMLDadY?<&cXj!ts7wnz%C?Nk>&qheb&Rj=1$1}dpdUR9Kq`* z(}ipJ8(bpC0p>)^>6vf2+qYkz(5rXP&@Z+FviGQ8JZ7;zBsz+8j+No7p7A5N$IZuWe%}1}d{D^PM)L zfzGn=fZuLiMF-tArxsuLI2F5aap>7gbRd6!_MHvrXEeb$#oEDu!2@mlk}Qds+c@4G zFUjZ1{NUW<>clkT&&1U`K=J#Fj>KOF)D^9Wzli6VzBRwxe1AS3H|N>$$pEDIU{|Hx1H+4fh62G~=9jQ4#2$av>(sGxXx&zB2hF!Tb`6php=+lu0ehjz zd+3AlK;ZHpn~d;Zp?OYJOY~j0etnz!I}ZOFF1Ce2 z|0Dk;Zw&v8<&5v(zhnE@W)j=^u@fx z-{@aJ-=v!S_vcM@$(KxPV+i<;!@(EvI(N{%HTAR}PX7{?k5`^I7FQ_!riOXB;J$9X z_3I(d%fXMH$aAAcjrg9{&Vn(#OW!Mz4glWK3BbA8{xlw(A;9JX?j4Z%$Oib|#(v3! zjp3j10QoPPi@>qzbHXovUk`AduS>V?rt2|2br+t&wdn}xee(B~&9`TFfpi7zTXG}K zV?#`HI&HSCw>R_*m*{c$nMJo z_oJlu0gMaqJv6}F&-V_nPRy7vdD3}|AqDk6$A3@Zzq5Fsv4=LGA3)VNum4d!S4u>ip_Ykci zI`B;1!w)_r=$rzJCv0u_3pO1fdzH@u6<#g73Pvd`wF0A{B zKDuhnM7F)|S|f%ASpR@t7z3Ca!27&|&ljK@{Imzzsp)mHtn9ifQ zjk)T-oU6SsIe#+zKkz5plkYFsK7T*8leV&-!ytY~_tP)n2i~WjSQBvEV*`Nihs(}0 zO!A+(fXM*u$sR3wV9m(qe&F86eqR%&4%*7zAnngBsQ-a~m;cCqWVKxQ*5-iikOR^I zSljXOf%QFT09oMx9WWd2t!|Svxu9A$ zH|rj!$mWe*1i!_}F5X=BF>GX%L)l&yHZ$hWgk!_Sj>0o8&wQW9a}HQnWsQn8b@e@V z6u#l%yAJGo*n0dMf93#;{mcoO3&8&)N1c&f1J?6c$$n_TSLl74^2wXsxvMLhk)nlt;T;D2;KqXGG$0pviE2A~6@ z0c`$fX1~)kSs**0(*ZI8`2r|EJYCTKt9F?Fm+XJ1Th{o%KhL(lr?nmWs6*@;{`C&5 zjjvlX*ZMl&BBB>)Oki!eW%Fjno2)TF4>>-AHxgj=sLmXZ#QfrEJe=otZL}r}53z4S zz4K*+Je3W>&1)SY)xD3?bt3W_d3e&{bH`d3Cq!Dl1AmXzJd1CKSr05 zo$!tEj>2%Um7{CvjAZeN#@Tq^4R$r~p8Td8yaUhxbYXUXeQUvdfG}J8l^j48*mpnr zUd!yku}8G}BLzZ$rMDY%gn&{+#rC7;VN+8MxrS zt=uI3TPe(e@D8oBvI^R2r+38$0{wJUKeQE3*moPk>3Z}5eX&PI;bu3FbB%9QsKov@u+3iHzILnrFvy$}I#owkG6+%L}IsY}F1<7mi<^Jzdi8w2*X@ z_;=Zm3Jb~52A7C+{YuAlb#osu=cSGv2ghr3+y{O=6%97 zG{Am($35}@ddT5k>0BfVx7LP;Q0ApYm z;T@Xq5bHtc2jDXaxiDWVWA3E<1~F|gpYn1UQ|T|`Tj7y!ENsnRG%)zIb##!F2{sOB zOuz--t{37@>IIH}bVBV{%xLWK{XZgsbwA96p=*#xEkiLOlk!~d)efLq&R((IIt;uoAdPZx4&gY&oJ)?LRdI3r-K;n~J$(Q${a((90I0&Jk&I%&+t)-AljyZDiF$UvJTY2351Fy;g2 z2gZJ6BXI!pDD+HpB*tv^bFqfT_ucS5drW<=B)kvL+nS(U{Icnr4q*BpJTJKq@ZC1$ zp$9VOW()xT>W8a@_4RlkDo|UlQX4XkXWS-|(a3B3xZs4bpFU;WY}ZP-7yhAl-m8mf z8k+#`3g7qW*3;&*yqk?>Hxv>>5~i?FH2uSZwDg?wQ?_>d(yI`ECL?2bvRO zC&fkrz!S`!O+Oah&<^yz@%o-~^oTRnFIoSIevQ}T{$u{%Lw&-#vj@`m zulN|{{KgoGyfFO4a*{aaPRxP93FRkw#h+cbARYWe{lr;&;(Uxb;FB=xZ~9z7|6BXO ziP{L>==*%=qqPidSbb!_FpyvK3Gk4uGpTZPI?kP!io2g`>wLZS4Op)K2I<|j7m@i1W0cDz ze;3+`45L4IM+ZxHb`rNw+q2fsSwr_Y?5X}dQGQ@@Y=3q;E4Ehb zz2uoRLEmhOA1Cmw7W^Hr$$Q>E_ zkUJrGe_Z;8y$omTyLMl{vbZ=ee)412x4w?uX;bwL*#*U)-?UX^R0cf5#St6 zz@BM)+~Zc_;wiJ?9D4KzcO5T#Z`6&Xa0Gd#It@wd44V@e#+wx ztSc{5ez~Oaw4|dPZwF~POOtaObtin#J&+MJ{q9noRlHwIV6%?LYQh+6k<)xbh@8NW{MDEf9Z__6(7o2IZQEKG>s=0f7uHC?_o2G` zSll?m5 zt_;|l4CKe#`h(^t%y)ba zI5-Qr^;+r^99TbFKW#wfvZn2AWj}zoy^hxV@!S4Befz@rY~K#V-@*8ID10ZvJ~{e{ ze&d_-*|*%vTrczM8?Fca_CsEXZYplP{)R9^GC=xC1!opdtBtgVcPq^{s%z1uF*Jr@4WAP56?UBjU>Eo{3f^Y ziQ+r)Rg(fd4X-5uzCAe(J4t@xoF|D(rX}0~UIoNs;e->jfS~dIkcCHR?*jW5cz*}zy0-QJG1hPf${d}Op)s_AbzVDH`M&!i*_|T#^8v+i zPLRgvh;f>8PtX}9!z1z_Cpb$Sxa-cl?YnmAg)22*U+?@6rv>4^yL8`gz`FzXD0C9j zku)x|hj`M&$#%Ag=>Uwa8b>d?G&}ZlCI@3ay2y>CH-uYmo*fv6Br7W50p627L4G0Y zjyX0f)2%OszvI-O^euhgAkeg;R&a)HRd{lM>TxP^eRtaH1p z%@!ROUDs_0p5C6@%GN8Jf|Vybu#bz_8d+=NTPW)GIkMSGWH)K5@x#}kv7b5p9Iy8~ z>3EG1q7|nbr)_8-IRejcehqp6bTI4M>ng;P6_U*r=S`jxJ#hbn7RNa%q>)}`=l#R? z(km*+!%o7WSZ#A zb*PIahtOeMHv;dR4bRyt=p4u|+Q3;S&?E20*zf%Sj+3}$u1Oy{9^Ka9I*8c@WVdnq z8RMSK`!xq;UeEjleZ%JXIu{lF!)y_nPZG|Y(&v)sNXdC*{16H{sC>fZsBymIYx_~e<63C5xh;*R=x!Yuf(a@|}k@gUNp|&&SOpz~=zS zd2~}`Jm9>}xjCg8YmzvJ?x0KBgib2im4UC3jm{_VEx4v%0Q!hNb3B7zfWGzq28WI- zaAfwFc#e{!j=KxryaD*U1-DU z!)b$efi9pC0NLVuAiN*wALxN`{MA3d78dEuDbAe3vjbE*bxA99$ex)En$@7`SDW+F7;6_^*dcToj85aZq{^j zvPSO(UHG{w&_!20R8(Z+S zXhC>@Hg1zlmM-~$`u?aw_!s>iqW4~oekoi>%;T@sxjM6Axt`>4&Z@ol?m2d@AF>9y z1HS!yHtOaaJZP6Q5=9Rg-VNFY*ZF+yJPod$r<~v5NBWok_WlP4NgRPUbQbiVfHkoI}dlvz%efoDW*S9o&&cgn8o|ETe z;b*LKm$43A$I}r`{v=R`*M&>{Zm*?XNxFbub2J;z#d$xI6Iu)KF~r9*;hy~&HkaqD za?T)UUeEj<@H32|2WS9VfCe~wlraO`)BlVE;01ns_|ZqAhvq&UW?pq|q_(^*8$i7+ z3fO-opLY_E&11eGyhiZ!!vgN^hPQoufwq9(3CGllEb{Yp!8L7yerY3T=hA+)F$2Dl zbuKrZpB<-;SNhWX*ZbXZmYj2%9a8gTx39S!jj_t@km#o9DSlQUV^|WHUFrmA17jxkQMb85OVHK?(UZN0=!QMHMz4}1wr79zXtS|6eIpy8 z1)WK+>3n2oL2@1_XIKM%R<-0pggoHvSjGzQz*yjX53WpSeEiYyc&ra%E7W%be{RzF zWbOyu;HPonmy)@ci{HzI$B4VODIZ)Shd8eo;5=;3tL6-Cfb+^|E5I+`PngnY;0<_( zv+n43=7`BL1sQ|AlQ}JV1TqF1M3>-Qpg}*ElkvsRc||AUy^%S9pRbDifi`{2L_Ydh z3cWI(I^Fsi(LT=NdRp?+zmx}XcB_xKoC^!TQNN%2iX5c>IcpT&q5ZChp@;kZNWAME zx8G^=W6r7MOiE}5J`mq$)~sEf5zaF@H#eiPAfq#*GwT10&K|E2lnW>2qJhk^Wy><~ zm&SpN+MQ9mGiqZ-{g4q&WYorTolP4_PfLsEW`gHDpVk;(E_t7MV*ZmEy;Hf~FD?BuE!{4o{?Dl2t!|yeZEX?VWF((5v`;c1Bbh+m zrk^KeG;$UmaXS)oOL)ocNX~oE3v^?41?irQP4wAp*~f03ojq^S$0+)B&b{{-+u=Xhp(}8Z1oW5Wv-?R4kQbT+IH!|wkUG$Nnd4&L0Fax|v)_@%y2s4xBv&LukRQm5 zD`J^(q23Ys#kvJD&Bq_tV?}diOW%3BOn6PJPs>EdY0+R>_)Lr5%7xQ1^?jM5ZzAY0?rlt3!C7a8IzqFuS<7D~d^Cp$4{biDQY018{WMH{)n3haT z>wOI*L(9}BW$1o;?6HU0H?VP-ZS|n64Hi4#Aq@}C+5;PA?P6{VbQjwJkhBLT*96@5 z=5{#d8vyf8bWhwQxE;dXWL(luyqAT&#=-1-ZtsiNBxui(*!8umNMc)EHJK`bmr4({jr+E~Q07^k+GJD;_DQuVrg5n>uX@I7ka0 zh8O8R;K}eMT_i04f7p&zXx`azJ%r~yXztNSNtQ@2jij5UV<^+Sx?H+STH`}nx=WdK zv$XUWc)DDAZd$Zb4y{NJre32L>ZTpirOVW|GWwBoO*r3(bpTxGVYgj(54-P?wR>ax z2KL%>@2so!Y`%vD+)nQ2>accz9UZ%JZ9sNYTT{>)IktAr<{-?$zp=&L6u@5Td2uN- z2_8oMN#I;4Z?B*I#JW81#k2eUUA-T0=^y$@afEHqUa9SvX7HE&l4#^Rt3F$v+bpN0K{O4h(opv(dOo&}}-Zkv9>&`|C zyY9A&xlRwPg+Lz;-e2z0m-zGhnrHCg;qDR!ec)~?Bx%m+4wo{)BW@DMggJh3z3sTr zywg3n2IvD^&PM^jKXy<03wev&1?WfNy9_>~f9Yr8JO|(%z69Uk1UJS#cmrqX-}5x~ z*Nj;{ANTgG`R5LT>m>uqG!H2UpabPC6CIR^4$37PDANF$07$l!0cuA%a)GwdPJzh< z-UV9^@5fniJM6H7!Hzof)&aVpKj?>Kzj$BaLK8{YBc_qvMF%;$i2WYsIC0)Hynve+ zS0115ChpR)g#YdHM7}>7{iP1_jQ_ZKXLShyWAP; z;B z&6r1{NwnBuVUUpwwrtxzd5g$IUfq&W#A(*c9+Ya5uW}f{Ev~&l05g# zaPH^-yIj!PXE)KwhqBK`k^^bwD~nyD1<3@X2hjjDQ6@StdQd&(&;y|MAalx*L9Q=g z>(u@7b_XD5fO5tm_d)6A=};}Tg@toJsM-^@+-i$lJZS*Wv(;8xnZJ7V>N%IP0MZct zZNNQ520yEwb>rvt@@>1#2IWu(X>iH!^%6$i-ZuOWgz;X0zYFgOq~b90tN)GWv6CP> zhYTJJ&aMAqnQr|V%XavjJ~n*A=jwOsfBeD;avCTTM8ehk;^l(tel7sy8a+t=I96*k zuPS{cIZ!TIC>Q?ACBJbwKN32CpKXkYWkXu+26FX6gR~L45Dk!*^M51mIpQ3LzG%(--N**sGY-(y=lV%OW{b>Mn?@bmp2cfYV7SZl(3pT{}v z@HTvHcy@jgzBBZv0KD3G4BlNHD=a6c31jS5JZH%)5&jxEkLTmuagQq-n&O7ePy0QT=k9Tm}GtWssbMNG{r}uQkC&McBd;N(zb7?mg7pK2Vw(c;kS!lWFKq=>tf>J3gI{h!eNDQTY*~$h^r(%gPax)<_e$t z6u^5TDC^a{^%s%>gEU`%Oti32Ov z(?adxT__$~55#W^9fa?NfcOr0RAu!O9)Gq0vI_@Pll`KG@O7Z}BQ%uT5cl|Fk7;<~ z@y9h3ZEP0Eo^dF7wBN4v)t6oV@b6!I$-bcj|87^SsxJ1#J7?b-rk^)i{Qp9@V9L~o zWfy8CzB*LA)KG3i@l!+h9O1wEtgElN<+-Pxj&!C%bdlCSSl_G4z}~C}=iF=ff9~n0g7ANf#_j(W z{wu?0W#OBG-c3&9{Qu1SC!+SPT8E3Z*2lVNRR;FxJ$(NIVamh_;n^pjsJQfk3vc}| z$p6Z41m2UlPx>p%v)SR!|5~5;x%mHX@ZYXwE5kqQ`c)ksdEi0gf8js6c*<`l6?wT2oE`0K-;P_{cL{$gyKW*})u<)rTBiRA&l-+0B zjo>GVtMBS}9J~8n%h{}aWBL!MF>B_G`OhtQ+U!53AOeZyYk zdh!1=8uK|%^P0=Aux~^7KBTI`BE9=OjsKU({*PR|^ui0~>ujl?B>uJExyFsx&YJ(s z6HglcZ=7|V?VDxqY*mIQ<~?fbKZ~?iSnB}uwU+ji#J|=&YsmgPf5GGPP5zhaTT$&x zuJZ7d=D?Es0sGIT!hg4pKT-UT9$n)G@&AHHA2a=rz1+TMzbd;=>$`@FwEnkP{C~0J zf8U?5{D=SNKm3TDsmnfjzNM(jz_%*!zsCLWyy$@Oe_-b>KS}wo{V^v0!9VMNd@JGK z)Kq16f%snQe&Jc^MRMoM{_~T>Kl-26|K>mb(8Ce>Ki}~9w^>!$Wj>_wKYCJnpw@rp zYhUM27ysAHm@j)r#2L!7_5Bv#-&J*3C>yBOeZmuu%me>V{G|C`_{ZKY9XD_u0pG4y zb$CwSiX#7G{QqR-KjZ&(S6}lu_AbuHo~v&w^?hSihiI{EpyK<0UKEf2JA5Yt>kmBB zxY0KLKlRXk_uG8{^Ync;-MLPZ;|B1@~YU75F*hTBVFFrQ+q0qBi_W=I& z?Ru34&U5*L&JVyAF8Lpc|DV+N9Y0C^Z_=nUcQ3u}`ajGU{(E%o79Q2!C(f3r>hOEn zKxbTWWkCOz9{8O0_wW1z7v>dcSlbxw-TF{$#fL}xbLe)BCAx0c>i zt~2Oou}@B@{9oOh^S?kk_CC5BtdZ_{xKj6G+^26oKJzn$sz*_qFPLlY=DpmU6yfRR{6Gn>tUc?oSl)IoYrG z%lW~-zWH0q*^pHoqz5gfKYpV4FZf!#?}PU}u#|Hot2$Iw{3HMU4AH9W>P+Q##19P$ zGRluXFemequM*4ux68`PLRE)C{O@{Fvd@0Z{`~g=|DyY)oNZjyfqu|=k5y^>ucr4s zQ0Mo&#~J5U9dwp(A^BfWZvIGRK>oW_6aOEn{a+t&c6e0>o%_8)=l3>9$Vl%mcYma4 zw`Ti0nCtJT^RAE58Q-6BHhfk8>8$6Eb$0gQF_PEouOBnwH`4ZM!hcQOCG`z=R8(c) z+h?8U{f*AYPTK#R1~$@#KdPwY`m+W?>W=?A3N`{k$4@9!=1(&yu6%NN!wZUg$tPoH03 ze*6Z{dF6j={@e4~laMyVRL^_fChvLc*VfNpJ@0v&dF#h-pC>oJ}a1=rg#qo1)0IW&rA*}TLHSIB+k@uXRKL2^Lr{5=={~dph%gd2S9RIY( z|2&(%n?LW8ea@w~;V*>w9?$StUk|keO$61D=-yb^d)#o$G$M&h>9p z0FKtV_Qw=}V-#a9!f4$z{;y?N*dynLsZ@wA+{^s9vE#=O( zJ8!$~rkqm@w}{)x+;CmHF|fpN7Bvbf*`0yz%?^KKRj|nYw3?y9E87!5O-PP;;bz>+Xns zb*IRVI!FE=oEtxM_|Pz9=n#EF-zl7X&V;}{@ch31^6T))r=QrnekniuD(G+4*T4CO zJ0w2~pMUvz__zFDe)*N*!7}R4+O?|#doo+-?irmw6FSHD6l*VHRIgsWlT`n{I*VHc8}pZ z@4RPsoRasISN<5LOg@kE&%+p<3(OsH6VII(WETsGQg85}L94V+%I;Lv{Rp9V zpWfjr?VE0`yQ9GicT;mGBj-w8dBqjslK8GQ3s>Bwmt1Dw9CPjf_mp#|9QVuJRC<%$ z(K$o@(WA%c{>G_xha>m1U8Z}k>({H-S9i8F?9!$4+Sa}bl5$ur=LD79COss zcJHh9?FQ}L4*mQ0wR=7La`&&o`u6Reb^G?n$t_ z1GqDQ^Rc;4Qtv;cZoRrisx#e0cc$wMXuC&M=e-B+sN#;1U33o)cbM$BBX^&yyH|$0 zX1JeZr=4`)3HPGNzw^$zbB8-n^lV{rYbbm>-9@51W~_g8=Zv*ObJd_T;790gvPRu? zmjGwe>wHSJyNBJs%iU(&XR?>yzq9wcdwBe=oSe&jJBj<8{O%m$chEcRy4&tSb78Aj zbRQ<@Ow8%irgLBTNb2w3w_kaO&K+|1ywkV(RwguQa#Cn~QsdD0WZfs<z)bjau`2ue7Hd8ax`t$)aqA%m$z=!*6!}^ z+_iHkK2>*->+asR`j)+8hxVe0&e=HK-_x~=?)vVM#k1i%#<|XRwY#4Q>n=Zisq^{m zPF&sfk-O^x{GWN|S-OjMqP@T9|1jMPRYp5cYJ5^?*0fn@BVL8xTDE8rjyvIa@RYkt zUg!0PQ*@7X)27XhURt$iC7Nli`{!F_@7~s3>+QIoUM}}d>5hjmV)%&Aqi2sWaL~YT z+Mw)x%ae32x#)kW>TjUB*3kYwz4{oPLMs!6FYewLE`Id;Ik>lDl)}yw{f-56=PCCo z%hqA{98Q{aUQTWxJEn@?;Ps0xzR2!Zr*Hb{j*#NwZgy{Cg9Z%-ss8(mH#S@@**i$`V5b^8+WmPN8-nDw?$tXyaM#|EqD{Yp@5n|+nxE&x6@K{Phlhie z_K*e*EDv}0&=1(-;JI6MpCfXEGb%LR^wPau`2M^>9`oZ?xpSz!hw^N0Co%Lk$wzlq<G(Uf&qta#t#6ZE-&-=k7kB^T)ZXV!Zh6x#ymv%nIof6_Vc- z=m^{=dC0*BAB!%~wM*B>q0#>R`)5jq49OGd9%=5wBA%Se@e5aqI=Q5 z?o@$Kno3?aZQe9AZK2S=oBMlRceUycm=TiaCu_WIDw*F@GEa5Y zMn$jmw||{F)n;6I<=f~P=zH(Hw^a8pzh`>L^5yS^70ceYJHv@X{{YZOr0$x2_Ky!f z3ag|qEq?y_bfco8&EA7@z<&E}H}lFXmw&E(%o+;RERh?EiXk(W1;zM>aaH(ig2HLjA>=vdh1aZb@D5z4xxY`?6)(yV42!;QbG* z{trLQzW+z@H%HX}5B0<9HLG;@!s5))jgCCNl4>dqR{w7|YsQS_Uw`$L)vq~))xToJ za(n+}E0*QzU$G)v|N9?)Bt1R*{>7~rE#~icS^-D*sF}w7p&(N#hTeeK^ z|6W-3?y{gck=4Ik^{)`l!T-AVmp{fobg)V?=XKp{e8Ta^H?H(WD~S+KRKH;Q`FDTw z?{BQW_w@ensvZ9R@2me;EYE)9=lD;)KYDOly0^XsUs9`P&03XIv(aGbBu#Zkvdwdr zE`|Qzjr&1vOoz}zwoPgaG!gg73cWw`2HE2$ZM2e&MCuNtcB{oh=-KGpxX5wB)@bgt zdd+In!^>2U?R840ZO;pH4(4vGf9uZNi#JlmMx&%39WD93Sm$WJs5`P3Kl#*CI!Y=F zPe1kaVx6zN_~`{tFV;HS;swtDPcPQ^kbPdT;6&o!rQ zEW@|NkvE2FlwAlkuuOG82x69~C2%`Mldben`L9~rHf@Wg$6X~kezo8#ovU)S;2Moz z*GO-iDSc(8bh?3>-xZwm&wJz%n^+>-nLj!tW&3NdzWTN9d8_WtkR4<%-5c3mI&B}# zNtTlboHH+oI?*g%q6$_`bDi z(u4^&B=aQwnya=%FY#|Yv4MQ@*(c$1h4I}JGO|aHQwJyWB>lQ0yteK=e?#z=&PjVq rwEmX(?JWhpr91B5(%Al{bUf2dJ>Ltu*?f7Wx0`#ab?$O;ZW8_<9;S+J literal 0 HcmV?d00001 diff --git a/Clients/PrinterSetupWizard/res/Print.ico b/Clients/PrinterSetupWizard/res/Print.ico new file mode 100644 index 0000000000000000000000000000000000000000..22130b3de3ee242e78b347bc07d177cf3cd44a10 GIT binary patch literal 77214 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 literal 0 HcmV?d00001 diff --git a/Clients/PrinterSetupWizard/res/PrinterSetupWizard.ico b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.ico new file mode 100644 index 0000000000000000000000000000000000000000..22130b3de3ee242e78b347bc07d177cf3cd44a10 GIT binary patch literal 77214 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 literal 0 HcmV?d00001 diff --git a/Clients/PrinterSetupWizard/res/PrinterSetupWizard.manifest b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.manifest new file mode 100644 index 0000000..90a067e --- /dev/null +++ b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.manifest @@ -0,0 +1,22 @@ + + + +Your app description here + + + + + + diff --git a/Clients/PrinterSetupWizard/res/PrinterSetupWizard.rc2 b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.rc2 new file mode 100644 index 0000000..27e049a --- /dev/null +++ b/Clients/PrinterSetupWizard/res/PrinterSetupWizard.rc2 @@ -0,0 +1,13 @@ +// +// Wiz97_3.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/Clients/PrinterSetupWizard/res/Thumbs.db b/Clients/PrinterSetupWizard/res/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..f9f63303f0eed979076b3fdbf3beed6e2930de75 GIT binary patch literal 6144 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 literal 0 HcmV?d00001 diff --git a/Clients/PrinterSetupWizard/res/banner_icon.bmp b/Clients/PrinterSetupWizard/res/banner_icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..8e62c35c4da70559ff1c79cc3ca21a9811a6aa24 GIT binary patch literal 7308 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 SFYnO1FW(`(N2wh+cI-dnTqX1X literal 0 HcmV?d00001 diff --git a/Clients/PrinterSetupWizard/res/watermark.bmp b/Clients/PrinterSetupWizard/res/watermark.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e76046b64272f10609ca113e73cfc768aa9a7ec7 GIT binary patch literal 162908 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 literal 0 HcmV?d00001 diff --git a/Clients/PrinterSetupWizard/resource.h b/Clients/PrinterSetupWizard/resource.h new file mode 100644 index 0000000..8c2a6ca --- /dev/null +++ b/Clients/PrinterSetupWizard/resource.h @@ -0,0 +1,74 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by PrinterSetupWizard.rc +// +#define IDR_MANIFEST 1 +#define IDM_ABOUTBOX 0x0010 +#define IDD_ABOUTBOX 100 +#define IDS_ABOUTBOX 101 +#define IDD_PRINTERSETUPWIZARD_DIALOG 102 +#define IDS_GOODBYE 102 +#define IDS_GREETING 103 +#define IDS_BROWSE_TITLE 104 +#define IDS_BROWSE_SUBTITLE 105 +#define IDS_CAPTION 106 +#define IDD_FIRST_PAGE 107 +#define IDS_GOODBYE_GOOD1 107 +#define IDS_SEARCHING 108 +#define IDD_SECOND_PAGE 109 +#define IDS_GOODBYTE_GOOD2 109 +#define IDS_INSTALL_TITLE 110 +#define IDS_INSTALL_SUBTITLE 111 +#define IDS_ERROR_SELECTING_PRINTER_TEXT 112 +#define IDS_ERROR_SELECTING_PRINTER_CAPTION 113 +#define IDS_INSTALL_ERROR_CAPTION 114 +#define IDS_INSTALL_ERROR_MESSAGE 115 +#define IDS_MANUFACTURER_HEADING 116 +#define IDS_MODEL_HEADING 117 +#define IDS_NO_RENDEZVOUS_PRINTERS 118 +#define IDS_NO_MDNSRESPONDER_SERVICE_TEXT 119 +#define IDS_NO_MDNSRESPONDER_SERVICE_CAPTION 120 +#define IDS_PRINTER_MATCH_GOOD 121 +#define IDS_PRINTER_MATCH_BAD 122 +#define IDS_YES 123 +#define IDS_NO 124 +#define IDS_LARGE_FONT 125 +#define IDR_MAINFRAME 128 +#define IDB_BANNER_ICON 129 +#define IDD_THIRD_PAGE 130 +#define IDB_WATERMARK 131 +#define IDD_FOURTH_PAGE 132 +#define IDI_INFO 136 +#define IDB_ABOUT 138 +#define IDD_DIALOG1 139 +#define IDI_ICON2 141 +#define IDI_PRINTER 141 +#define IDC_BUTTON1 1000 +#define IDC_LIST1 1000 +#define IDC_BROWSE_LIST 1000 +#define IDC_RADIO1 1001 +#define IDC_COMBO1 1001 +#define IDC_RADIO2 1002 +#define IDC_GREETING 1003 +#define IDC_CHECK1 1016 +#define IDC_DEFAULT_PRINTER 1016 +#define IDC_PRINTER_IMAGE 1017 +#define IDC_PRINTER_NAME 1018 +#define IDC_PRINTER_MANUFACTURER 1019 +#define IDC_PRINTER_MODEL 1020 +#define IDC_GOODBYE 1021 +#define IDC_PRINTER_PORT 1022 +#define IDC_PRINTER_DEFAULT 1023 +#define IDC_PRINTER_SELECTION_TEXT 1024 +#define IDC_HAVE_DISK 1025 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 142 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1026 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Clients/PrinterSetupWizard/stdafx.cpp b/Clients/PrinterSetupWizard/stdafx.cpp new file mode 100644 index 0000000..53abf25 --- /dev/null +++ b/Clients/PrinterSetupWizard/stdafx.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1997-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: stdafx.cpp,v $ +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#include "stdafx.h" + + diff --git a/Clients/PrinterSetupWizard/stdafx.h b/Clients/PrinterSetupWizard/stdafx.h new file mode 100644 index 0000000..160644b --- /dev/null +++ b/Clients/PrinterSetupWizard/stdafx.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1997-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: stdafx.h,v $ +Revision 1.1 2004/06/18 04:36:58 rpantos +First checked in + + +*/ + +#pragma once + +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#endif + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#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 + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later. +#define _WIN32_WINNT 0x0400 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +// Step 3: We want to see one image, but not a tile +#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later. +#define _WIN32_IE 0x0500 // Change this to the appropriate value to target IE 5.0 or later. +#endif + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit + +// turns off MFC's hiding of some common and often safely ignored warning messages +#define _AFX_ALL_WARNINGS + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC Automation classes + +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT +#include + diff --git a/Clients/SimpleChat.NET/App.ico b/Clients/SimpleChat.NET/App.ico new file mode 100755 index 0000000000000000000000000000000000000000..3a5525fd794f7a7c5c8e6187f470ea3af38cd2b6 GIT binary patch literal 1078 zcmeHHJr05}7=1t!Hp3A*8IHkVf+j?-!eHY14Gtcw1Eb*_9>Bq^zETJ@GKj{_2j4$w zo9}xCh!8{T3=X##Skq>ikMjsvB|y%crWBM2iW(4pI}c%z6%lW!=~4v77#3{z!dmB1 z__&l)-{KUYR+|8|;wB^R|9ET$J@(@=#rd^=)qs85?vAy(PSF5CyNkus435LVkZ$rj zNw|JG-P7^hF<(;#o*Vk}5R#e|^13tBbQkeF?djULtvqyxd3<{9 literal 0 HcmV?d00001 diff --git a/Clients/SimpleChat.NET/AssemblyInfo.cs b/Clients/SimpleChat.NET/AssemblyInfo.cs new file mode 100755 index 0000000..a778420 --- /dev/null +++ b/Clients/SimpleChat.NET/AssemblyInfo.cs @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1997-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: AssemblyInfo.cs,v $ +Revision 1.1 2004/07/19 07:57:08 shersche +Initial revision + + + +*/ + +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/Clients/SimpleChat.NET/SimpleChat.NET.csproj b/Clients/SimpleChat.NET/SimpleChat.NET.csproj new file mode 100755 index 0000000..2165beb --- /dev/null +++ b/Clients/SimpleChat.NET/SimpleChat.NET.csproj @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clients/SimpleChat.NET/SimpleChat.cs b/Clients/SimpleChat.NET/SimpleChat.cs new file mode 100755 index 0000000..fb841f2 --- /dev/null +++ b/Clients/SimpleChat.NET/SimpleChat.cs @@ -0,0 +1,757 @@ +/* + * Copyright (c) 1997-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: SimpleChat.cs,v $ +Revision 1.5 2004/09/13 19:37:42 shersche +Change code to reflect namespace and type changes to dnssd.NET library + +Revision 1.4 2004/09/11 05:42:56 shersche +don't reset SelectedIndex in OnRemove + +Revision 1.3 2004/09/11 00:38:58 shersche +DNSService APIs now expect port in host format + +Revision 1.2 2004/07/19 22:08:53 shersche +Fixed rdata->int conversion problem in QueryRecordReply + +Revision 1.1 2004/07/19 07:57:08 shersche +Initial revision + + + +*/ + +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Net; +using System.Net.Sockets; +using System.Data; +using System.Text; +using Apple.DNSSD; + +namespace SimpleChat.NET +{ + /// + /// Summary description for Form1. + /// + /// + + // + // PeerData + // + // Holds onto the information associated with a peer on the network + // + public class PeerData + { + public int InterfaceIndex; + public String Name; + public String Type; + public String Domain; + public IPAddress Address; + public int Port; + + public override String + ToString() + { + return Name; + } + + public override bool + Equals(object other) + { + bool result = false; + + if (other != null) + { + if ((object) this == other) + { + result = true; + } + else if (other is PeerData) + { + PeerData otherPeerData = (PeerData) other; + + result = (this.Name == otherPeerData.Name); + } + } + + return result; + } + + public override int + GetHashCode() + { + return Name.GetHashCode(); + } + }; + + // + // ResolveData + // + // Holds onto the information associated with the resolution + // of a DNSService + // + public class ResolveData + { + public int InterfaceIndex; + public String FullName; + public String HostName; + public int Port; + public Byte[] TxtRecord; + + public override String + ToString() + { + return FullName; + } + }; + + + // + // SocketStateObject + // + // Holds onto the data associated with an asynchronous + // socket operation + // + class SocketStateObject + { + public const int BUFFER_SIZE = 1024; + private Socket m_socket; + public byte[] m_buffer; + public bool m_complete; + public StringBuilder m_sb = new StringBuilder(); + + public SocketStateObject(Socket socket) + { + m_buffer = new byte[BUFFER_SIZE]; + m_complete = false; + m_socket = socket; + } + + public Socket + WorkSocket + { + get + { + return m_socket; + } + } + } + public class Form1 : System.Windows.Forms.Form + { + private System.Windows.Forms.ComboBox comboBox1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label1; + private ServiceRef registrar = null; + private ServiceRef browser = null; + private ServiceRef resolver = null; + private String myName; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + // + // These all of our callbacks. These are invoked in the context + // of the main (GUI) thread. The DNSService callbacks Invoke() + // them + delegate void RegisterServiceCallback(String name); + delegate void AddPeerCallback(PeerData data); + delegate void RemovePeerCallback(PeerData data); + delegate void ResolveServiceCallback(ResolveData data); + delegate void ResolveAddressCallback(System.Net.IPAddress address); + delegate void ReadMessageCallback(String data); + + RegisterServiceCallback registerServiceCallback; + AddPeerCallback addPeerCallback; + RemovePeerCallback removePeerCallback; + ResolveServiceCallback resolveServiceCallback; + ResolveAddressCallback resolveAddressCallback; + ReadMessageCallback readMessageCallback; + private System.Windows.Forms.RichTextBox richTextBox1; + + // + // The socket that we will be reading data from + // + Socket socket = null; + + // + // OnRegisterService + // + // The name that we are passed might be different than the + // name we called Register with. So we hold onto this name + // rather than the name we Register with. + // + // This is called (indirectly) from OnRegisterReply(). + // + private void + OnRegisterService + ( + String name + ) + { + myName = name; + } + + // + // OnAddPeer + // + // Called when DNSServices detects a new P2P Chat peer has + // joined. + // + // This is called (indirectly) from OnBrowseReply() + // + private void + OnAddPeer + ( + PeerData peer + ) + { + comboBox1.Items.Add(peer); + + if (comboBox1.Items.Count == 1) + { + comboBox1.SelectedIndex = 0; + } + } + + // + // OnRemovePeer + // + // Called when DNSServices detects a P2P peer has left + // the network + // + // This is called (indirectly) from OnBrowseReply() + // + private void + OnRemovePeer + ( + PeerData peer + ) + { + comboBox1.Items.Remove(peer); + } + + // + // OnResolveService + // + // Called when DNSServices has resolved a service. + // + // This is called (indirectly) from OnResolveService() + // + private void + OnResolveService + ( + ResolveData data + ) + { + resolver.Dispose(); + + PeerData peer = (PeerData) comboBox1.SelectedItem; + + peer.Port = data.Port; + + try + { + resolver = DNSService.QueryRecord(0, 0, data.HostName, /* ns_t_a */ 1, /* ns_t_c */ 1, new DNSService.QueryRecordReply(OnQueryRecordReply)); + } + catch + { + MessageBox.Show("QueryRecord Failed", "Error"); + Application.Exit(); + } + } + + // + // OnResolveAddress + // + // Called when DNSServices has finished a query operation + // + // This is called (indirectly) from OnQueryRecordReply() + // + private void + OnResolveAddress + ( + System.Net.IPAddress address + ) + { + resolver.Dispose(); + + PeerData peer = (PeerData) comboBox1.SelectedItem; + + peer.Address = address; + } + + // + // OnReadMessage + // + // Called when there is data to be read on a socket + // + // This is called (indirectly) from OnReadSocket() + // + private void + OnReadMessage + ( + String msg + ) + { + int rgb = 0; + + for (int i = 0; i < msg.Length && msg[i] != ':'; i++) + { + rgb = rgb ^ ((int) msg[i] << (i % 3 + 2) * 8); + } + + Color color = Color.FromArgb(rgb & 0x007F7FFF); + + richTextBox1.SelectionColor = color; + + richTextBox1.AppendText(msg + "\n"); + } + + // + // OnRegisterReply + // + // Called by DNSServices core as a result of DNSService.Register() + // call + // + // This is called from a worker thread by DNSService core. + // + private void + OnRegisterReply + ( + ServiceRef sdRef, + ServiceFlags flags, + ErrorCode errorCode, + String name, + String regtype, + String domain) + { + if (errorCode == ErrorCode.NoError) + { + Invoke(registerServiceCallback, new Object[]{name}); + } + else + { + MessageBox.Show("OnRegisterReply returned an error code " + errorCode, "Error"); + } + } + + + // + // OnBrowseReply + // + // Called by DNSServices core as a result of DNSService.Browse() + // call + // + // This is called from a worker thread by DNSService core. + // + private void + OnBrowseReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String name, + String type, + String domain) + { + if (errorCode == ErrorCode.NoError) + { + PeerData peer = new PeerData(); + + peer.InterfaceIndex = interfaceIndex; + peer.Name = name; + peer.Type = type; + peer.Domain = domain; + peer.Address = null; + + if ((flags & ServiceFlags.Add) != 0) + { + Invoke(addPeerCallback, new Object[]{peer}); + } + else if ((flags == 0) || ((flags & ServiceFlags.MoreComing) != 0)) + { + Invoke(removePeerCallback, new Object[]{peer}); + } + } + else + { + MessageBox.Show("OnBrowseReply returned an error code " + errorCode, "Error"); + } + } + + // + // OnResolveReply + // + // Called by DNSServices core as a result of DNSService.Resolve() + // call + // + // This is called from a worker thread by DNSService core. + // + private void + OnResolveReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String fullName, + String hostName, + int port, + Byte[] txtRecord + ) + { + if (errorCode == ErrorCode.NoError) + { + ResolveData data = new ResolveData(); + + data.InterfaceIndex = interfaceIndex; + data.FullName = fullName; + data.HostName = hostName; + data.Port = port; + data.TxtRecord = txtRecord; + + Invoke(resolveServiceCallback, new Object[]{data}); + } + else + { + MessageBox.Show("OnResolveReply returned an error code: " + errorCode, "Error"); + } + } + + // + // OnQueryRecordReply + // + // Called by DNSServices core as a result of DNSService.QueryRecord() + // call + // + // This is called from a worker thread by DNSService core. + // + private void + OnQueryRecordReply + ( + ServiceRef sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String fullName, + int rrtype, + int rrclass, + Byte[] rdata, + int ttl + ) + { + if (errorCode == ErrorCode.NoError) + { + uint bits = BitConverter.ToUInt32(rdata, 0); + System.Net.IPAddress data = new System.Net.IPAddress(bits); + + Invoke(resolveAddressCallback, new Object[]{data}); + } + else + { + MessageBox.Show("OnQueryRecordReply returned an error code: " + errorCode, "Error"); + } + } + + // + // OnReadSocket + // + // Called by the .NET core when there is data to be read on a socket + // + // This is called from a worker thread by the .NET core + // + private void + OnReadSocket + ( + IAsyncResult ar + ) + { + SocketStateObject so = (SocketStateObject) ar.AsyncState; + Socket s = so.WorkSocket; + + try + { + if (s == null) + { + return; + } + + int read = s.EndReceive(ar); + + if (read > 0) + { + String msg = Encoding.UTF8.GetString(so.m_buffer, 0, read); + + Invoke(readMessageCallback, new Object[]{msg}); + } + + s.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(OnReadSocket), so); + } + catch + { + } + } + + + public Form1() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + registerServiceCallback = new RegisterServiceCallback(OnRegisterService); + addPeerCallback = new AddPeerCallback(OnAddPeer); + removePeerCallback = new RemovePeerCallback(OnRemovePeer); + resolveServiceCallback = new ResolveServiceCallback(OnResolveService); + resolveAddressCallback = new ResolveAddressCallback(OnResolveAddress); + readMessageCallback = new ReadMessageCallback(OnReadMessage); + + this.Load += new System.EventHandler(this.Form1_Load); + + this.AcceptButton = button1; + } + + /// + /// Clean up any resources being used. + /// + protected override void + Dispose( bool disposing ) + { + if( disposing ) + { + if (components != null) + { + components.Dispose(); + } + + if (registrar != null) + { + registrar.Dispose(); + } + + if (browser != null) + { + browser.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.comboBox1 = new System.Windows.Forms.ComboBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.richTextBox1 = new System.Windows.Forms.RichTextBox(); + this.SuspendLayout(); + // + // comboBox1 + // + this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox1.Location = new System.Drawing.Point(59, 208); + this.comboBox1.Name = "comboBox1"; + this.comboBox1.Size = new System.Drawing.Size(224, 21); + this.comboBox1.Sorted = true; + this.comboBox1.TabIndex = 5; + this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); + // + // textBox2 + // + this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.textBox2.Location = new System.Drawing.Point(8, 248); + this.textBox2.Name = "textBox2"; + this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Horizontal; + this.textBox2.Size = new System.Drawing.Size(192, 20); + this.textBox2.TabIndex = 2; + this.textBox2.Text = ""; + this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged); + // + // button1 + // + this.button1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right); + this.button1.Enabled = false; + this.button1.Location = new System.Drawing.Point(208, 248); + this.button1.Name = "button1"; + this.button1.TabIndex = 3; + this.button1.Text = "Send"; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // label1 + // + this.label1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left); + this.label1.Location = new System.Drawing.Point(8, 210); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(48, 16); + this.label1.TabIndex = 4; + this.label1.Text = "Talk To:"; + // + // richTextBox1 + // + this.richTextBox1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right); + this.richTextBox1.Location = new System.Drawing.Point(8, 8); + this.richTextBox1.Name = "richTextBox1"; + this.richTextBox1.ReadOnly = true; + this.richTextBox1.Size = new System.Drawing.Size(272, 184); + this.richTextBox1.TabIndex = 1; + this.richTextBox1.Text = ""; + // + // Form1 + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(292, 273); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.richTextBox1, + this.label1, + this.button1, + this.textBox2, + this.comboBox1}); + this.Name = "Form1"; + this.Text = "SimpleChat.NET"; + this.ResumeLayout(false); + + } + #endregion + + private void Form1_Load(object sender, EventArgs e) + { + IPEndPoint localEP = new IPEndPoint(System.Net.IPAddress.Any, 0); + + // + // create the socket and bind to INADDR_ANY + // + socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Bind(localEP); + localEP = (IPEndPoint) socket.LocalEndPoint; + + // + // start asynchronous read + // + SocketStateObject so = new SocketStateObject(socket); + socket.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(this.OnReadSocket), so); + + try + { + // + // start the register and browse operations + // + registrar = DNSService.Register(0, 0, System.Environment.UserName, "_p2pchat._udp", null, null, localEP.Port, null, new DNSService.RegisterReply(OnRegisterReply)); + browser = DNSService.Browse(0, 0, "_p2pchat._udp", null, new DNSService.BrowseReply(OnBrowseReply)); + } + catch + { + MessageBox.Show("DNSServices Not Available", "Error"); + Application.Exit(); + } + } + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.Run(new Form1()); + } + + // + // send the message to a peer + // + private void button1_Click(object sender, System.EventArgs e) + { + PeerData peer = (PeerData) comboBox1.SelectedItem; + + String message = myName + ": " + textBox2.Text; + + Byte[] bytes = Encoding.UTF8.GetBytes(message); + + UdpClient udpSocket = new UdpClient(peer.Address.ToString(), peer.Port); + + udpSocket.Send(bytes, bytes.Length); + + richTextBox1.SelectionColor = Color.Black; + + richTextBox1.AppendText(textBox2.Text + "\n"); + + textBox2.Text = ""; + } + + // + // called when typing in message box + // + private void textBox2_TextChanged(object sender, System.EventArgs e) + { + PeerData peer = (PeerData) comboBox1.SelectedItem; + + if ((peer.Address != null) && (textBox2.Text.Length > 0)) + { + button1.Enabled = true; + } + else + { + button1.Enabled = false; + } + } + + // + // called when peer target changes + // + /// + /// + /// + /// + private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e) + { + PeerData peer = (PeerData) comboBox1.SelectedItem; + + try + { + resolver = DNSService.Resolve(0, 0, peer.Name, peer.Type, peer.Domain, new DNSService.ResolveReply(OnResolveReply)); + } + catch + { + MessageBox.Show("Unable to Resolve service", "Error"); + Application.Exit(); + } + } + } +} diff --git a/Clients/SimpleChat.NET/SimpleChat.resx b/Clients/SimpleChat.NET/SimpleChat.resx new file mode 100755 index 0000000..e5b5a11 --- /dev/null +++ b/Clients/SimpleChat.NET/SimpleChat.resx @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Form1 + + \ No newline at end of file diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c index 1fb02d0..17c4a76 100644 --- a/Clients/dns-sd.c +++ b/Clients/dns-sd.c @@ -1,26 +1,40 @@ /* * 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@ + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Formatting notes: * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion @@ -35,42 +49,39 @@ * 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 +To build this tool, copy and paste the following into a command line: -Revision 1.5 2004/05/21 17:39:27 cheshire -Include extra headers to fix Xcode build +OS X: +gcc dns-sd.c -o dns-sd -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 +POSIX systems: +gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd +Windows: +cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT -DNOT_HAVE_SETLINEBUF ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib +(may require that you run a Visual Studio script such as vsvars32.bat first) */ +#include "dns_sd.h" +#include #include // For stdout, stderr #include // For exit() -#include // For strlen(), strcpy(), bzero() -#include // For getopt() and optind +#include // For strlen(), strcpy(), bzero() #include // For errno, EINTR -#define BIND_8_COMPAT -#include // For T_HINFO, etc. +#include + +#ifdef _WIN32 +#include +typedef int pid_t; +#define getpid _getpid +#define strcasecmp _stricmp +#define snprintf _snprintf +#else #include // For struct timeval -#include +#include // For getopt() and optind +#include // For inet_addr() +#endif + //************************************************************************************************************* // Globals @@ -78,76 +89,199 @@ Check in code to make command-line "dns-sd" testing tool typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; static int operation; -static DNSServiceRef client = NULL; +static DNSServiceRef client = NULL; +static DNSServiceRef client2 = NULL; static int num_printed; static char addtest = 0; static DNSRecordRef record = NULL; -static char myhinfo9[11] = "\003Mac\006OS 9.2"; +static char myhinfoW[14] = "\002PC\012Windows XP"; static char myhinfoX[ 9] = "\003Mac\004OS X"; static char updatetest[3] = "\002AA"; static char bigNULL[4096]; -#define LONG_TIME 0x4000000 +// Note: the select() implementation on Windows (Winsock2) fails with any timeout much larger than this +#define LONG_TIME 100000000 + static volatile int stopNow = 0; static volatile int timeOut = LONG_TIME; //************************************************************************************************************* // Supporting Utility Function +static uint16_t GetRRType(const char *s) + { + if (!strcasecmp(s, "A" )) return(kDNSServiceType_A); + else if (!strcasecmp(s, "NS" )) return(kDNSServiceType_NS); + else if (!strcasecmp(s, "MD" )) return(kDNSServiceType_MD); + else if (!strcasecmp(s, "MF" )) return(kDNSServiceType_MF); + else if (!strcasecmp(s, "CNAME" )) return(kDNSServiceType_CNAME); + else if (!strcasecmp(s, "SOA" )) return(kDNSServiceType_SOA); + else if (!strcasecmp(s, "MB" )) return(kDNSServiceType_MB); + else if (!strcasecmp(s, "MG" )) return(kDNSServiceType_MG); + else if (!strcasecmp(s, "MR" )) return(kDNSServiceType_MR); + else if (!strcasecmp(s, "NULL" )) return(kDNSServiceType_NULL); + else if (!strcasecmp(s, "WKS" )) return(kDNSServiceType_WKS); + else if (!strcasecmp(s, "PTR" )) return(kDNSServiceType_PTR); + else if (!strcasecmp(s, "HINFO" )) return(kDNSServiceType_HINFO); + else if (!strcasecmp(s, "MINFO" )) return(kDNSServiceType_MINFO); + else if (!strcasecmp(s, "MX" )) return(kDNSServiceType_MX); + else if (!strcasecmp(s, "TXT" )) return(kDNSServiceType_TXT); + else if (!strcasecmp(s, "RP" )) return(kDNSServiceType_RP); + else if (!strcasecmp(s, "AFSDB" )) return(kDNSServiceType_AFSDB); + else if (!strcasecmp(s, "X25" )) return(kDNSServiceType_X25); + else if (!strcasecmp(s, "ISDN" )) return(kDNSServiceType_ISDN); + else if (!strcasecmp(s, "RT" )) return(kDNSServiceType_RT); + else if (!strcasecmp(s, "NSAP" )) return(kDNSServiceType_NSAP); + else if (!strcasecmp(s, "NSAP_PTR")) return(kDNSServiceType_NSAP_PTR); + else if (!strcasecmp(s, "SIG" )) return(kDNSServiceType_SIG); + else if (!strcasecmp(s, "KEY" )) return(kDNSServiceType_KEY); + else if (!strcasecmp(s, "PX" )) return(kDNSServiceType_PX); + else if (!strcasecmp(s, "GPOS" )) return(kDNSServiceType_GPOS); + else if (!strcasecmp(s, "AAAA" )) return(kDNSServiceType_AAAA); + else if (!strcasecmp(s, "LOC" )) return(kDNSServiceType_LOC); + else if (!strcasecmp(s, "NXT" )) return(kDNSServiceType_NXT); + else if (!strcasecmp(s, "EID" )) return(kDNSServiceType_EID); + else if (!strcasecmp(s, "NIMLOC" )) return(kDNSServiceType_NIMLOC); + else if (!strcasecmp(s, "SRV" )) return(kDNSServiceType_SRV); + else if (!strcasecmp(s, "ATMA" )) return(kDNSServiceType_ATMA); + else if (!strcasecmp(s, "NAPTR" )) return(kDNSServiceType_NAPTR); + else if (!strcasecmp(s, "KX" )) return(kDNSServiceType_KX); + else if (!strcasecmp(s, "CERT" )) return(kDNSServiceType_CERT); + else if (!strcasecmp(s, "A6" )) return(kDNSServiceType_A6); + else if (!strcasecmp(s, "DNAME" )) return(kDNSServiceType_DNAME); + else if (!strcasecmp(s, "SINK" )) return(kDNSServiceType_SINK); + else if (!strcasecmp(s, "OPT" )) return(kDNSServiceType_OPT); + else if (!strcasecmp(s, "TKEY" )) return(kDNSServiceType_TKEY); + else if (!strcasecmp(s, "TSIG" )) return(kDNSServiceType_TSIG); + else if (!strcasecmp(s, "IXFR" )) return(kDNSServiceType_IXFR); + else if (!strcasecmp(s, "AXFR" )) return(kDNSServiceType_AXFR); + else if (!strcasecmp(s, "MAILB" )) return(kDNSServiceType_MAILB); + else if (!strcasecmp(s, "MAILA" )) return(kDNSServiceType_MAILA); + else if (!strcasecmp(s, "ANY" )) return(kDNSServiceType_ANY); + else return(atoi(s)); + } + //************************************************************************************************************* // Sample callback functions for each of the operation types static void printtimestamp(void) { - struct timeval tv; struct tm tm; + int ms; +#ifdef _WIN32 + SYSTEMTIME sysTime; + time_t uct = time(NULL); + tm = *localtime(&uct); + GetLocalTime(&sysTime); + ms = sysTime.wMilliseconds; +#else + struct timeval tv; 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); + ms = tv.tv_usec/1000; +#endif + printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); } #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) +static const char *GetNextLabel(const char *cstr, char label[64]) { - (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"); + char *ptr = label; + while (*cstr && *cstr != '.') // While we have characters in the label... + { + char c = *cstr++; + if (c == '\\') + { + c = *cstr++; + if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1])) + { + 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 = (char)val; cstr += 2; } // If valid three-digit decimal value, use it + } + } + *ptr++ = c; + if (ptr >= label+64) return(NULL); + } + if (*cstr) cstr++; // Skip over the trailing dot (if present) + *ptr++ = 0; + return(cstr); } -static void browsedom_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, +static void DNSSD_API enum_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context) { + int labels = 0, depth = 0, i, initial = 0; + char text[64]; + const char *label[128]; + (void)client; // Unused - (void)interface; // Unused + (void)ifIndex; // Unused (void)errorCode; // Unused (void)context; // Unused + + if (!*replyDomain) return; + + // 1. Print the header + if (num_printed++ == 0) printf("Timestamp Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing"); printtimestamp(); - printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(flags)); - if (flags) printf(" Flags: %X", flags); + printf("%-10s", DomainMsg(flags)); + printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : ""); + flags &= ~kDNSServiceFlagsMoreComing; + flags &= ~kDNSServiceFlagsAdd; + flags &= ~kDNSServiceFlagsDefault; + if (flags) printf("Flags: %4X ", flags); + else printf(" "); + + // 2. Count the labels + while (*replyDomain) + { + label[labels++] = replyDomain; + replyDomain = GetNextLabel(replyDomain, text); + } + + // 3. Decide if we're going to clump the last two or three labels (e.g. "apple.com", or "nicta.com.au") + if (labels >= 3 && replyDomain - label[labels-1] <= 3 && label[labels-1] - label[labels-2] <= 4) initial = 3; + else if (labels >= 2 && replyDomain - label[labels-1] <= 4) initial = 2; + else initial = 1; + labels -= initial; + + // 4. Print the initial one-, two- or three-label clump + for (i=0; i0) printf("."); + printf("%s", text); + } printf("\n"); + + // 5. Print the remainder of the hierarchy + for (depth=0; depth %s\n", text); + } } -static void browse_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, +static void DNSSD_API browse_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, 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); + printf("%s%6X%3d %-24s %-24s %s\n", op, flags, ifIndex, replyDomain, replyType, replyName); } -static void resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, +static void DNSSD_API resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const char *txtRecord, void *context) { const char *src = txtRecord; @@ -155,8 +289,9 @@ static void resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; (void)client; // Unused - (void)interface; // Unused + (void)ifIndex; // Unused (void)errorCode; // Unused + (void)txtLen; // Unused (void)context; // Unused printtimestamp(); @@ -195,7 +330,7 @@ static void resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t static void myTimerCallBack(void) { - DNSServiceErrorType err; + DNSServiceErrorType err = kDNSServiceErr_Unknown; switch (operation) { @@ -204,11 +339,11 @@ static void myTimerCallBack(void) switch (addtest) { case 0: printf("Adding Test HINFO record\n"); - err = DNSServiceAddRecord(client, &record, 0, T_HINFO, sizeof(myhinfo9), &myhinfo9[0], 120); + err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_HINFO, sizeof(myhinfoW), &myhinfoW[0], 0); addtest = 1; break; case 1: printf("Updating Test HINFO record\n"); - err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 120); + err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 0); addtest = 2; break; case 2: printf("Removing Test HINFO record\n"); @@ -226,14 +361,14 @@ static void myTimerCallBack(void) 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); + err = DNSServiceUpdateRecord(client, NULL, 0, 1+updatetest[0], &updatetest[0], 0); } break; case 'N': { printf("Adding big NULL record\n"); - err = DNSServiceAddRecord(client, &record, 0, T_NULL, sizeof(bigNULL), &bigNULL[0], 120); + err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0); timeOut = LONG_TIME; } break; @@ -246,7 +381,7 @@ static void myTimerCallBack(void) } } -static void reg_reply(DNSServiceRef client, DNSServiceFlags flags, DNSServiceErrorType errorCode, +static void DNSSD_API reg_reply(DNSServiceRef client, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { (void)client; // Unused @@ -264,17 +399,33 @@ static void reg_reply(DNSServiceRef client, DNSServiceFlags flags, DNSServiceErr if (operation == 'A' || operation == 'U' || operation == 'N') timeOut = 5; } -void qr_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, +static void DNSSD_API qr_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t ifIndex, 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 *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; + const unsigned char *rd = rdata; + const unsigned char *end = (const unsigned char *) rdata + rdlen; char rdb[1000]; + char *p = rdb; + const char * const lim = rdb + sizeof(rdb); + + (void)sdRef; // Unused + (void)flags; // Unused + (void)ifIndex; // Unused + (void)errorCode;// Unused + (void)ttl; // Unused + (void)context; // Unused + 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; + case kDNSServiceType_A: sprintf(rdb, "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); break; + default : p += snprintf(p, lim-p, "%d bytes%s", rdlen, rdlen ? ":" : ""); + while (rd < end && p < lim) p += snprintf(p, lim-p, " %02X", *rd++); + break; } - printf("%-30s%4d%4d %s\n", fullname, rrtype, rrclass, rdb); + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C"); + printtimestamp(); + printf("%s%6X%3d %-30s%4d%4d %s\n", op, flags, ifIndex, fullname, rrtype, rrclass, rdb); } //************************************************************************************************************* @@ -282,11 +433,14 @@ void qr_reply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceInde static void HandleEvents(void) { - int dns_sd_fd = DNSServiceRefSockFD(client); + int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1; + int dns_sd_fd2 = client2 ? DNSServiceRefSockFD(client2) : -1; int nfds = dns_sd_fd + 1; fd_set readfds; struct timeval tv; int result; + + if (dns_sd_fd2 > dns_sd_fd) nfds = dns_sd_fd2 + 1; while (!stopNow) { @@ -296,7 +450,8 @@ static void HandleEvents(void) FD_ZERO(&readfds); // 2. Add the fd for our client(s) to the fd_set - FD_SET(dns_sd_fd, &readfds); + if (client ) FD_SET(dns_sd_fd , &readfds); + if (client2) FD_SET(dns_sd_fd2, &readfds); // 3. Set up the timeout. tv.tv_sec = timeOut; @@ -305,7 +460,10 @@ static void HandleEvents(void) result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); if (result > 0) { - if (FD_ISSET(dns_sd_fd, &readfds)) DNSServiceProcessResult(client); + DNSServiceErrorType err = kDNSServiceErr_NoError; + if (client && FD_ISSET(dns_sd_fd , &readfds)) err = DNSServiceProcessResult(client ); + else if (client2 && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client2); + if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; } } else if (result == 0) myTimerCallBack(); @@ -317,24 +475,114 @@ static void HandleEvents(void) } } +static int getfirstoption( int argc, char **argv, const char *optstr, int *pOptInd) +// Return the recognized option in optstr and the option index of the next arg. +#if NOT_HAVE_GETOPT + { + int i; + for ( i=1; i < argc; i++) + { + if ( argv[i][0] == '-' && &argv[i][1] && + NULL != strchr( optstr, argv[i][1])) + { + *pOptInd = i + 1; + return argv[i][1]; + } + } + return -1; + } +#else + { + int operation = getopt(argc, (char * const *)argv, optstr); + *pOptInd = optind; + return operation; + } +#endif + +static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef record, DNSServiceFlags flags, + DNSServiceErrorType errorCode, void * context) + { + char *name = (char *)context; + + (void)service; // Unused + (void)record; // Unused + (void)flags; // Unused + + printf("Got a reply for %s: ", name); + 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; + } + } + +static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef *sdRef, const char *host, const char *ip) + { + unsigned long addr = inet_addr(ip); + DNSServiceErrorType err = DNSServiceCreateConnection(sdRef); + if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } + return(DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, kDNSServiceInterfaceIndexAny, host, + kDNSServiceType_A, kDNSServiceClass_IN, sizeof(addr), &addr, 240, MyRegisterRecordCallback, (void*)host)); + } + +static DNSServiceErrorType RegisterService(DNSServiceRef *sdRef, + const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv) + { + uint16_t PortAsNumber = atoi(port); + Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; + char txt[2048] = ""; + char *ptr = txt; + int i; + + if (nam[0] == '.' && nam[1] == 0) nam = ""; // We allow '.' on the command line as a synonym for empty string + if (dom[0] == '.' && dom[1] == 0) dom = ""; // We allow '.' on the command line as a synonym for empty string + + for (i = 0; i < argc; i++) + { + char *len = ptr++; + *len = strlen(argv[i]); + strcpy(ptr, argv[i]); + ptr += *len; + } + + printf("Registering Service %s.%s%s", nam, typ, dom); + if (host && *host) printf(" host %s", host); + printf(" port %s %s\n", port, txt); + return(DNSServiceRegister(sdRef, /* kDNSServiceFlagsAllowRemoteQuery */ 0, 0, nam, typ, dom, host, registerPort.NotAnInteger, ptr-txt, txt, reg_reply, NULL)); + } + int main(int argc, char **argv) { +#ifdef _WIN32 + const char kFilePathSep = '\\'; +#else + const char kFilePathSep = '/'; +#endif DNSServiceErrorType err; char *dom; + int optind; + const char *progname = strrchr(argv[0], kFilePathSep) ? strrchr(argv[0], kFilePathSep) + 1 : argv[0]; +#ifndef NOT_HAVE_SETLINEBUF setlinebuf(stdout); // Want to see lines as they appear, not block buffered +#endif if (argc < 2) goto Fail; // Minimum command line is the command name and one argument - operation = getopt(argc, (char * const *)argv, "EFBLQRAUNTMI"); + operation = getfirstoption( argc, argv, "EFBLQRPAUNTMI", &optind); if (operation == -1) goto Fail; switch (operation) { case 'E': printf("Looking for recommended registration domains:\n"); - err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, 0, regdom_reply, NULL); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, 0, enum_reply, NULL); break; case 'F': printf("Looking for recommended browsing domains:\n"); - err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, 0, browsedom_reply, NULL); + err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, 0, enum_reply, NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "nicta.com.au.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "rendezvous.nicta.com.au.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "ibm.com.", NULL); + //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "dns-sd.ibm.com.", NULL); break; case 'B': if (argc < optind+1) goto Fail; @@ -352,37 +600,23 @@ int main(int argc, char **argv) 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; - } + err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], NULL, argv[optind+3], argc-(optind+4), argv+(optind+4)); + break; - 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); + case 'P': if (argc < optind+6) goto Fail; + err = RegisterProxyAddressRecord(&client2, argv[optind+4], argv[optind+5]); + if (err) break; + err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], argv[optind+4], argv[optind+3], argc-(optind+6), argv+(optind+6)); break; - } case 'Q': { + uint16_t rrtype, rrclass; + DNSServiceFlags flags = 0; 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); + rrtype = (argc <= optind+1) ? kDNSServiceType_A : GetRRType(argv[optind+1]); + rrclass = (argc <= optind+2) ? kDNSServiceClass_IN : atoi(argv[optind+2]); + if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery; + err = DNSServiceQueryRecord(&client, flags, 0, argv[optind+0], rrtype, rrclass, qr_reply, NULL); break; } @@ -414,7 +648,7 @@ int main(int argc, char **argv) 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); + if (!err) err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0); break; } @@ -424,7 +658,7 @@ int main(int argc, char **argv) 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); + if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0); break; } @@ -435,21 +669,23 @@ int main(int argc, char **argv) HandleEvents(); // Be sure to deallocate the DNSServiceRef when you're finished - DNSServiceRefDeallocate(client); + if (client ) DNSServiceRefDeallocate(client ); + if (client2) DNSServiceRefDeallocate(client2); 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]); + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", progname); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", progname); + fprintf(stderr, "%s -B (Browse for services instances)\n", progname); + fprintf(stderr, "%s -L (Look up a service instance)\n", progname); + fprintf(stderr, "%s -R [...] (Register a service)\n", progname); + fprintf(stderr, "%s -P [...] (Proxy)\n", progname); + fprintf(stderr, "%s -Q (Generic query for any record type)\n", progname); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", progname); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", progname); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", progname); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", progname); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", progname); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", progname); return 0; } diff --git a/Makefile b/Makefile index 6535aa1..20adec5 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ include /Developer/Makefiles/pb_makefiles/platform.make -MVERS = "mDNSResponder-66.3" +MVERS = "mDNSResponder-87" install: cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) diff --git a/README.txt b/README.txt index fa70ecf..5f17866 100644 --- a/README.txt +++ b/README.txt @@ -141,3 +141,26 @@ 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. + + +Compiling on Older C Compilers +------------------------------ + +We go to some lengths to make the code portable, but //-style comments +are one of the modern conveniences we can't live without. + +If your C compiler doesn't understand these comments, you can transform +them into classical K&R /* style */ comments with a quick GREP +search-and-replace pattern. + +In BBEdit on the Mac: +1. Open the "Find" dialog window and make sure "Use Grep" is selected +2. Search For : ([^:])//(.*) +3. Replace With: \1/*\2 */ +4. Drag your mDNSResponder source code folder to the Multi-File search pane +5. Click "Replace All" + +For the more command-line oriented, cd into your mDNSResponder source code +directory and execute the following command (all one line): + +find mDNSResponder \( -name \*.c\* -or -name \*.h \) -exec sed -i .orig -e 's,^//\(.*\),/*\1 */,' -e '/\/\*/\!s,\([^:]\)//\(.*\),\1/*\2 */,' {} \; diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c index 6e317a9..13319c1 100644 --- a/mDNSCore/DNSCommon.c +++ b/mDNSCore/DNSCommon.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,135 @@ Change History (most recent first): $Log: DNSCommon.c,v $ +Revision 1.74 2004/12/09 22:49:15 ksekar + Wide-Area Goodbyes broken + +Revision 1.73 2004/12/07 22:49:06 cheshire + BIND doesn't like zero-length rdata + +Revision 1.72 2004/12/06 21:15:20 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.71 2004/12/04 02:12:45 cheshire + mDNSResponder puts LargeCacheRecord on the stack + +Revision 1.70 2004/12/03 19:52:44 ksekar +Use PutResourceRecordTTLJumbo for putDeletionRecord() + +Revision 1.69 2004/12/03 07:20:50 ksekar + Wide-Area: Registration of large TXT record fails + +Revision 1.68 2004/11/24 00:10:43 cheshire + For unicast operations, verify that service types are legal + +Revision 1.67 2004/10/26 03:52:02 cheshire +Update checkin comments + +Revision 1.66 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.65 2004/10/20 02:15:09 cheshire +Add case in GetRRDisplayString() to display NS rdata + +Revision 1.64 2004/10/13 00:24:02 cheshire +Disable "array is too small to include a terminating null character" warning on Windows + +Revision 1.63 2004/10/10 06:57:14 cheshire +Change definition of "localdomain" to make code compile a little smaller + +Revision 1.62 2004/10/06 01:44:19 cheshire + Resolving too quickly sometimes returns stale TXT record + +Revision 1.61 2004/09/30 00:24:56 ksekar + Dynamically update default registration domains on config change + +Revision 1.60 2004/09/27 23:25:30 cheshire +Fix compiler warning: soa.serial is signed, not unsigned + +Revision 1.59 2004/09/27 22:53:45 ksekar +Fixed getLargeResourceRecord for SOA rdata. + +Revision 1.58 2004/09/25 02:41:39 cheshire + Deliver near-pending "remove" events before new "add" events + +Revision 1.57 2004/09/25 02:24:27 cheshire +Removed unused rr->UseCount + +Revision 1.56 2004/09/24 20:57:39 cheshire + Eliminate inappropriate casts that cause misaligned-address errors + +Revision 1.55 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.54 2004/09/17 00:49:51 cheshire +Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use +is GetLargeResourceRecord + +Revision 1.53 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.52 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.51 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.50 2004/09/16 01:58:14 cheshire +Fix compiler warnings + +Revision 1.49 2004/09/14 23:42:35 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.48 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.47 2004/08/25 02:50:04 cheshire + Browses are no longer piggybacking on other browses +Make mDNSSameAddress() recognise that two mDNSAddrType_None addresses are necessarily equal + +Revision 1.46 2004/08/18 17:35:40 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.45 2004/08/15 18:26:00 cheshire +Don't use strcpy() on "struct domainname" objects; use AssignDomainName() instead +(A "struct domainname" is a collection of packed pascal strings, not a C string.) + +Revision 1.44 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.43 2004/08/12 02:55:46 ksekar +Fix param order error moving putPrereqNameNotInUse from uDNS.c using +ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy(). + +Revision 1.42 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.41 2004/08/10 01:10:01 cheshire + Current method of doing subtypes causes name collisions +Minor revision from Roger Pantos + +Revision 1.40 2004/08/04 22:10:46 cheshire + Current method of doing subtypes causes name collisions +Change to use "._sub." instead of ".s." to mark subtypes. + +Revision 1.39 2004/07/13 21:24:24 rpantos +Fix for . + +Revision 1.38 2004/06/18 21:08:58 cheshire + Applications are registering invalid records +Attempts to create domain names like "www..apple.com." now logged to aid debugging + +Revision 1.37 2004/06/18 20:25:42 cheshire + Add a syslog message if someone tries to use "local.arpa". + +Revision 1.36 2004/06/18 19:09:59 cheshire + Current method of doing subtypes causes name collisions + Revision 1.35 2004/06/05 00:14:44 cheshire Fix signed/unsigned and other compiler warnings @@ -105,9 +232,9 @@ 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 +Added an asynchronous 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 +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 @@ -142,7 +269,7 @@ Revision 1.1 2003/12/13 03:05:27 ksekar */ -// Set mDNS_InstantiateInlines to tell mDNSClientAPI.h to instantiate inline functions, if necessary +// Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary #define mDNS_InstantiateInlines 1 #include "DNSCommon.h" @@ -152,6 +279,9 @@ Revision 1.1 2003/12/13 03:05:27 ksekar // Otherwise, this generates warnings for the perfectly natural construct "while(1)" // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know #pragma warning(disable:4127) + // Disable "array is too small to include a terminating null character" warning + // -- domain labels have an initial length byte, not a terminating null character + #pragma warning(disable:4295) #endif // *************************************************************************** @@ -169,7 +299,7 @@ mDNSexport DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig) { newelem = (DNameListElem*)mDNSPlatformMemAllocate(sizeof(DNameListElem)); if (!newelem) { LogMsg("ERROR: malloc"); return mDNSNULL; } - mDNSPlatformStrCopy(ptr->name.c, newelem->name.c); + AssignDomainName(newelem->name, ptr->name); newelem->next = copy; copy = newelem; } @@ -194,6 +324,19 @@ mDNSexport void mDNS_FreeDNameList(DNameListElem *list) #pragma mark - General Utility Functions #endif +// return true for RFC1918 private addresses +mDNSexport mDNSBool IsPrivateV4Addr(mDNSAddr *addr) + { + mDNSu8 *b; + + if (addr->type != mDNSAddrType_IPv4) return mDNSfalse; + b = addr->ip.v4.b; + + return ((b[0] == 10) || // 10/8 prefix + (b[0] == 172 && b[1] > 15 && b[1] < 32) || // 172.16/12 + (b[0] == 192 && b[1] == 168)); // 192.168/16 + } + mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf) { while (intf && !intf->InterfaceActive) intf = intf->next; @@ -237,24 +380,27 @@ mDNSexport char *DNSTypeName(mDNSu16 rrtype) } } -mDNSexport char *GetRRDisplayString_rdb(mDNS *const m, const ResourceRecord *rr, RDataBody *rd) +mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer) { - char *ptr = m->MsgBuffer; - mDNSu32 length = mDNS_snprintf(m->MsgBuffer, 79, "%4d %##s %s ", rr->rdlength, rr->name.c, DNSTypeName(rr->rrtype)); + char *ptr = buffer; + mDNSu32 length = mDNS_snprintf(buffer, 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_A: mDNS_snprintf(buffer+length, 79-length, "%.4a", &rd->ipv4); break; + + case kDNSType_NS: // Same as PTR case kDNSType_CNAME:// Same as PTR - case kDNSType_PTR: mDNS_snprintf(m->MsgBuffer+length, 79-length, "%##s", &rd->name); break; + case kDNSType_PTR: mDNS_snprintf(buffer+length, 79-length, "%##s", rd->name.c); 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; + case kDNSType_TXT: mDNS_snprintf(buffer+length, 79-length, "%#s", rd->txt.c); break; + + case kDNSType_AAAA: mDNS_snprintf(buffer+length, 79-length, "%.16a", &rd->ipv6); break; + case kDNSType_SRV: mDNS_snprintf(buffer+length, 79-length, "%##s", rd->srv.target.c); break; + default: mDNS_snprintf(buffer+length, 79-length, "RDLen %d: %s", rr->rdlength, rd->data); break; } - for (ptr = m->MsgBuffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.'; - return(m->MsgBuffer); + for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.'; + return(buffer); } mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) @@ -262,7 +408,12 @@ mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) static mDNSu32 seed = 0; mDNSu32 mask = 1; - if (!seed) seed = (mDNSu32)mDNSPlatformTimeNow(); + if (!seed) + { + int i; + seed = mDNSPlatformRandomSeed(); // Pick an initial seed + for (i=0; i<100; i++) seed = seed * 21 + 1; // And mix it up a bit + } while (mask < max) mask = (mask << 1) | 1; do seed = seed * 21 + 1; while ((seed & mask) > max); return (seed & mask); @@ -274,6 +425,7 @@ mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2) { switch (ip1->type) { + case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4)); case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6)); } @@ -285,7 +437,7 @@ mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip) { switch(ip->type) { - case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroup.NotAnInteger); + case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroupv4.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] && @@ -429,13 +581,15 @@ mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char * // 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) +mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring) { + const char *cstr = cstring; 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 + if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); } while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label... { mDNSu8 c = (mDNSu8)*cstr++; // Read the character @@ -605,15 +759,6 @@ mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], do 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) { @@ -624,15 +769,31 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, // 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) + if (!name && type) { - 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) + const mDNSu8 *s0 = type->c; + if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63) { - name = (domainlabel *)type; - type = (domainname *)s2; + 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); + } + } } } @@ -648,13 +809,11 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, 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 (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] != '_') @@ -665,13 +824,15 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, 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; } + { 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; if (!domain->c[0]) { errormsg="Service domain must be non-empty"; goto fail; } + if (SameDomainName(domain, (domainname*)"\x05" "local" "\x04" "arpa")) + { errormsg="Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; } dst = AppendDomainName(fqdn, domain); if (!dst) { errormsg="Service domain too long"; goto fail; } return(dst); @@ -864,6 +1025,14 @@ mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceReco } } +mDNSexport mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2) + { + return (r1->namehash == r2->namehash && + r1->rrtype == r2->rrtype && + SameDomainName(&r1->name, &r2->name) && + SameRData(r1, r2)); + } + mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) { if (rr->InterfaceID && @@ -878,11 +1047,11 @@ mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) { - RDataBody *rd = &rr->rdata->u; + const 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_A: return(sizeof(rd->ipv4)); case kDNSType_CNAME:// Same as PTR case kDNSType_NS: // Same as PTR case kDNSType_PTR: return(CompressedDomainNameLength(&rd->name, name)); @@ -903,6 +1072,11 @@ mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd) { mDNSu16 len; + // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates + // with zero-length rdata, so for consistency we don't allow them for mDNS either. + // Otherwise we risk having applications that work with mDNS but not with uDNS. + if (!rdlength) return(mDNSfalse); + switch(rrtype) { case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); @@ -1067,6 +1241,15 @@ mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val) return ptr + sizeof(mDNSOpaque16); } +mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val) + { + ptr[0] = (mDNSu8)((val >> 24) & 0xFF); + ptr[1] = (mDNSu8)((val >> 16) & 0xFF); + ptr[2] = (mDNSu8)((val >> 8) & 0xFF); + ptr[3] = (mDNSu8)((val ) & 0xFF); + return ptr + sizeof(mDNSu32); + } + mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr) { int nput = 0; @@ -1088,15 +1271,13 @@ mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord * 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); + ptr = putVal32(ptr, opt->OptData.llq.lease); 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); + ptr = putVal32(ptr, opt->OptData.lease); nput += sizeof(mDNSs32); } else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; } @@ -1174,10 +1355,10 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS 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]; + *ptr++ = rr->rdata->u.ipv4.b[0]; + *ptr++ = rr->rdata->u.ipv4.b[1]; + *ptr++ = rr->rdata->u.ipv4.b[2]; + *ptr++ = rr->rdata->u.ipv4.b[3]; return(ptr); case kDNSType_CNAME:// Same as PTR @@ -1212,16 +1393,10 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS } } -mDNSexport mDNSu8 *PutResourceRecordTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl) +mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit) { 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) { @@ -1287,6 +1462,90 @@ mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 return(ptr+4); } +// for dynamic updates +mDNSexport 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 mDNSNULL; // If we're out-of-space, return NULL + *ptr++ = (mDNSu8)(kDNSType_SOA >> 8); + *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF); + *ptr++ = zoneClass.b[0]; + *ptr++ = zoneClass.b[1]; + msg->h.mDNS_numZones++; + return ptr; + } + +// for dynamic updates +mDNSexport mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end) + { + AuthRecord prereq; + + mDNSPlatformMemZero(&prereq, sizeof(AuthRecord)); + AssignDomainName(prereq.resrec.name, *name); + prereq.resrec.rrtype = kDNSQType_ANY; + prereq.resrec.rrclass = kDNSClass_NONE; + ptr = putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq); + return ptr; + } + +// for dynamic updates +mDNSexport 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 = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0); + rr->rrclass = origclass; + return ptr; + } + +// for dynamic updates +mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name) + { + const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;; + mDNSu16 class = kDNSQClass_ANY; + mDNSu16 rrtype = kDNSQType_ANY; + + ptr = putDomainNameAsLabels(msg, ptr, limit, name); + if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL + ptr[0] = (mDNSu8)(rrtype >> 8); + ptr[1] = (mDNSu8)(rrtype & 0xFF); + ptr[2] = (mDNSu8)(class >> 8); + ptr[3] = (mDNSu8)(class & 0xFF); + ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl + ptr[8] = ptr[9] = 0; // zero rdlength/rdata + + msg->h.mDNS_numUpdates++; + return ptr + 10; + } + +// for dynamic updates +mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease) + { + AuthRecord rr; + ResourceRecord *opt = &rr.resrec; + rdataOpt *optRD; + + mDNSPlatformMemZero(&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 = lease; + end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, opt, 0); + if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; } + + return end; + } + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -1418,19 +1677,23 @@ mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 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) +mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, + const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *largecr) { + CacheRecord *rr = &largecr->r; mDNSu16 pktrdlength; + + if (largecr == &m->rec && rr->resrec.RecordType) + LogMsg("GetLargeResourceRecord: m->rec appears to be already in use"); 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->TimeRcvd = m ? m->timenow : 0; + rr->DelayDelivery = 0; + rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTime() + rr->LastUsed = m ? m->timenow : 0; rr->CRActiveQuestion = mDNSNULL; rr->UnansweredQueries = 0; rr->LastUnansweredTime= 0; @@ -1458,21 +1721,19 @@ mDNSexport const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage * con rr->resrec.RecordType |= kDNSRecordTypePacketUniqueMask; ptr += 10; if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } + end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record - if (RDataStorage) - rr->resrec.rdata = RDataStorage; - else - { - rr->resrec.rdata = (RData*)&rr->rdatastorage; - rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); - } + rr->resrec.rdata = (RData*)&rr->rdatastorage; + rr->resrec.rdata->MaxRDLength = MaximumRDSize; + + if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name.c); 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]; + case kDNSType_A: rr->resrec.rdata->u.ipv4.b[0] = ptr[0]; + rr->resrec.rdata->u.ipv4.b[1] = ptr[1]; + rr->resrec.rdata->u.ipv4.b[2] = ptr[2]; + rr->resrec.rdata->u.ipv4.b[3] = ptr[3]; break; case kDNSType_CNAME:// Same as PTR @@ -1506,16 +1767,16 @@ mDNSexport const mDNSu8 *GetResourceRecord(mDNS *const m, const DNSMessage * con //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; + case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname); + if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL; } + ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname); + if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL; } + if (ptr + 0x14 != end) { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL; } + rr->resrec.rdata->u.soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]); + rr->resrec.rdata->u.soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]); + rr->resrec.rdata->u.soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]); + rr->resrec.rdata->u.soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]); + rr->resrec.rdata->u.soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); break; case kDNSType_OPT: getOptRdata(ptr, end, &rr->resrec, pktrdlength); break; @@ -1597,7 +1858,7 @@ mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mD #pragma mark - Packet Sending Functions #endif -mDNSlocal mStatus sendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, +mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo) { mStatus status; @@ -1654,31 +1915,96 @@ mDNSlocal mStatus sendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDN return(status); tcp_error: - LogMsg("sendDNSMessage: error sending message over tcp"); + LogMsg("mDNSSendDNSMessage: 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); - } +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - RR List Management & Task Management +#endif -mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, - mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport) +mDNSexport void mDNS_Lock(mDNS *const m) { - return sendDNSMessage(m, msg, end, InterfaceID, dst, dstport, -1, mDNSNULL); + // MUST grab the platform lock FIRST! + mDNSPlatformLock(m); + + // Normally, mDNS_reentrancy is zero and so is mDNS_busy + // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too + // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one + // If mDNS_busy != mDNS_reentrancy that's a bad sign + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // If this is an initial entry into the mDNSCore code, set m->timenow + // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set + if (m->mDNS_busy == 0) + { + if (m->timenow) + LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNSPlatformRawTime() + m->timenow_adjust); + m->timenow = mDNSPlatformRawTime() + m->timenow_adjust; + if (m->timenow == 0) m->timenow = 1; + } + else if (m->timenow == 0) + { + LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); + m->timenow = mDNSPlatformRawTime() + m->timenow_adjust; + if (m->timenow == 0) m->timenow = 1; + } + + if (m->timenow_last - m->timenow > 0) + { + m->timenow_adjust += m->timenow_last - m->timenow; + LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust); + m->timenow = m->timenow_last; + } + m->timenow_last = m->timenow; + + // Increment mDNS_busy so we'll recognise re-entrant calls + m->mDNS_busy++; } -mDNSexport mStatus mDNSSendSignedDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, - mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, uDNS_AuthInfo *authInfo) +mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) { - return sendDNSMessage(m, msg, end, InterfaceID, dst, dstport, -1, authInfo); + mDNSs32 e = m->timenow + 0x78000000; + if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) return(e); + if (m->NewQuestions) + { + if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering; + else return(m->timenow); + } + if (m->NewLocalOnlyQuestions) return(m->timenow); + if (m->NewLocalOnlyRecords) return(m->timenow); + if (m->DiscardLocalOnlyRecords) return(m->timenow); + if (m->SuppressSending) return(m->SuppressSending); +#ifndef UNICAST_DISABLED + if (e - m->uDNS_info.nextevent > 0) e = m->uDNS_info.nextevent; +#endif + if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; + if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; + if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; + if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; + return(e); } -mDNSexport mStatus mDNSSendSignedDNSMessage_tcp(const mDNS *const m, DNSMessage *const msg, mDNSu8 * end, int sd, uDNS_AuthInfo *authInfo) +mDNSexport void mDNS_Unlock(mDNS *const m) { - if (sd < 0) { LogMsg("mDNSSendDNSMessage_tcp: invalid desciptor %d", sd); return mStatus_UnknownErr; } - return sendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, authInfo); + // Decrement mDNS_busy + m->mDNS_busy--; + + // Check for locking failures + if (m->mDNS_busy != m->mDNS_reentrancy) + LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + + // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow + if (m->mDNS_busy == 0) + { + m->NextScheduledEvent = GetNextScheduledEvent(m); + if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero"); + m->timenow = 0; + } + + // MUST release the platform lock LAST! + mDNSPlatformUnlock(m); } diff --git a/mDNSCore/DNSCommon.h b/mDNSCore/DNSCommon.h index 674bb7a..5466501 100644 --- a/mDNSCore/DNSCommon.h +++ b/mDNSCore/DNSCommon.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,54 @@ Change History (most recent first): $Log: DNSCommon.h,v $ +Revision 1.28 2004/12/06 21:15:22 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.27 2004/12/03 07:20:50 ksekar + Wide-Area: Registration of large TXT record fails + +Revision 1.26 2004/12/03 05:18:33 ksekar + mDNSResponder needs to return more specific TSIG errors + +Revision 1.25 2004/10/26 03:52:02 cheshire +Update checkin comments + +Revision 1.24 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.23 2004/10/03 23:18:58 cheshire +Move address comparison macros from DNSCommon.h to mDNSEmbeddedAPI.h + +Revision 1.22 2004/09/30 00:24:56 ksekar + Dynamically update default registration domains on config change + +Revision 1.21 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.20 2004/09/17 00:49:51 cheshire +Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use +is GetLargeResourceRecord + +Revision 1.19 2004/09/16 21:59:15 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.18 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.17 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.16 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.15 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + Revision 1.14 2004/05/28 23:42:36 ksekar : Feature: DNS server->client notification on record changes (#7805) @@ -48,16 +94,16 @@ 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 +Added an asynchronous 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 +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 +Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSEmbeddedAPI.h so embedded 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" @@ -77,7 +123,7 @@ Revision 1.1 2003/12/13 03:05:27 ksekar #ifndef __DNSCOMMON_H_ #define __DNSCOMMON_H_ -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #ifdef __cplusplus extern "C" { @@ -127,6 +173,13 @@ typedef enum kDNSFlag1_RC_NotZone = 0x0A } DNS_Flags; +typedef enum + { + TSIG_ErrBadSig = 16, + TSIG_ErrBadKey = 17, + TSIG_ErrBadTime = 18 + } TSIG_ErrorCode; + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -138,41 +191,18 @@ 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); @@ -196,7 +226,8 @@ extern mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord * extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); - +extern mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2); + extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate); #define GetRRDomainNameTarget(RR) ( \ @@ -221,13 +252,32 @@ extern mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, mDNSu8 *ptr, c 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); +// 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 +extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit); + +#define PutResourceRecordTTL(msg, ptr, count, rr, ttl) PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), \ + ((msg)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? (msg)->data + NormalMaxDNSMessageData : (msg)->data + AbsoluteMaxDNSMessageData) + +#define PutResourceRecordTTLJumbo(msg, ptr, count, rr, ttl) PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), \ + (msg)->data + AbsoluteMaxDNSMessageData) + 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); + +extern mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass); + +extern mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end); + +extern mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr); + +extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name); + +extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease); #define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) @@ -250,8 +300,8 @@ extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *pt 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 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr, + const mDNSu8 * const end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *largecr); extern const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end); @@ -264,9 +314,6 @@ extern const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 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 - @@ -274,11 +321,17 @@ extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 #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); +extern mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, + mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo); + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - RR List Management & Task Management +#endif + +extern void mDNS_Lock(mDNS *const m); +extern void mDNS_Unlock(mDNS *const m); #ifdef __cplusplus } diff --git a/mDNSCore/DNSDigest.c b/mDNSCore/DNSDigest.c index 370e706..55e304c 100644 --- a/mDNSCore/DNSDigest.c +++ b/mDNSCore/DNSDigest.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,29 @@ Change History (most recent first): $Log: DNSDigest.c,v $ +Revision 1.12 2004/12/03 07:20:50 ksekar + Wide-Area: Registration of large TXT record fails + +Revision 1.11 2004/12/02 01:10:27 cheshire +Fix to compile cleanly on 64-bit x86 + +Revision 1.10 2004/11/01 20:36:04 ksekar + mDNSResponder should not receive Keychain Notifications + +Revision 1.9 2004/10/26 09:00:12 cheshire +Save a few bytes by creating HMAC_MD5_AlgName as a C string instead of a 256-byte object + +Revision 1.8 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.7 2004/08/15 18:36:38 cheshire +Don't use strcpy() and strlen() on "struct domainname" objects; +use AssignDomainName() and DomainNameLength() instead +(A "struct domainname" is a collection of packed pascal strings, not a C string.) + Revision 1.6 2004/06/02 00:17:46 ksekar Referenced original OpenSSL license headers in source file description. @@ -53,7 +74,7 @@ Support for TSIG signed dynamic updates. extern "C" { #endif -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "DNSCommon.h" // Disable certain benign warnings with Microsoft compilers @@ -661,7 +682,7 @@ void md5_block_data_order (MD5_CTX *c, const void *p,int num); * Time for some action:-) */ -int HASH_UPDATE (HASH_CTX *c, const void *data_, mDNSu32 len) +int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len) { const unsigned char *data=(const unsigned char *)data_; register HASH_LONG * p; @@ -1297,13 +1318,10 @@ mDNSexport mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu3 #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' } }; +#define HMAC_MD5_AlgName (*(const domainname*) "\010" "hmac-md5" "\007" "sig-alg" "\003" "reg" "\003" "int") + // Adapted from Appendix, RFC 2104 -mDNSexport void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, mDNSu8 *key, mDNSu32 len) +mDNSexport void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, const mDNSu8 *key, mDNSu32 len) { MD5_CTX k; mDNSu8 buf[MD5_LEN]; @@ -1354,11 +1372,10 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 // 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); + AssignDomainName(tsig.resrec.name, info->keyname); + MD5_Update(&c, info->keyname.c, DomainNameLength(&info->keyname)); // class tsig.resrec.rrclass = kDNSQClass_ANY; @@ -1370,9 +1387,9 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 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; + AssignDomainName(tsig.resrec.rdata->u.name, HMAC_MD5_AlgName); + len = DomainNameLength(&HMAC_MD5_AlgName); + rdata = tsig.resrec.rdata->u.data + len; MD5_Update(&c, HMAC_MD5_AlgName.c, len); // time @@ -1392,7 +1409,8 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 // fudge buf = mDNSOpaque16fromIntVal(300); // 300 sec is fudge recommended in RFC 2485 - ((mDNSOpaque16 *)rdata)->NotAnInteger = buf.NotAnInteger; + rdata[0] = buf.b[0]; + rdata[1] = buf.b[1]; rdata += sizeof(mDNSOpaque16); MD5_Update(&c, buf.b, sizeof(mDNSOpaque16)); @@ -1411,19 +1429,21 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 MD5_Final(digest, &c); // set remaining rdata fields - *(mDNSOpaque16 *)rdata = mDNSOpaque16fromIntVal(MD5_LEN); // MAC size + rdata[0] = (mDNSu8)((MD5_LEN >> 8) & 0xff); + rdata[1] = (mDNSu8)( MD5_LEN & 0xff); 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); + rdata[0] = msg->h.id.b[0]; // original ID + rdata[1] = msg->h.id.b[1]; + rdata[2] = 0; // no error + rdata[3] = 0; + rdata[4] = 0; // other data len + rdata[5] = 0; + rdata += 6; tsig.resrec.rdlength = (mDNSu16)(rdata - tsig.resrec.rdata->u.data); - *end = PutResourceRecordTTL(msg, ptr, numAdditionals, &tsig.resrec, 0); + *end = PutResourceRecordTTLJumbo(msg, ptr, numAdditionals, &tsig.resrec, 0); if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); return mDNSNULL; } // update num additionals @@ -1433,7 +1453,6 @@ mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 return *end; } - #ifdef __cplusplus } diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index b5364c2..83f634b 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -1,10 +1,9 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- + * * 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 @@ -46,6 +45,369 @@ Change History (most recent first): $Log: mDNS.c,v $ +Revision 1.491 2004/12/11 01:52:11 cheshire + Support kDNSServiceFlagsAllowRemoteQuery for registering services too + +Revision 1.490 2004/12/10 20:06:25 cheshire + Reduce egregious stack space usage +Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes + +Revision 1.489 2004/12/10 20:03:43 cheshire + Reduce egregious stack space usage +Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes + +Revision 1.488 2004/12/10 19:50:41 cheshire + Reduce egregious stack space usage +Reduced SendResponses() stack frame from 9K to 176 bytes + +Revision 1.487 2004/12/10 19:39:13 cheshire + Reduce egregious stack space usage +Reduced SendQueries() stack frame from 18K to 112 bytes + +Revision 1.486 2004/12/10 14:16:17 cheshire + Relax update rate limiting +We now allow an average rate of ten updates per minute. +Updates in excess of that are rate limited, but more gently than before. + +Revision 1.485 2004/12/10 02:09:24 cheshire + Modify default TTLs + +Revision 1.484 2004/12/09 03:15:40 ksekar + use _legacy instead of _default to find "empty string" browse domains + +Revision 1.483 2004/12/07 23:00:14 ksekar + DNSServiceRegisterRecord() can crash on deregistration: +Call RecordProbeFailure even if there is no record callback + +Revision 1.482 2004/12/07 22:49:06 cheshire + BIND doesn't like zero-length rdata + +Revision 1.481 2004/12/07 21:26:04 ksekar + DNSServiceRegisterRecord() can crash on deregistration + +Revision 1.480 2004/12/07 20:42:33 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.479 2004/12/07 17:50:49 ksekar + BIND doesn't like zero-length rdata + +Revision 1.478 2004/12/06 21:15:22 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.477 2004/12/04 02:12:45 cheshire + mDNSResponder puts LargeCacheRecord on the stack + +Revision 1.476 2004/11/29 23:34:31 cheshire +On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero +is crude, and effectively halves the time resolution. The more selective NonZeroTime() function +only nudges the time value to 1 if the interval calculation happens to result in the value zero. + +Revision 1.475 2004/11/29 23:13:31 cheshire + All unique records in a set should have the cache flush bit set +Additional check: Make sure we don't unnecessarily send packets containing only additionals. +(This could occur with multi-packet KA lists, if the answer and additionals were marked +by the query packet, and then the answer were later suppressed in a subsequent KA packet.) + +Revision 1.474 2004/11/29 17:18:12 cheshire +Remove "Unknown DNS packet type" message for update responses + +Revision 1.473 2004/11/25 01:57:52 cheshire + All unique records in a set should have the cache flush bit set + +Revision 1.472 2004/11/25 01:28:09 cheshire + Need to implement random delay for 'QU' unicast replies (and set cache flush bit too) + +Revision 1.471 2004/11/25 01:10:13 cheshire +Move code to add additional records to a subroutine called AddAdditionalsToResponseList() + +Revision 1.470 2004/11/24 21:54:44 cheshire + mDNSCore not receiving unicast responses properly + +Revision 1.469 2004/11/24 04:50:39 cheshire +Minor tidying + +Revision 1.468 2004/11/24 01:47:07 cheshire + DNSServiceRegisterRecord should call CallBack on success. + +Revision 1.467 2004/11/24 01:41:28 cheshire +Rename CompleteProbing() to AcknowledgeRecord() + +Revision 1.466 2004/11/23 21:08:07 ksekar +Don't use ID to demux multicast/unicast now that unicast uses random IDs + +Revision 1.465 2004/11/15 20:09:21 ksekar + Wide Area support for Add/Remove record + +Revision 1.464 2004/11/03 01:44:36 cheshire +Update debugging messages + +Revision 1.463 2004/10/29 02:38:48 cheshire +Fix Windows compile errors + +Revision 1.462 2004/10/28 19:21:07 cheshire +Guard against registering interface with zero InterfaceID + +Revision 1.461 2004/10/28 19:02:16 cheshire +Remove \n from LogMsg() call + +Revision 1.460 2004/10/28 03:24:40 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.459 2004/10/26 22:34:37 cheshire + Need to protect mDNSResponder from unbounded packet flooding + +Revision 1.458 2004/10/26 20:45:28 cheshire +Show mask in "invalid mask" message + +Revision 1.457 2004/10/26 06:28:36 cheshire +Now that we don't check IP TTL any more, remove associated log message + +Revision 1.456 2004/10/26 06:21:42 cheshire +Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address) + +Revision 1.455 2004/10/26 06:11:40 cheshire +Add improved logging to aid in diagnosis of mDNSResponder crashed + +Revision 1.454 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.453 2004/10/22 20:52:06 ksekar + Create NAT port mappings for Long Lived Queries + +Revision 1.452 2004/10/20 01:50:40 cheshire + Cannot resolve non-local registrations using the mach API +Implemented ForceMCast mode for AuthRecords as well as for Questions + +Revision 1.451 2004/10/19 21:33:15 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.450 2004/10/19 17:42:59 ksekar +Fixed compiler warnings for non-debug builds. + +Revision 1.449 2004/10/18 22:57:07 cheshire + Seen in console: Ignored apparent spoof mDNS Response with TTL 1 + +Revision 1.448 2004/10/16 00:16:59 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.447 2004/10/15 00:51:21 cheshire + Seen in console: Ignored apparent spoof mDNS Response with TTL 1 + +Revision 1.446 2004/10/14 00:43:34 cheshire + Services continue to announce SRV and HINFO + +Revision 1.445 2004/10/12 21:07:09 cheshire +Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit() + +Revision 1.444 2004/10/11 17:54:16 ksekar +Changed hashtable pointer output from debugf to verbosedebugf. + +Revision 1.443 2004/10/10 07:05:45 cheshire +For consistency, use symbol "localdomain" instead of literal string + +Revision 1.442 2004/10/08 20:25:10 cheshire +Change of plan for -- we're not going to do that at this time + +Revision 1.441 2004/10/08 03:25:01 ksekar + domain enumeration should use LLQs + +Revision 1.440 2004/10/06 01:44:19 cheshire + Resolving too quickly sometimes returns stale TXT record + +Revision 1.439 2004/10/03 23:14:11 cheshire +Add "mDNSEthAddr" type and "zeroEthAddr" constant + +Revision 1.438 2004/09/29 23:07:04 cheshire +Patch from Pavel Repin to fix compile error on Windows + +Revision 1.437 2004/09/28 02:23:50 cheshire + Deliver near-pending "remove" events before new "add" events +Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot +For records with the cache flush bit set, defer the decision until the end of the packet + +Revision 1.436 2004/09/28 01:27:04 cheshire +Update incorrect log message + +Revision 1.435 2004/09/25 02:41:39 cheshire + Deliver near-pending "remove" events before new "add" events + +Revision 1.434 2004/09/25 02:32:06 cheshire +Update comments + +Revision 1.433 2004/09/25 02:24:27 cheshire +Removed unused rr->UseCount + +Revision 1.432 2004/09/24 21:35:17 cheshire + Browses are no longer piggybacking on other browses +TargetPort and TargetQID are allowed to be undefined if no question->Target is set + +Revision 1.431 2004/09/24 21:33:12 cheshire +Adjust comment + +Revision 1.430 2004/09/24 02:15:49 cheshire + Late conflicts don't send goodbye packets on other interfaces + +Revision 1.429 2004/09/24 00:20:21 cheshire + Any rrtype is a conflict for unique records + +Revision 1.428 2004/09/24 00:12:25 cheshire +Get rid of unused RRUniqueOrKnownUnique(RR) + +Revision 1.427 2004/09/23 20:44:11 cheshire + Reduce timeout before expiring records on failure + +Revision 1.426 2004/09/23 20:21:07 cheshire + Refine "immediate answer burst; restarting exponential backoff sequence" logic +Associate a unique sequence number with each received packet, and only increment the count of recent answer +packets if the packet sequence number for this answer record is not one we've already seen and counted. + +Revision 1.425 2004/09/23 20:14:38 cheshire +Rename "question->RecentAnswers" to "question->RecentAnswerPkts" + +Revision 1.424 2004/09/23 00:58:36 cheshire + Rate limiting interferes with updating TXT records + +Revision 1.423 2004/09/23 00:50:53 cheshire + Don't send a (DE) if a service is unregistered after wake from sleep + +Revision 1.422 2004/09/22 02:34:46 cheshire +Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h + +Revision 1.421 2004/09/21 23:29:49 cheshire + DNSServiceResolve should delay sending packets + +Revision 1.420 2004/09/21 23:01:42 cheshire +Update debugf messages + +Revision 1.419 2004/09/21 19:51:14 cheshire +Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c + +Revision 1.418 2004/09/21 18:40:17 cheshire + Adjust default record TTLs + +Revision 1.417 2004/09/21 17:32:16 cheshire + Rate limiting imposed too soon + +Revision 1.416 2004/09/20 23:52:01 cheshire +CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c + +Revision 1.415 2004/09/18 01:14:09 cheshire + Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces + +Revision 1.414 2004/09/18 01:06:48 cheshire +Add comments + +Revision 1.413 2004/09/17 01:08:48 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.412 2004/09/17 00:46:33 cheshire +mDNS_TimeNow should take const mDNS parameter + +Revision 1.411 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.410 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.409 2004/09/16 21:59:15 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.408 2004/09/16 21:36:36 cheshire + Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.407 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.406 2004/09/16 01:58:14 cheshire +Fix compiler warnings + +Revision 1.405 2004/09/16 00:24:48 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.404 2004/09/15 21:44:11 cheshire + Randomize initial timenow_adjust value in mDNS_Init +Show time value in log to help diagnose errors + +Revision 1.403 2004/09/15 00:46:32 ksekar +Changed debugf to verbosedebugf in CheckCacheExpiration + +Revision 1.402 2004/09/14 23:59:55 cheshire + Randomize initial timenow_adjust value in mDNS_Init + +Revision 1.401 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.400 2004/09/02 03:48:47 cheshire + Disable targeted unicast query support by default +1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record +2. New field AllowRemoteQuery in AuthRecord structure +3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set +4. mDNS.c only answers remote queries if AllowRemoteQuery is set + +Revision 1.399 2004/09/02 01:39:40 cheshire +For better readability, follow consistent convention that QR bit comes first, followed by OP bits + +Revision 1.398 2004/09/01 03:59:29 ksekar +: Conditionally compile out uDNS code on Windows + +Revision 1.397 2004/08/25 22:04:25 rpantos +Fix the standard Windows compile error. + +Revision 1.396 2004/08/25 00:37:27 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.395 2004/08/18 17:21:18 ksekar +Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs() + +Revision 1.394 2004/08/14 03:22:41 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.393 2004/08/13 23:42:52 cheshire +Removed unused "zeroDomainNamePtr" + +Revision 1.392 2004/08/13 23:37:02 cheshire +Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with +"uDNS_info.UnicastHostname" for clarity + +Revision 1.391 2004/08/13 23:25:00 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + +Revision 1.390 2004/08/11 02:17:01 cheshire + Registering service with port number 0 should create a "No Such Service" record + +Revision 1.389 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.388 2004/07/30 17:40:06 ksekar +: TXT Record updates not available for wide-area services + +Revision 1.387 2004/07/26 22:49:30 ksekar +: Feature #9516: Need support for NATPMP in client + +Revision 1.386 2004/07/13 21:24:24 rpantos +Fix for . + +Revision 1.385 2004/06/18 19:09:59 cheshire + Current method of doing subtypes causes name collisions + +Revision 1.384 2004/06/15 04:31:23 cheshire +Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion() + +Revision 1.383 2004/06/11 00:04:59 cheshire + TTL must be greater than zero for DNSServiceRegisterRecord + 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 @@ -201,7 +563,7 @@ 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 +Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.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 @@ -243,13 +605,13 @@ 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. +Best solution is just to combine mDNSEmbeddedAPI.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 +Move AssignDomainName macro to mDNSEmbeddedAPI.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 @@ -332,7 +694,7 @@ Revision 1.299 2003/09/03 02:33:09 cheshire Don't update m->NewQuestions until *after* CheckCacheExpiration(); Revision 1.298 2003/09/03 01:47:01 cheshire - Rendezvous services always in a state of flux + Services always in a state of flux Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds Revision 1.297 2003/08/29 19:44:15 cheshire @@ -410,7 +772,7 @@ Final expiration queries now only mark the question for sending on the particula pertaining to the record that's expiring. Revision 1.278 2003/08/18 22:53:37 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() + mDNSResponder divide by zero in mDNSPlatformRawTime() Revision 1.277 2003/08/18 19:05:44 cheshire UpdateRecord not working right @@ -441,7 +803,7 @@ We want to avoid touching the rdata pages, so we don't page them in. Revision 1.272 2003/08/14 19:29:04 cheshire Include cache records in SIGINFO output -Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them +Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them Revision 1.271 2003/08/14 02:17:05 cheshire Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord @@ -581,7 +943,7 @@ Revision 1.231 2003/07/18 00:06:37 cheshire To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->" Revision 1.230 2003/07/17 18:16:54 cheshire - Rendezvous services always in a state of flux + Services always in a state of flux In preparation for working on this, made some debugf messages a little more selective Revision 1.229 2003/07/17 17:35:04 cheshire @@ -984,7 +1346,7 @@ Fix some warnings Revision 1.118 2003/05/14 18:48:40 cheshire mDNSResponder should be smarter about reconfigurations More minor refinements: -CFSocket.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory +mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away Revision 1.117 2003/05/14 07:08:36 cheshire @@ -996,7 +1358,7 @@ and caused lots of extra network traffic. Now it only removes interfaces that have really gone, and only adds new ones that weren't there before. Revision 1.116 2003/05/14 06:51:56 cheshire - Rendezvous doesn't refresh server info if changed during sleep + mDNSResponder doesn't refresh server info if changed during sleep Revision 1.115 2003/05/14 06:44:31 cheshire Improve debugging message @@ -1214,7 +1576,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-existance +Added mDNS_RegisterNoSuchService() function for assertion of non-existence of a particular named service Revision 1.59 2002/09/19 21:25:34 cheshire @@ -1232,7 +1594,6 @@ 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 "DNSCommon.h" // Defines general DNS untility routines #include "uDNS.h" // Defines entry points into unicast-specific routines // Disable certain benign warnings with Microsoft compilers @@ -1257,15 +1618,15 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release #endif mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; -mDNSexport const mDNSv4Addr zeroIPAddr = { { 0 } }; +mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } }; mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; +mDNSexport const mDNSEthAddr zeroEthAddr = { { 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 } }; 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; // Note that mDNSInterfaceMark is the same value as mDNSInterface_LocalOnly, but they are used in different contexts mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; @@ -1275,7 +1636,7 @@ mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } }; mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; -mDNSexport const mDNSv4Addr AllDNSLinkGroup = { { 224, 0, 0, 251 } }; +mDNSexport const mDNSv4Addr AllDNSLinkGroupv4 = { { 224, 0, 0, 251 } }; mDNSexport const mDNSv6Addr AllDNSLinkGroupv6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } }; mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; @@ -1283,27 +1644,28 @@ mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF 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*)"") +mDNSexport const mDNSOpaque16 UpdateReqFlags= { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; +mDNSexport const mDNSOpaque16 UpdateRespFlags={ { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; // Any records bigger than this are considered 'large' records #define SmallRecordLimit 1024 -#define kDefaultTTLforUnique 240 -#define kDefaultTTLforShared (2*3600) - #define kMaxUpdateCredits 10 -#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 50) +#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6) static const char *const mDNS_DomainTypeNames[] = { "_browse._dns-sd._udp.", "_default._browse._dns-sd._udp.", "_register._dns-sd._udp.", - "_default._register._dns-sd._udp." + "_default._register._dns-sd._udp.", + "_legacy._browse._dns-sd._udp." }; +#ifdef UNICAST_DISABLED +#define uDNS_IsActiveQuery(q, u) mDNSfalse +#endif + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -1639,20 +2001,10 @@ mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) (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 @@ -1689,9 +2041,11 @@ mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, c 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. 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. +// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our +// authoratative record is unique (as opposed to shared). For unique records, we are supposed to have +// complete ownership of *all* types for this name, so *any* record type with the same name is a conflict. +// In addition, when probing we send our questions with the wildcard type kDNSQType_ANY, +// so a response of any type should match, even if it is not actually 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); } @@ -1699,7 +2053,7 @@ mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, cons 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); + if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && 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)); } @@ -1741,7 +2095,7 @@ mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const r { if (rr->resrec.RecordType == kDNSRecordTypeUnique) { - //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, GetRRDisplayString(m, rr)); + //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr)); if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0) m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval); } @@ -1764,7 +2118,7 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) // If we have no probe suppression time set, or it is in the past, set it now if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) { - m->SuppressProbes = (m->timenow + DefaultProbeIntervalForTypeUnique) | 1; + m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique); // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation if (m->SuppressProbes - m->NextScheduledProbe >= 0) m->SuppressProbes = m->NextScheduledProbe; @@ -1804,12 +2158,12 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype); - if (target && SameDomainName(target, &m->hostname)) + if (target && SameDomainName(target, &m->MulticastHostname)) debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name.c, target->c); - if (target && !SameDomainName(target, &m->hostname)) + if (target && !SameDomainName(target, &m->MulticastHostname)) { - AssignDomainName(*target, m->hostname); + AssignDomainName(*target, m->MulticastHostname); SetNewRData(&rr->resrec, mDNSNULL, 0); // If we're in the middle of probing this record, we need to start again, @@ -1820,19 +2174,18 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) // If we've announced this record, we really should send a goodbye packet for the old rdata before // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records, // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way. - if (rr->AnnounceCount < InitialAnnounceCount && rr->resrec.RecordType == kDNSRecordTypeShared) + if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared) debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; + rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); InitializeLastAPTime(m,rr); } } -mDNSlocal void CompleteProbing(mDNS *const m, AuthRecord *const rr) +mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr) { - verbosedebugf("Probing for %##s (%s) complete", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); if (!rr->Acknowledged && rr->RecordCallback) { // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function @@ -1855,17 +2208,22 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) AuthRecord **p = &m->ResourceRecords; AuthRecord **d = &m->DuplicateRecords; AuthRecord **l = &m->LocalOnlyRecords; + + mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); + + if ((mDNSs32)rr->resrec.rroriginalttl <= 0) + { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); } #if TEST_LOCALONLY_FOR_EVERYTHING 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)) +#ifndef UNICAST_DISABLED + if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(&rr->resrec.name)) rr->uDNS_info.id = zeroID; else return uDNS_RegisterRecord(m, rr); - +#endif + while (*p && *p != rr) p=&(*p)->next; while (*d && *d != rr) d=&(*d)->next; while (*l && *l != rr) l=&(*l)->next; @@ -1917,6 +2275,7 @@ 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 +// rr->AllowRemoteQuery = 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; @@ -1924,11 +2283,13 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->Acknowledged = mDNSfalse; rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); rr->AnnounceCount = InitialAnnounceCount; + rr->RequireGoodbye = mDNSfalse; rr->IncludeInProbe = mDNSfalse; rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; rr->ImmedAdditional = mDNSNULL; rr->SendRNow = mDNSNULL; - rr->v4Requester = zeroIPAddr; + rr->v4Requester = zerov4Addr; rr->v6Requester = zerov6Addr; rr->NextResponse = mDNSNULL; rr->NR_AnswerTo = mDNSNULL; @@ -1962,11 +2323,15 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } if (!ValidateDomainName(&rr->resrec.name)) - { LogMsg("Attempt to register record with invalid name: %s", GetRRDisplayString(m, rr)); return(mStatus_Invalid); } + { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } + + // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates with zero-length rdata. It's common for + // existing mDNS clients to create empty TXT records, so we silently change those to a TXT record containing a single empty string. + if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; } // 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); } + { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); } rr->resrec.namehash = DomainNameHashValue(&rr->resrec.name); rr->resrec.rdatahash = RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u); @@ -2021,6 +2386,10 @@ mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) *p = rr; } } + + // For records that are not going to probe, acknowledge them right away + if (rr->resrec.RecordType != kDNSRecordTypeUnique) AcknowledgeRecord(m, rr); + return(mStatus_NoError); } @@ -2028,12 +2397,17 @@ mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr) { m->ProbeFailTime = m->timenow; m->NumFailedProbes++; - // If we've had ten or more probe failures, rate-limit to one every five seconds - // The result is ORed with 1 to make sure SuppressProbes is not accidentally set to zero - if (m->NumFailedProbes >= 10) + // If we've had fifteen or more probe failures, rate-limit to one every five seconds. + // If a bunch of hosts have all been configured with the same name, then they'll all + // conflict and run through the same series of names: name-2, name-3, name-4, etc., + // up to name-10. After that they'll start adding random increments in the range 1-100, + // so they're more likely to branch out in the available namespace and settle on a set of + // unique names quickly. If after five more tries the host is still conflicting, then we + // may have a serious problem, so we start rate-limiting so we don't melt down the network. + if (m->NumFailedProbes >= 15) { - m->SuppressProbes = (m->timenow + mDNSPlatformOneSecond * 5) | 1; - LogMsg("Excessive name conflicts (%d) for %##s (%s); rate limiting in effect", + m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5); + LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect", m->NumFailedProbes, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } } @@ -2059,8 +2433,10 @@ 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 && !IsLocalDomain(&rr->resrec.name) && rr->uDNS_info.id.NotAnInteger) +#ifndef UNICAST_DISABLED + if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(&rr->resrec.name))) return uDNS_DeregisterRecord(m, rr); +#endif if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly) p = &m->LocalOnlyRecords; while (*p && *p != rr) p=&(*p)->next; @@ -2091,18 +2467,20 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, dup->next = rr->next; // And then... rr->next = dup; // ... splice it in right after the record we're about to delete dup->resrec.RecordType = rr->resrec.RecordType; - dup->ProbeCount = rr->ProbeCount; - dup->AnnounceCount = rr->AnnounceCount; - dup->ImmedAnswer = rr->ImmedAnswer; - dup->ImmedAdditional = rr->ImmedAdditional; - dup->v4Requester = rr->v4Requester; - dup->v6Requester = rr->v6Requester; - dup->ThisAPInterval = rr->ThisAPInterval; - dup->AnnounceUntil = rr->AnnounceUntil; - dup->LastAPTime = rr->LastAPTime; - dup->LastMCTime = rr->LastMCTime; - dup->LastMCInterface = rr->LastMCInterface; - if (RecordType == kDNSRecordTypeShared) rr->AnnounceCount = InitialAnnounceCount; + dup->ProbeCount = rr->ProbeCount; + dup->AnnounceCount = rr->AnnounceCount; + dup->RequireGoodbye = rr->RequireGoodbye; + dup->ImmedAnswer = rr->ImmedAnswer; + dup->ImmedUnicast = rr->ImmedUnicast; + dup->ImmedAdditional = rr->ImmedAdditional; + dup->v4Requester = rr->v4Requester; + dup->v6Requester = rr->v6Requester; + dup->ThisAPInterval = rr->ThisAPInterval; + dup->AnnounceUntil = rr->AnnounceUntil; + dup->LastAPTime = rr->LastAPTime; + dup->LastMCTime = rr->LastMCTime; + dup->LastMCInterface = rr->LastMCInterface; + rr->RequireGoodbye = mDNSfalse; } } } @@ -2112,7 +2490,7 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, p = &m->DuplicateRecords; while (*p && *p != rr) p=&(*p)->next; // If we found our record on the duplicate list, then make sure we don't send a goodbye for it - if (*p && RecordType == kDNSRecordTypeShared) rr->AnnounceCount = InitialAnnounceCount; + if (*p) rr->RequireGoodbye = mDNSfalse; if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } @@ -2120,13 +2498,13 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, { // No need to log an error message if we already know this is a potentially repeated deregistration if (drt != mDNS_Dereg_repeat) - debugf("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); return(mStatus_BadReferenceErr); } // If this is a shared record and we've announced it at least once, // we need to retract that announcement before we delete the record - if (RecordType == kDNSRecordTypeShared && rr->AnnounceCount < InitialAnnounceCount) + if (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) { verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); rr->resrec.RecordType = kDNSRecordTypeDeregistering; @@ -2172,17 +2550,12 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, // 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) + if (drt == mDNS_Dereg_conflict) { RecordProbeFailure(m, rr); - if (rr->RecordCallback) - rr->RecordCallback(m, rr, mStatus_NameConflict); + if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_NameConflict); } + else if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); m->mDNS_reentrancy--; // Decrement to block mDNS API calls again } return(mStatus_NoError); @@ -2195,13 +2568,127 @@ mDNSlocal mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, #pragma mark - Packet Sending Functions #endif +mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add) + { + if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse) + { + **nrpp = rr; + // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo) + // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does + // The referenced record will definitely be acceptable (by recursive application of this rule) + if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo; + rr->NR_AdditionalTo = add; + *nrpp = &rr->NextResponse; + } + debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + } + +mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID) + { + AuthRecord *rr, *rr2; + for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put + { + // (Note: This is an "if", not a "while". If we add a record, we'll find it again + // later in the "for" loop, and we will follow further "additional" links then.) + if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID)) + AddRecordToResponseList(nrpp, rr->Additional1, rr); + + if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) + AddRecordToResponseList(nrpp, rr->Additional2, rr); + + // 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 (RRTypeIsAddressType(rr2->resrec.rrtype) && // For all address records (A/AAAA) ... + ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) && // ... which are valid for answer ... + 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(nrpp, rr2, rr); + } + } + +mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID) + { + AuthRecord *rr; + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + + // Make a list of all our records that need to be unicast to this destination + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + // If we find we can no longer unicast this answer, clear ImmedUnicast + if (rr->ImmedAnswer == mDNSInterfaceMark || + mDNSSameIPv4Address(rr->v4Requester, onesIPv4Addr) || + mDNSSameIPv6Address(rr->v6Requester, onesIPv6Addr) ) + rr->ImmedUnicast = mDNSfalse; + + if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID) + if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) || + (dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6))) + { + rr->ImmedAnswer = mDNSNULL; // Clear the state fields + rr->ImmedUnicast = mDNSfalse; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) // rr->NR_AnswerTo + { rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; } + } + } + + AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); + + while (ResponseRecords) + { + mDNSu8 *responseptr = m->omsg.data; + mDNSu8 *newptr; + InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); + + // Put answers in the packet + while (ResponseRecords && ResponseRecords->NR_AnswerTo) + { + rr = ResponseRecords; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (!newptr && m->omsg.h.numAnswers) break; // If packet full, send it now + if (newptr) responseptr = newptr; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + rr->RequireGoodbye = mDNStrue; + } + + // Add additionals, if there's space + while (ResponseRecords && !ResponseRecords->NR_AnswerTo) + { + rr = ResponseRecords; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + + if (newptr) responseptr = newptr; + if (newptr && m->omsg.h.numAnswers) rr->RequireGoodbye = mDNStrue; + else if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->ImmedAnswer = mDNSInterfaceMark; + ResponseRecords = rr->NextResponse; + rr->NextResponse = mDNSNULL; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + } + + if (m->omsg.h.numAnswers) mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, dest, MulticastDNSPort, -1, mDNSNULL); + } + } + mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr) { - // Setting AnnounceCount to InitialAnnounceCount signals mDNS_Deregister_internal() + // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() // that it should go ahead and immediately dispose of this registration - rr->resrec.RecordType = kDNSRecordTypeShared; - rr->AnnounceCount = InitialAnnounceCount; - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + rr->resrec.RecordType = kDNSRecordTypeShared; + rr->RequireGoodbye = mDNSfalse; + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this } // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change @@ -2217,23 +2704,14 @@ mDNSlocal void DiscardDeregistrations(mDNS *const m) AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - CompleteDeregistration(m, rr); + CompleteDeregistration(m, rr); // Don't touch rr after this } } 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 - const AuthRecord *a; - for (a = m->ResourceRecords; a; a=a->next) - if (a->SendRNow == InterfaceID && a != rr && SameResourceRecordSignature(&a->resrec, &rr->resrec)) break; - return (a == mDNSNULL); // If no more members of this set found, then we should set the cache flush bit + else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); } // Note about acceleration of announcements to facilitate automatic coalescing of @@ -2261,6 +2739,22 @@ mDNSlocal void SendResponses(mDNS *const m) m->NextScheduledResponse = m->timenow + 0x78000000; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->ImmedUnicast) + { + mDNSAddr v4 = { mDNSAddrType_IPv4, {{{0}}} }; + mDNSAddr v6 = { mDNSAddrType_IPv6, {{{0}}} }; + v4.ip.v4 = rr->v4Requester; + v6.ip.v6 = rr->v6Requester; + if (!mDNSIPv4AddressIsZero(rr->v4Requester)) SendDelayedUnicastResponse(m, &v4, rr->ImmedAnswer); + if (!mDNSIPv6AddressIsZero(rr->v6Requester)) SendDelayedUnicastResponse(m, &v6, rr->ImmedAnswer); + if (rr->ImmedUnicast) + { + LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr)); + rr->ImmedUnicast = mDNSfalse; + } + } + // *** // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on // *** @@ -2354,7 +2848,7 @@ mDNSlocal void SendResponses(mDNS *const m) rr->LastMCInterface = rr->ImmedAnswer; } SetNextAnnounceProbeTime(m, rr); - //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, GetRRDisplayString(m, rr)); + //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr)); } // *** @@ -2366,10 +2860,9 @@ mDNSlocal void SendResponses(mDNS *const m) int numDereg = 0; int numAnnounce = 0; int numAnswer = 0; - DNSMessage response; - mDNSu8 *responseptr = response.data; + mDNSu8 *responseptr = m->omsg.data; mDNSu8 *newptr; - InitializeDNSMessage(&response.h, zeroID, ResponseFlags); + InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); // First Pass. Look for: // 1. Deregistering records that need to send their goodbye packet @@ -2384,8 +2877,8 @@ mDNSlocal void SendResponses(mDNS *const m) { if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) { - newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0); - if (!newptr && response.h.numAnswers) break; + newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + if (!newptr && m->omsg.h.numAnswers) break; numDereg++; responseptr = newptr; } @@ -2394,31 +2887,30 @@ mDNSlocal void SendResponses(mDNS *const m) RData *OldRData = rr->resrec.rdata; mDNSu16 oldrdlength = rr->resrec.rdlength; // 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) + if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye) { - newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, 0); - if (!newptr && response.h.numAnswers) break; + newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + if (!newptr && m->omsg.h.numAnswers) break; numDereg++; responseptr = newptr; } // Now try to see if we can fit the update in the same packet (not fatal if we can't) SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); - if ((rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && HaveSentEntireRRSet(m, rr, intf->InterfaceID)) + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecord(&response, responseptr, &response.h.numAnswers, &rr->resrec); + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state if (newptr) responseptr = newptr; SetNewRData(&rr->resrec, OldRData, oldrdlength); } else { - if ((rr->resrec.RecordType & kDNSRecordTypeUniqueMask) && HaveSentEntireRRSet(m, rr, intf->InterfaceID)) + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecordTTL(&response, responseptr, &response.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl); + newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl); rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state - if (!newptr && response.h.numAnswers) break; + if (!newptr && m->omsg.h.numAnswers) break; + rr->RequireGoodbye = !m->SleepState; if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++; responseptr = newptr; } @@ -2433,47 +2925,56 @@ mDNSlocal void SendResponses(mDNS *const m) newptr = responseptr; for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->ImmedAdditional == intf->InterfaceID) - { - // Since additionals are optional, we clear ImmedAdditional anyway, even if we subsequently find it doesn't fit in the packet - rr->ImmedAdditional = mDNSNULL; - if (newptr && ResourceRecordIsValidAnswer(rr)) + if (ResourceRecordIsValidAnswer(rr)) { - if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + // If we have at least one answer already in the packet, then plan to add additionals too + mDNSBool SendAdditional = (m->omsg.h.numAnswers > 0); + + // If we're not planning to send any additionals, but this record is a unique one, then + // make sure we haven't already sent any other members of its RRSet -- if we have, then they + // will have had the cache flush bit set, so now we need to finish the job and send the rest. + if (!SendAdditional && (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)) { - // Try to find another member of this set that we're still planning to send on this interface const AuthRecord *a; for (a = m->ResourceRecords; a; a=a->next) - if (a->ImmedAdditional == intf->InterfaceID && SameResourceRecordSignature(&a->resrec, &rr->resrec)) break; - if (a == mDNSNULL) // If no more members of this set found - rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + if (a->LastMCTime == m->timenow && + a->LastMCInterface == intf->InterfaceID && + SameResourceRecordSignature(&a->resrec, &rr->resrec)) { SendAdditional = mDNStrue; break; } } - newptr = PutResourceRecord(&response, newptr, &response.h.numAdditionals, &rr->resrec); - if (newptr) + if (!SendAdditional) // If we don't want to send this after all, + rr->ImmedAdditional = mDNSNULL; // then cancel its ImmedAdditional field + else if (newptr) // Else, try to add it if we can { - 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; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it + newptr = PutResourceRecord(&m->omsg, newptr, &m->omsg.h.numAdditionals, &rr->resrec); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state + if (newptr) + { + responseptr = newptr; + rr->ImmedAdditional = mDNSNULL; + rr->RequireGoodbye = mDNStrue; + // 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 } - } - if (response.h.numAnswers > 0) // We *never* send a packet with only additionals in it + if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals) { debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", numDereg, numDereg == 1 ? "" : "s", numAnnounce, numAnnounce == 1 ? "" : "s", numAnswer, numAnswer == 1 ? "" : "s", - response.h.numAdditionals, response.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); - 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; } + m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID); + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); + if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10); + if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; } // There might be more things to send on this interface, so go around one more time and try again. } else // Nothing more to send on this interface; go to next @@ -2499,20 +3000,24 @@ mDNSlocal void SendResponses(mDNS *const m) m->CurrentRecord = rr->next; 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 + { LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } - if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) - CompleteDeregistration(m, rr); - else + if (rr->ImmedAnswer) { - rr->ImmedAnswer = mDNSNULL; - rr->v4Requester = zeroIPAddr; - rr->v6Requester = zerov6Addr; + 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); // Don't touch rr after this + else + { + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; + rr->v4Requester = zerov4Addr; + rr->v6Requester = zerov6Addr; + } } } - verbosedebugf("SendResponses: Next in %d ticks", m->NextScheduledResponse - m->timenow); + verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow); } // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache, @@ -2526,12 +3031,14 @@ mDNSlocal void SendResponses(mDNS *const m) // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets), // so allow at most 1/10 second lateness #define CacheCheckGracePeriod(RR) ( \ + ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \ ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \ ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \ ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10)) // Note: MUST call SetNextCacheCheckTime any time we change: // rr->TimeRcvd +// rr->DelayDelivery // rr->resrec.rroriginalttl // rr->UnansweredQueries // rr->CRActiveQuestion @@ -2551,20 +3058,18 @@ mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr) if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0) m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)); + + if (rr->DelayDelivery) + if (m->NextCacheCheck - rr->DelayDelivery > 0) + m->NextCacheCheck = rr->DelayDelivery; } -#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 45) +#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15) #define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5) #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) 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 @@ -2579,7 +3084,7 @@ mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, rr->resrec.rroriginalttl = interval * 4 / mDNSPlatformOneSecond; SetNextCacheCheckTime(m, rr); } - debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, GetRRDisplayString(m, rr)); + debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr)); return(mStatus_NoError); } @@ -2591,7 +3096,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) && m->CanReceiveUnicast; + mDNSBool ucast = (q->LargeAnswers || q->ThisQInterval <= InitialQuestionInterval*2) && m->CanReceiveUnicastOn5353; 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)); @@ -2812,17 +3317,16 @@ mDNSlocal void SendQueries(mDNS *const m) 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; + mDNSu8 *qptr = m->omsg.data; + const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data); + InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); + qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); + mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, &q->Target, q->TargetPort, -1, mDNSNULL); + q->ThisQInterval *= 2; + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->SendQNow = mDNSNULL; m->ExpectUnicastResponse = m->timenow; } @@ -2870,8 +3374,8 @@ mDNSlocal void SendQueries(mDNS *const m) // then we consider it recent enough that we don't need to do an identical query ourselves. ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2); - q->LastQTxTime = m->timenow; - q->RecentAnswers = 0; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; } // For all questions (not just the ones we're sending) check what the next scheduled event will be SetNextQueryTime(m,q); @@ -2917,7 +3421,7 @@ mDNSlocal void SendQueries(mDNS *const m) for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0; - CompleteProbing(m, rr); + AcknowledgeRecord(m, rr); } } } @@ -2927,7 +3431,7 @@ mDNSlocal void SendQueries(mDNS *const m) AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0) - CompleteProbing(m, rr); + AcknowledgeRecord(m, rr); } } @@ -2935,9 +3439,8 @@ mDNSlocal void SendQueries(mDNS *const m) while (intf) { AuthRecord *rr; - DNSMessage query; - mDNSu8 *queryptr = query.data; - InitializeDNSMessage(&query.h, zeroID, QueryFlags); + mDNSu8 *queryptr = m->omsg.data; + InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); if (!KnownAnswerList) { @@ -2949,12 +3452,12 @@ mDNSlocal void SendQueries(mDNS *const m) for (q = m->Questions; q; q=q->next) if (q->SendQNow == intf->InterfaceID) { - debugf("SendQueries: %s question for %##s (%s) at %lu forecast total %lu", + debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d", SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ", - q->qname.c, DNSTypeName(q->qtype), queryptr - query.data, queryptr + answerforecast - query.data); + q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data); // If we're suppressing this question, or we successfully put it, update its SendQNow state if (SuppressOnThisInterface(q->DupSuppress, intf) || - BuildQuestion(m, &query, &queryptr, q, &kalistptr, &answerforecast)) + BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); } @@ -2962,10 +3465,10 @@ 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) && m->CanReceiveUnicast; + mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; 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)); + const mDNSu8 *const limit = m->omsg.data + ((m->omsg.h.numQuestions) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); + mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, &rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate; if (newptr && newptr + forecast < limit) @@ -2979,7 +3482,7 @@ mDNSlocal void SendQueries(mDNS *const m) else { verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - query.h.numQuestions--; + m->omsg.h.numQuestions--; } } } @@ -2989,10 +3492,10 @@ mDNSlocal void SendQueries(mDNS *const m) { CacheRecord *rr = KnownAnswerList; mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; - mDNSu8 *newptr = PutResourceRecordTTL(&query, queryptr, &query.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd); + mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd); if (newptr) { - verbosedebugf("SendQueries: Put %##s (%s) at %lu - %lu", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), queryptr - query.data, newptr - query.data); + verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data); queryptr = newptr; KnownAnswerList = rr->NextInKAList; rr->NextInKAList = mDNSNULL; @@ -3001,9 +3504,9 @@ mDNSlocal void SendQueries(mDNS *const m) { // If we ran out of space and we have more than one question in the packet, that's an error -- // we shouldn't have put more than one question if there was a risk of us running out of space. - if (query.h.numQuestions > 1) - LogMsg("SendQueries: Put %d answers; No more space for known answers", query.h.numAnswers); - query.h.flags.b[0] |= kDNSFlag0_TC; + if (m->omsg.h.numQuestions > 1) + LogMsg("SendQueries: Put %d answers; No more space for known answers", m->omsg.h.numAnswers); + m->omsg.h.flags.b[0] |= kDNSFlag0_TC; break; } } @@ -3011,24 +3514,24 @@ mDNSlocal void SendQueries(mDNS *const m) for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->IncludeInProbe) { - mDNSu8 *newptr = PutResourceRecord(&query, queryptr, &query.h.numAuthorities, &rr->resrec); + mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec); rr->IncludeInProbe = mDNSfalse; if (newptr) queryptr = newptr; else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } - if (queryptr > query.data) + if (queryptr > m->omsg.data) { - if ((query.h.flags.b[0] & kDNSFlag0_TC) && query.h.numQuestions > 1) - LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet\n", query.h.numQuestions); + if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) + LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions); debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", - query.h.numQuestions, query.h.numQuestions == 1 ? "" : "s", - query.h.numAnswers, query.h.numAnswers == 1 ? "" : "s", - query.h.numAuthorities, query.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); - 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 + m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", + m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", + m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID); + if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL); + if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, mDNSNULL); + if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + mDNSPlatformOneSecond/10); if (++pktcount >= 1000) { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; } // There might be more records left in the known answer list, or more questions to send @@ -3050,7 +3553,7 @@ mDNSlocal void SendQueries(mDNS *const m) 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; } + { LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m, rr)); rr->SendRNow = mDNSNULL; } } } @@ -3067,8 +3570,11 @@ mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, C verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)", q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue) + // may be called twice, once when the record is received, and again when it's time to notify local clients. + // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this. + rr->LastUsed = m->timenow; - rr->UseCount++; if (ActiveQuestion(q) && rr->CRActiveQuestion != q) { if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count @@ -3076,15 +3582,47 @@ mDNSlocal void AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, C SetNextCacheCheckTime(m, rr); } - // CAUTION: MUST NOT do anything more with q after calling q->Callback(), because the client's callback function - // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. - // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv() - // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions - // being deleted out from under them. + if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback if (q->QuestionCallback) q->QuestionCallback(m, q, &rr->resrec, AddRecord); m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function + // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. + // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv() + // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions + // being deleted out from under them. + } + +mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) + { + rr->DelayDelivery = 0; + if (m->CurrentQuestion) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set"); + m->CurrentQuestion = m->Questions; + while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions) + { + DNSQuestion *q = m->CurrentQuestion; + m->CurrentQuestion = q->next; + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); + } + m->CurrentQuestion = mDNSNULL; + } + +mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot) + { + const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second + const mDNSs32 start = m->timenow - 0x10000000; + mDNSs32 delay = start; + CacheRecord *rr; + for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) + if (rr->resrec.namehash == namehash && SameDomainName(&rr->resrec.name, name)) + if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second + if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted + delay = RRExpireTime(rr); + if (delay - start > 0) return(delay ? delay : 1); // Make sure we return non-zero if we want to delay + else return(0); } // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call. @@ -3110,19 +3648,32 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) // We must be at least at the eight-second interval to do this. If we're at the four-second interval, or less, // there's not much benefit accelerating because we will anyway send another query within a few seconds. // The first reset query is sent out randomized over the next four seconds to reduce possible synchronization between machines. - if (ActiveQuestion(q) && ++q->RecentAnswers >= 10 && - q->ThisQInterval > InitialQuestionInterval*16 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) + if (q->LastAnswerPktNum != m->PktNum) { - LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence", - q->qname.c, DNSTypeName(q->qtype)); - q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); - q->ThisQInterval = InitialQuestionInterval; - SetNextQueryTime(m,q); + q->LastAnswerPktNum = m->PktNum; + if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 && + q->ThisQInterval > InitialQuestionInterval*16 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond) + { + LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence", + q->qname.c, DNSTypeName(q->qtype)); + q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4); + q->ThisQInterval = InitialQuestionInterval; + SetNextQueryTime(m,q); + } } verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); q->CurrentAnswers++; if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; + if (q->CurrentAnswers > 4000) + { + static int msgcount = 0; + if (msgcount++ < 10) + LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack", + q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers); + rr->resrec.rroriginalttl = 1; + rr->UnansweredQueries = MaxUnansweredQueries; + } AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue); // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() } @@ -3179,7 +3730,9 @@ mDNSlocal void ReleaseCacheRR(mDNS *const m, CacheRecord *r) m->rrcache_totalused--; } -mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) +// Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering CacheRecordDeferredAdd calls +// The in-order nature of the cache lists ensures that all callbacks for old records are delivered before callbacks for newer records +mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot) { CacheRecord **rp = &(m->rrcache_hash[slot]); @@ -3193,7 +3746,7 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) if (m->timenow - event >= 0) // If expired, delete it { *rp = rr->next; // Cut it from the list - verbosedebugf("CheckCacheExpiration: Deleting %s", GetRRDisplayString(m, rr)); + verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m, rr)); if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away { CacheRecordRmv(m, rr); @@ -3204,18 +3757,24 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) } else // else, not expired; see if we need to query { - if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) + if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) + event = rr->DelayDelivery; + else { - if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query - event = rr->NextRequiredQuery; // then just record when we want the next query - else // else trigger our question to go out now + if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr); + if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries) { - // Set NextScheduledQuery to timenow so that SendQueries() will run. - // SendQueries() will see that we have records close to expiration, and send FEQs for them. - m->NextScheduledQuery = m->timenow; - // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(), - // which will correctly update m->NextCacheCheck for us - event = m->timenow + 0x3FFFFFFF; + if (m->timenow - rr->NextRequiredQuery < 0) // If not yet time for next query + event = rr->NextRequiredQuery; // then just record when we want the next query + else // else trigger our question to go out now + { + // Set NextScheduledQuery to timenow so that SendQueries() will run. + // SendQueries() will see that we have records close to expiration, and send FEQs for them. + m->NextScheduledQuery = m->timenow; + // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(), + // which will correctly update m->NextCacheCheck for us + event = m->timenow + 0x3FFFFFFF; + } } } if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0) @@ -3223,7 +3782,7 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, mDNSu32 slot) rp = &rr->next; } } - if (m->rrcache_tail[slot] != rp) debugf("CheckCacheExpiration: Updating m->rrcache_tail[%d] from %p to %p", slot, m->rrcache_tail[slot], rp); + if (m->rrcache_tail[slot] != rp) verbosedebugf("CheckCacheExpiration: Updating m->rrcache_tail[%lu] from %p to %p", slot, m->rrcache_tail[slot], rp); m->rrcache_tail[slot] = rp; m->lock_rrcache = 0; } @@ -3233,7 +3792,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) mDNSBool ShouldQueryImmediately = mDNStrue; CacheRecord *rr; DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer - mDNSu32 slot = HashSlot(&q->qname); + const mDNSu32 slot = HashSlot(&q->qname); verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -3262,7 +3821,8 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) // If this record set is marked unique, then that means we can reasonably assume we have the whole set // -- we don't need to rush out on the network and query immediately to see if there are more answers out there - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ShouldQueryImmediately = mDNSfalse; + if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique)) + ShouldQueryImmediately = mDNSfalse; q->CurrentAnswers++; if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; @@ -3287,7 +3847,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, mDNSBool AddRecord) { // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it - if (AddRecord) rr->AnnounceCount = InitialAnnounceCount - 1; + if (AddRecord) rr->RequireGoodbye = mDNStrue; m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback if (q->QuestionCallback) q->QuestionCallback(m, q, &rr->resrec, AddRecord); @@ -3301,9 +3861,10 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - if (m->CurrentQuestion) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set"); + if (m->CurrentQuestion) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set"); m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted + if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set"); m->CurrentRecord = m->LocalOnlyRecords; while (m->CurrentRecord && m->CurrentRecord != m->NewLocalOnlyRecords) { @@ -3318,6 +3879,7 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) } m->CurrentQuestion = mDNSNULL; + m->CurrentRecord = mDNSNULL; } mDNSlocal void AnswerLocalOnlyQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord) @@ -3332,7 +3894,7 @@ mDNSlocal void AnswerLocalOnlyQuestions(mDNS *const m, AuthRecord *rr, mDNSBool { debugf("AnswerLocalOnlyQuestions %p %##s (%s) %lu", rr, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl); AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); - // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord() + // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord() } } m->CurrentQuestion = mDNSNULL; @@ -3411,7 +3973,7 @@ mDNSlocal CacheRecord *GetFreeCacheRR(mDNS *const m, mDNSu16 RDLength) ReleaseCacheRR(m, rr); } } - if (m->rrcache_tail[slot] != rp) debugf("GetFreeCacheRR: Updating m->rrcache_tail[%d] from %p to %p", slot, m->rrcache_tail[slot], rp); + if (m->rrcache_tail[slot] != rp) verbosedebugf("GetFreeCacheRR: Updating m->rrcache_tail[%lu] from %p to %p", slot, m->rrcache_tail[slot], rp); m->rrcache_tail[slot] = rp; } #if MDNS_DEBUGMSGS @@ -3434,9 +3996,9 @@ mDNSlocal CacheRecord *GetFreeCacheRR(mDNS *const m, mDNSu16 RDLength) else m->rrcache_report += 100; } mDNSPlatformMemZero(r, sizeof(*r)); - r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage + r->resrec.rdata = (RData*)&r->rdatastorage; // By default, assume we're usually going to be using local storage - if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage + if (RDLength > InlineCacheRDSize) // If RDLength is too big, allocate extra storage { r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength); if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength; @@ -3462,89 +4024,26 @@ mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) SetNextCacheCheckTime(m, rr); } -mDNSlocal void mDNS_Lock(mDNS *const m) +mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) { - // MUST grab the platform lock FIRST! + mDNSs32 time; mDNSPlatformLock(m); - - // Normally, mDNS_reentrancy is zero and so is mDNS_busy - // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too - // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one - // If mDNS_busy != mDNS_reentrancy that's a bad sign - if (m->mDNS_busy != m->mDNS_reentrancy) - LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - // If this is an initial entry into the mDNSCore code, set m->timenow - // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set - if (m->mDNS_busy == 0) - { - if (m->timenow) - LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNSPlatformTimeNow() + m->timenow_adjust); - m->timenow = mDNSPlatformTimeNow() + m->timenow_adjust; - if (m->timenow == 0) m->timenow = 1; - } - else if (m->timenow == 0) - { - LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); - m->timenow = mDNSPlatformTimeNow() + m->timenow_adjust; - if (m->timenow == 0) m->timenow = 1; - } - - if (m->timenow_last - m->timenow > 0) + if (m->mDNS_busy) { - m->timenow_adjust += m->timenow_last - m->timenow; - LogMsg("mDNSPlatformTimeNow went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust); - m->timenow = m->timenow_last; + LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow."); + if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy); } - m->timenow_last = m->timenow; - - // Increment mDNS_busy so we'll recognise re-entrant calls - m->mDNS_busy++; - } - -mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) - { - mDNSs32 e = m->timenow + 0x78000000; - if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) return(e); - if (m->NewQuestions) return(m->timenow); - if (m->NewLocalOnlyQuestions) return(m->timenow); - if (m->NewLocalOnlyRecords) return(m->timenow); - if (m->DiscardLocalOnlyRecords) return(m->timenow); - if (m->SuppressSending) return(m->SuppressSending); - if (e - m->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; - if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; - return(e); - } - -mDNSlocal void mDNS_Unlock(mDNS *const m) - { - // Decrement mDNS_busy - m->mDNS_busy--; - // Check for locking failures - if (m->mDNS_busy != m->mDNS_reentrancy) - LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); - - // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow - if (m->mDNS_busy == 0) - { - m->NextScheduledEvent = GetNextScheduledEvent(m); - if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero"); - m->timenow = 0; - } - - // MUST release the platform lock LAST! + if (m->timenow) time = m->timenow; + else time = mDNSPlatformRawTime() + m->timenow_adjust; mDNSPlatformUnlock(m); + return(time); } 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; @@ -3567,7 +4066,11 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) } // 4. See if we can answer any of our new local questions from the cache - for (i=0; m->NewQuestions && i<1000; i++) AnswerNewQuestion(m); + for (i=0; m->NewQuestions && i<1000; i++) + { + if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break; + AnswerNewQuestion(m); + } if (i >= 1000) debugf("mDNS_Execute: AnswerNewQuestion exceeded loop limit"); for (i=0; m->DiscardLocalOnlyRecords && i<1000; i++) DiscardLocalOnlyRecords(m); @@ -3636,7 +4139,9 @@ 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). +#ifndef UNICAST_DISABLED uDNS_Execute(m); +#endif mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value return(m->NextScheduledEvent); } @@ -3663,10 +4168,12 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) if (sleepstate) { - uDNS_SuspendLLQs(m); +#ifndef UNICAST_DISABLED + uDNS_Sleep(m); +#endif // 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) + if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) rr->ImmedAnswer = mDNSInterfaceMark; SendResponses(m); } @@ -3676,15 +4183,16 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) mDNSu32 slot; CacheRecord *cr; - uDNS_RestartLLQs(m); - +#ifndef UNICAST_DISABLED + uDNS_Wake(m); +#endif // 1. Retrigger all our questions for (q = m->Questions; q; q=q->next) // Scan our list of questions if (ActiveQuestion(q)) { - q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question - q->LastQTime = m->timenow - q->ThisQInterval; - q->RecentAnswers = 0; + q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + q->LastQTime = m->timenow - q->ThisQInterval; + q->RecentAnswerPkts = 0; ExpireDupSuppressInfo(q->DupSuppress, m->timenow); m->NextScheduledQuery = m->timenow; } @@ -3699,13 +4207,11 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) for (rr = m->ResourceRecords; rr; rr=rr->next) { if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->AnnounceCount = InitialAnnounceCount; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); InitializeLastAPTime(m, rr); } - } mDNS_Unlock(m); @@ -3717,21 +4223,6 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate) #pragma mark - Packet Reception Functions #endif -mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add) - { - if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse) - { - **nrpp = rr; - // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo) - // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does - // The referenced record will definitely be acceptable (by recursive application of this rule) - if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo; - rr->NR_AdditionalTo = add; - *nrpp = &rr->NextResponse; - } - debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - } - #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo) mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end, @@ -3913,17 +4404,16 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q for (i = 0; i < query->h.numAuthorities; i++) { - LargeCacheRecord pkt; - ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec); if (!ptr) break; - if (ResourceRecordAnswersQuestion(&pkt.r.resrec, q)) + if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) { FoundUpdate = mDNStrue; - if (PacketRRConflict(m, our, &pkt.r)) + if (PacketRRConflict(m, our, &m->rec.r)) { - int result = (int)our->resrec.rrclass - (int)pkt.r.resrec.rrclass; - if (!result) result = (int)our->resrec.rrtype - (int)pkt.r.resrec.rrtype; - if (!result) result = CompareRData(our, &pkt.r); + int result = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass; + if (!result) result = (int)our->resrec.rrtype - (int)m->rec.r.resrec.rrtype; + if (!result) result = CompareRData(our, &m->rec.r); switch (result) { case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); @@ -3931,13 +4421,16 @@ mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const q case 0: break; case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict); - return; + goto exit; } } } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } if (!FoundUpdate) debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name.c, DNSTypeName(our->resrec.rrtype)); +exit: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr) @@ -3950,25 +4443,22 @@ mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceR // ProcessQuery examines a received query to see if we have any answers to give mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end, - const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, + const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast, DNSMessage *const response) { - AuthRecord *ResponseRecords = mDNSNULL; - AuthRecord **nrp = &ResponseRecords; - CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated - CacheRecord **eap = &ExpectedAnswers; - DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet - DNSQuestion **dqp = &DupQuestions; - mDNSs32 delayresponse = 0; - mDNSBool HaveUnicastAnswer = mDNSfalse; - const mDNSu8 *ptr = query->data; - mDNSu8 *responseptr = mDNSNULL; - AuthRecord *rr, *rr2; + AuthRecord *ResponseRecords = mDNSNULL; + AuthRecord **nrp = &ResponseRecords; + CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated + CacheRecord **eap = &ExpectedAnswers; + DNSQuestion *DupQuestions = mDNSNULL; // Our questions that are identical to questions in this packet + DNSQuestion **dqp = &DupQuestions; + mDNSs32 delayresponse = 0; + mDNSBool SendLegacyResponse = mDNSfalse; + const mDNSu8 *ptr = query->data; + mDNSu8 *responseptr = mDNSNULL; + AuthRecord *rr; int i; - // If TC flag is set, it means we should expect that additional known answers may be coming in another packet. - if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms - // *** // *** 1. Parse Question Section and mark potential answers // *** @@ -4004,7 +4494,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq)) + if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) { if (rr->resrec.RecordType == kDNSRecordTypeUnique) ResolveSimultaneousProbe(m, query, end, &pktq, rr); @@ -4012,9 +4502,9 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { NumAnswersForThisQuestion++; // Notes: - // NR_AnswerTo pointing into query packet means "answer via unicast" - // (may also choose to do multicast as well) - // NR_AnswerTo == ~0 means "definitely answer via multicast" (can't downgrade to unicast later) + // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may also choose to multicast as well) + // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier) + // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later) if (QuestionNeedsMulticastResponse) { // We only mark this question for sending if it is at least one second since the last time we multicast it @@ -4024,20 +4514,21 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) rr->NR_AnswerTo = (mDNSu8*)~0; } - else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = ptr; + else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1; } } } + // If we couldn't answer this question, someone else might be able to, + // so use random delay on response to reduce collisions + if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms + // We only do the following accelerated cache expiration processing and duplicate question suppression processing // for multicast queries with multicast responses. // For any query generating a unicast response we don't do this because we can't assume we will see the response if (QuestionNeedsMulticastResponse) { CacheRecord *rr; - // If we couldn't answer this question, someone else might be able to, - // so use random delay on response to reduce collisions - if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms // Make a list indicating which of our own cache records we expect to see updated as a result of this query // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated @@ -4082,25 +4573,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** // *** 3. Add additional records // *** - for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put - { - // (Note: This is an "if", not a "while". If we add a record, we'll find it again - // later in the "for" loop, and we will follow further "additional" links then.) - if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID)) - AddRecordToResponseList(&nrp, rr->Additional1, rr); - - if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID)) - AddRecordToResponseList(&nrp, rr->Additional2, rr); - - // For SRV records, automatically add the Address record(s) for the target host - if (rr->resrec.rrtype == kDNSType_SRV) - for (rr2=m->ResourceRecords; rr2; rr2=rr2->next) // Scan list of resource records - 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 && // ... whose name is the name of the SRV target - SameDomainName(&rr->resrec.rdata->u.srv.target, &rr2->resrec.name)) - AddRecordToResponseList(&nrp, rr2, rr); - } + AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); // *** // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list @@ -4108,26 +4581,25 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con for (i=0; ih.numAnswers; i++) // For each record in the query's answer section... { // Get the record... - LargeCacheRecord pkt; AuthRecord *rr; CacheRecord *ourcacherr; - ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); + ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec); if (!ptr) goto exit; // See if this Known-Answer suppresses any of our currently planned answers for (rr=ResponseRecords; rr; rr=rr->NextResponse) - if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&pkt.r, rr)) + if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr)) { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression) for (rr=m->ResourceRecords; rr; rr=rr->next) { // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression - if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&pkt.r, rr)) + if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr)) { if (srcaddr->type == mDNSAddrType_IPv4) { - if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zeroIPAddr; + if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr; } else if (srcaddr->type == mDNSAddrType_IPv6) { @@ -4135,9 +4607,10 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con } if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) { - rr->ImmedAnswer = mDNSNULL; + rr->ImmedAnswer = mDNSNULL; + rr->ImmedUnicast = mDNSfalse; #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES - LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, GetRRDisplayString(m, rr)); + LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr)); #endif } } @@ -4145,7 +4618,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always, // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list). - ourcacherr = FindIdenticalRecordInCache(m, &pkt.r.resrec); + ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond) { ourcacherr->MPUnansweredKA++; @@ -4159,7 +4632,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con while (*eap) { CacheRecord *rr = *eap; - if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec)) + if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) { *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; } else eap = &rr->NextInKAList; } @@ -4171,11 +4644,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con while (*dqp) { DNSQuestion *q = *dqp; - if (ResourceRecordAnswersQuestion(&pkt.r.resrec, q)) + if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q)) { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; } else dqp = &q->NextInDQList; } } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } // *** @@ -4192,16 +4666,18 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { if (rr->NR_AnswerTo) { - mDNSBool SendMulticastResponse = mDNSfalse; + mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response + mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response) // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc. if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0) SendMulticastResponse = mDNStrue; // If the client insists on a multicast response, then we'd better send one - if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue; - else if (rr->NR_AnswerTo) HaveUnicastAnswer = mDNStrue; + if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue; + else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse = mDNStrue; + else if (rr->NR_AnswerTo) SendLegacyResponse = mDNStrue; - if (SendMulticastResponse) + if (SendMulticastResponse || SendUnicastResponse) { #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES rr->ImmedAnswerMarkTime = m->timenow; @@ -4209,13 +4685,11 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con 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; - 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 + if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue; if (srcaddr->type == mDNSAddrType_IPv4) { if (mDNSIPv4AddressIsZero(rr->v4Requester)) rr->v4Requester = srcaddr->ip.v4; @@ -4228,11 +4702,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con } } } - if (rr->resrec.RecordType == kDNSRecordTypeShared) - { - if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms - else delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms - } + // If TC flag is set, it means we should expect that additional known answers may be coming in another packet, + // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11) + // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses + // else, for a simple unique record reply, we can reply immediately; no need for delay + if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms + else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms } else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0) { @@ -4279,10 +4754,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // *** // *** 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) + if (SendLegacyResponse) responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords); exit: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + // *** // *** 9. Finally, clear our link chains ready for use next time // *** @@ -4311,7 +4788,7 @@ exit: rr->LastUnansweredTime = m->timenow; if (rr->UnansweredQueries > 1) debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", - rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, GetRRDisplayString(m, rr)); + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); SetNextCacheCheckTime(m, rr); } @@ -4322,7 +4799,7 @@ exit: // Only show debugging message if this record was not about to expire anyway if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", - rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, GetRRDisplayString(m, rr)); + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer); } // Make a guess, based on the multi-packet query / known answer counts, whether we think we @@ -4343,7 +4820,7 @@ exit: // Only show debugging message if this record was not about to expire anyway if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond) debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", - rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, GetRRDisplayString(m, rr)); + rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr)); if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond) rr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query @@ -4371,16 +4848,44 @@ 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) { - DNSMessage response; - mDNSu8 *responseend = mDNSNULL; + mDNSu8 *responseend = mDNSNULL; + mDNSBool QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr); - if (!InterfaceID) + if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr)) { - 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", + LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", @@ -4389,7 +4894,7 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, return; } - 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", + verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p 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,", @@ -4397,16 +4902,16 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID, - (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), &response); + (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg); 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", + m->omsg.h.numQuestions, m->omsg.h.numQuestions == 1 ? "" : "s", + m->omsg.h.numAnswers, m->omsg.h.numAnswers == 1 ? "" : "s", + m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type); - mDNSSendDNSMessage(m, &response, responseend, InterfaceID, srcaddr, srcport); + mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, srcaddr, srcport, -1, mDNSNULL); } } @@ -4416,53 +4921,39 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSInterfaceID InterfaceID) { - 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; CacheRecord **cfp = &CacheFlushRecords; - + // All records in a DNS response packet are treated as equally valid statements of truth. If we want // to guard against spoof responses, then the only credible protection against that is cryptographic // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals; (void)srcaddr; // Currently used only for display in debugging message + (void)srcport; + (void)dstport; - 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, + 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, 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"); - // 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++; + // 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)) + return; for (i = 0; i < totalrecords && ptr && ptr < end; i++) { - LargeCacheRecord pkt; const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd); - ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &pkt); - if (!ptr) break; // Break out of the loop and clean up our CacheFlushRecords list before exiting + ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec); + if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting // 1. Check that this packet resource record does not conflict with any of ours if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set"); @@ -4471,16 +4962,16 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { AuthRecord *rr = m->CurrentRecord; m->CurrentRecord = rr->next; - if (PacketRRMatchesSignature(&pkt.r, rr)) // If interface, name, type (if verified) and class match... + if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match... { - // ... check to see if rdata is identical - if (SameRData(&pkt.r.resrec, &rr->resrec)) + // ... check to see if type and rdata are identical + if (m->rec.r.resrec.rrtype == rr->resrec.rrtype && SameRData(&m->rec.r.resrec, &rr->resrec)) { // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us - if (pkt.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState) + if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState) { // If we were planning to send on this -- and only this -- interface, then we don't need to any more - if (rr->ImmedAnswer == InterfaceID) rr->ImmedAnswer = mDNSNULL; + if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; } } else { @@ -4488,78 +4979,77 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } } } - else + // else, the packet RR has different type or different rdata -- check to see if this is a conflict + else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r)) { - // else, the packet RR has different rdata -- check to see if this is a conflict - if (pkt.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &pkt.r)) - { - debugf("mDNSCoreReceiveResponse: Our Record: %08X %08X %s", rr-> resrec.rdatahash, rr-> resrec.rdnamehash, GetRRDisplayString(m, rr)); - debugf("mDNSCoreReceiveResponse: Pkt Record: %08X %08X %s", pkt.r.resrec.rdatahash, pkt.r.resrec.rdnamehash, GetRRDisplayString(m, &pkt.r)); + debugf("mDNSCoreReceiveResponse: Our Record: %08lX %08lX %s", rr-> resrec.rdatahash, rr-> resrec.rdnamehash, ARDisplayString(m, rr)); + debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %08lX %s", m->rec.r.resrec.rdatahash, m->rec.r.resrec.rdnamehash, CRDisplayString(m, &m->rec.r)); - // If this record is marked DependentOn another record for conflict detection purposes, - // then *that* record has to be bumped back to probing state to resolve the conflict - while (rr->DependentOn) rr = rr->DependentOn; + // If this record is marked DependentOn another record for conflict detection purposes, + // then *that* record has to be bumped back to probing state to resolve the conflict + while (rr->DependentOn) rr = rr->DependentOn; - // If we've just whacked this record's ProbeCount, don't need to do it again - if (rr->ProbeCount <= DefaultProbeCountForTypeUnique) + // If we've just whacked this record's ProbeCount, don't need to do it again + if (rr->ProbeCount <= DefaultProbeCountForTypeUnique) + { + // If we'd previously verified this record, put it back to probing state and try again + if (rr->resrec.RecordType == kDNSRecordTypeVerified) { - // If we'd previously verified this record, put it back to probing state and try again - if (rr->resrec.RecordType == kDNSRecordTypeVerified) - { - debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - rr->resrec.RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique); - InitializeLastAPTime(m, rr); - RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate - } - // If we're probing for this record, we just failed - else if (rr->resrec.RecordType == kDNSRecordTypeUnique) - { - debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); - } - // We assumed this record must be unique, but we were wrong. - // (e.g. There are two mDNSResponders on the same machine giving - // different answers for the reverse mapping record.) - // This is simply a misconfiguration, and we don't try to recover from it. - else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) - { - debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record", - rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); - mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); - } - else - debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)", - rr->resrec.RecordType, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + rr->resrec.RecordType = kDNSRecordTypeUnique; + rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique); + InitializeLastAPTime(m, rr); + RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate + } + // If we're probing for this record, we just failed + else if (rr->resrec.RecordType == kDNSRecordTypeUnique) + { + debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + // We assumed this record must be unique, but we were wrong. + // (e.g. There are two mDNSResponders on the same machine giving + // different answers for the reverse mapping record.) + // This is simply a misconfiguration, and we don't try to recover from it. + else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) + { + debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record", + rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); } + else + debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)", + rr->resrec.RecordType, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype)); } - // Else, matching signature, different rdata, but not a considered a conflict. - // If the packet record has the cache-flush bit set, then we check to see if we have to re-assert our record(s) - // to rescue them (see note about "multi-homing and bridged networks" at the end of this function). - else if ((pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2) - { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } } + // Else, matching signature, different type or rdata, but not a considered a conflict. + // If the packet record has the cache-flush bit set, then we check to see if we + // have any record(s) of the same type that we should re-assert to rescue them + // (see note about "multi-homing and bridged networks" at the end of this function). + else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype) + if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2) + { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; } } } // 2. See if we want to add this packet resource record to our cache if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in { - mDNSu32 slot = HashSlot(&pkt.r.resrec.name); + const mDNSu32 slot = HashSlot(&m->rec.r.resrec.name); CacheRecord *rr; // 2a. Check if this packet resource record is already in our cache for (rr = m->rrcache_hash[slot]; rr; rr=rr->next) { // If we found this exact resource record, refresh its TTL - if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec)) + if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec)) { - if (pkt.r.resrec.rdlength > InlineCacheRDSize) + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) verbosedebugf("Found record size %5d interface %p already in cache: %s", - pkt.r.resrec.rdlength, InterfaceID, GetRRDisplayString(m, &pkt.r)); + m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); rr->TimeRcvd = m->timenow; - if (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) + if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) { // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList) @@ -4570,13 +5060,13 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { DNSQuestion *q; for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++; - rr->resrec.RecordType = pkt.r.resrec.RecordType; + rr->resrec.RecordType = m->rec.r.resrec.RecordType; } } - if (pkt.r.resrec.rroriginalttl > 0) + if (m->rec.r.resrec.rroriginalttl > 0) { - rr->resrec.rroriginalttl = pkt.r.resrec.rroriginalttl; + rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl; rr->UnansweredQueries = 0; rr->MPUnansweredQ = 0; rr->MPUnansweredKA = 0; @@ -4599,41 +5089,50 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // If packet resource record not in our cache, add it now // (unless it is just a deletion of a record we never had, in which case we don't care) - if (!rr && pkt.r.resrec.rroriginalttl > 0) + if (!rr && m->rec.r.resrec.rroriginalttl > 0) { - rr = GetFreeCacheRR(m, pkt.r.resrec.rdlength); - if (!rr) debugf("No cache space to add record for %#s", pkt.r.resrec.name.c); + rr = GetFreeCacheRR(m, m->rec.r.resrec.rdlength); + if (!rr) debugf("No cache space to add record for %#s", m->rec.r.resrec.name.c); else { RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer - *rr = pkt.r; - rr->resrec.rdata = saveptr; // and then restore it after the structure assignment + *rr = m->rec.r; + rr->resrec.rdata = saveptr; // and then restore it after the structure assignment if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) { *cfp = rr; cfp = &rr->NextInCFList; } // If this is an oversized record with external storage allocated, copy rdata to external storage - if (pkt.r.resrec.rdlength > InlineCacheRDSize) - mDNSPlatformMemCopy(pkt.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + pkt.r.resrec.rdlength); + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) + mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.rdlength); rr->next = mDNSNULL; // Clear 'next' pointer *(m->rrcache_tail[slot]) = rr; // Append this record to tail of cache slot list m->rrcache_tail[slot] = &(rr->next); // Advance tail pointer m->rrcache_used[slot]++; - //debugf("Adding RR %##s to cache (%d)", pkt.r.name.c, m->rrcache_used); + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) // If marked unique, assume we may have + rr->DelayDelivery = m->timenow + mDNSPlatformOneSecond; // to delay delivery of this 'add' event + else + rr->DelayDelivery = CheckForSoonToExpireRecords(m, &rr->resrec.name, rr->resrec.namehash, slot); + //debugf("Adding RR %##s to cache (%d)", m->rec.r.name.c, m->rrcache_used); CacheRecordAdd(m, rr); // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us SetNextCacheCheckTime(m, rr); } } } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } +exit: + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + // If we've just received one or more records with their cache flush bits set, // then scan that cache slot to see if there are any old stale records we need to flush while (CacheFlushRecords) { CacheRecord *r1 = CacheFlushRecords, *r2; + const mDNSu32 slot = HashSlot(&r1->resrec.name); CacheFlushRecords = CacheFlushRecords->NextInCFList; r1->NextInCFList = mDNSNULL; - for (r2 = m->rrcache_hash[HashSlot(&r1->resrec.name)]; r2; r2=r2->next) + for (r2 = m->rrcache_hash[slot]; r2; r2=r2->next) if (SameResourceRecordSignature(&r1->resrec, &r2->resrec) && m->timenow - r2->TimeRcvd > mDNSPlatformOneSecond) { verbosedebugf("Cache flush %p X %p %##s (%s)", r1, r2, r2->resrec.name.c, DNSTypeName(r2->resrec.rrtype)); @@ -4653,20 +5152,40 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, r2->UnansweredQueries = MaxUnansweredQueries; SetNextCacheCheckTime(m, r2); } + if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to + { + r1->DelayDelivery = CheckForSoonToExpireRecords(m, &r1->resrec.name, r1->resrec.namehash, slot); + if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); + } } } -mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, +mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSInterfaceID InterfaceID) { - 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); + DNSMessage *msg = (DNSMessage *)pkt; + const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; + const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; + mDNSu8 QR_OP; + mDNSu8 *ptr = mDNSNULL; + +#ifndef UNICAST_DISABLED + mDNSIPPort NATPort = mDNSOpaque16fromIntVal(NATMAP_PORT); + const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; + if (srcport.NotAnInteger == NATPort.NotAnInteger) + { + mDNS_Lock(m); + uDNS_ReceiveNATMap(m, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + mDNS_Unlock(m); + return; + } +#endif + if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); return; } + 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; + ptr = (mDNSu8 *)&msg->h.numQuestions; msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); @@ -4678,18 +5197,18 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS // 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); + m->PktNum++; +#ifndef UNICAST_DISABLED + if (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdateR)) + uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); + // Note: mDNSCore also needs to get access to received unicast responses +#endif if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); - 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); + else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); + else if (QR_OP != UpdateR) + 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 @@ -4734,11 +5253,11 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, const DNSQuestion *const for (q = m->Questions; q; q=q->next) // Scan our list of questions if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate { - q->ThisQInterval = question->ThisQInterval; - q->LastQTime = question->LastQTime; - q->RecentAnswers = 0; - q->DuplicateOf = FindDuplicateQuestion(m, q); - q->LastQTxTime = question->LastQTxTime; + q->ThisQInterval = question->ThisQInterval; + q->LastQTime = question->LastQTime; + q->RecentAnswerPkts = 0; + q->DuplicateOf = FindDuplicateQuestion(m, q); + q->LastQTxTime = question->LastQTxTime; SetNextQueryTime(m,q); } } @@ -4754,23 +5273,29 @@ mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const que if (question->Target.type && !ValidQuestionTarget(question)) { - LogMsg("Warning! Target.type = %d port = %u (Client forgot to initialize before calling mDNS_StartQuery?)", + LogMsg("Warning! Target.type = %ld 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, + if (!question->Target.type) // No question->Target specified, so clear TargetPort and TargetQID + { + question->TargetPort = zeroIPPort; + question->TargetQID = zeroID; + } + +#ifndef UNICAST_DISABLED + // If the client has specified 'kDNSServiceFlagsForceMulticast' // then we do a multicast query on that interface, even for unicast domains. - if (question->InterfaceID || IsLocalDomain(&question->qname)) + if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || 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; - +#else + question->uDNS_info.id = zeroID; +#endif // UNICAST_DISABLED + + //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated return(mStatus_NoCache); else @@ -4801,41 +5326,43 @@ mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const que } } - // Note: In the case where we already have the answer to this question in our cache, that may be all the client - // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would - // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds - // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately. if (!ValidateDomainName(&question->qname)) { LogMsg("Attempt to start query with invalid qname %##s %s", question->qname.c, DNSTypeName(question->qtype)); return(mStatus_Invalid); } + // Note: In the case where we already have the answer to this question in our cache, that may be all the client + // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would + // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds + // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately. if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); - question->next = mDNSNULL; - question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() - question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question - question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization - question->RecentAnswers = 0; - question->CurrentAnswers = 0; - question->LargeAnswers = 0; - question->UniqueAnswers = 0; - question->DuplicateOf = FindDuplicateQuestion(m, question); - question->NextInDQList = mDNSNULL; + question->next = mDNSNULL; + question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() + question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname)); + question->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question + question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization + question->LastAnswerPktNum = m->PktNum; + question->RecentAnswerPkts = 0; + question->CurrentAnswers = 0; + question->LargeAnswers = 0; + question->UniqueAnswers = 0; + question->DuplicateOf = FindDuplicateQuestion(m, question); + question->NextInDQList = mDNSNULL; for (i=0; iDupSuppress[i].InterfaceID = mDNSNULL; // question->InterfaceID must be already set by caller - question->SendQNow = mDNSNULL; - question->SendOnAll = mDNSfalse; - question->LastQTxTime = m->timenow; + question->SendQNow = mDNSNULL; + question->SendOnAll = mDNSfalse; + question->LastQTxTime = m->timenow; if (!question->DuplicateOf) - verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p (%p) started", - question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question); + verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p %d (%p) started", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question); else - verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p (%p) duplicate of (%p)", - question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question, question->DuplicateOf); + verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p %d (%p) duplicate of (%p)", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf); *q = question; if (question->InterfaceID == mDNSInterface_LocalOnly) @@ -4857,8 +5384,8 @@ mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const ques CacheRecord *rr; DNSQuestion **q = &m->Questions; - if (IsActiveUnicastQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question); - + if (uDNS_IsActiveQuery(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; @@ -4885,7 +5412,7 @@ mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const ques for (q = m->Questions; q; q=q->next) // Scan our list of questions if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) break; - verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %X", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), q); + verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype), q); rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count } @@ -4954,19 +5481,21 @@ mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr 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) + const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context) { question->InterfaceID = InterfaceID; question->Target = zeroAddr; question->qtype = kDNSType_PTR; question->qclass = kDNSClass_IN; + question->LongLived = mDNSfalse; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = ForceMCast; question->QuestionCallback = Callback; question->QuestionContext = Context; if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); - // 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)) +#ifndef UNICAST_DISABLED + if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname)) { question->LongLived = mDNSfalse; question->uDNS_info.id = zeroID; @@ -4974,9 +5503,25 @@ mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, } else { + mStatus status; + // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not + mDNS_Lock(m); question->LongLived = mDNStrue; - return uDNS_StartQuery(m, question); + status = uDNS_StartQuery(m, question); + mDNS_Unlock(m); + return(status); } +#else + return(mDNS_StartQuery(m, question)); +#endif // UNICAST_DISABLED + } + +mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m) + { + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue); + return(mDNSfalse); } mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) @@ -4996,15 +5541,16 @@ mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const R AssignDomainName(query->qAv4.qname, answer->rdata->u.srv.target); query->qAv6.InterfaceID = answer->InterfaceID; AssignDomainName(query->qAv6.qname, answer->rdata->u.srv.target); - mDNS_StartQuery_internal(m, &query->qAv4); - mDNS_StartQuery_internal(m, &query->qAv6); + mDNS_StartQuery(m, &query->qAv4); + // Only do the AAAA query if this machine actually has IPv6 active + if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); } // If this is not our first answer, only re-issue the address query if the target host name has changed else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) || !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target)) { - mDNS_StopQuery_internal(m, &query->qAv4); - mDNS_StopQuery_internal(m, &query->qAv6); + mDNS_StopQuery(m, &query->qAv4); + if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6); if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged) { // If we get here, it means: @@ -5023,8 +5569,9 @@ mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const R AssignDomainName(query->qAv6.qname, answer->rdata->u.srv.target); } debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query->qAv4.qname.c); - mDNS_StartQuery_internal(m, &query->qAv4); - mDNS_StartQuery_internal(m, &query->qAv6); + mDNS_StartQuery(m, &query->qAv4); + // Only do the AAAA query if this machine actually has IPv6 active + if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); } else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged) { @@ -5066,12 +5613,13 @@ mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const R mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; + //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer)); if (!AddRecord) return; if (answer->rrtype == kDNSType_A) { query->info->ip.type = mDNSAddrType_IPv4; - query->info->ip.ip.v4 = answer->rdata->u.ip; + query->info->ip.ip.v4 = answer->rdata->u.ipv4; } else if (answer->rrtype == kDNSType_AAAA) { @@ -5087,7 +5635,7 @@ mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const Reso query->GotADD = mDNStrue; query->info->InterfaceID = answer->InterfaceID; - verbosedebugf("FoundServiceInfo v%d: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); + verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's // callback function is allowed to do anything, including deleting this query and freeing its memory. @@ -5096,7 +5644,7 @@ mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const Reso if (++query->Answers >= 100) { if (answer->rrtype == kDNSType_A) - debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ip); + debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv4); else debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv6); } @@ -5120,6 +5668,9 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, AssignDomainName(query->qSRV.qname, info->name); query->qSRV.qtype = kDNSType_SRV; query->qSRV.qclass = kDNSClass_IN; + query->qSRV.LongLived = mDNSfalse; + query->qSRV.ExpectUnique = mDNStrue; + query->qSRV.ForceMCast = mDNSfalse; query->qSRV.QuestionCallback = FoundServiceInfoSRV; query->qSRV.QuestionContext = query; @@ -5129,6 +5680,9 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, AssignDomainName(query->qTXT.qname, info->name); query->qTXT.qtype = kDNSType_TXT; query->qTXT.qclass = kDNSClass_IN; + query->qTXT.LongLived = mDNSfalse; + query->qTXT.ExpectUnique = mDNStrue; + query->qTXT.ForceMCast = mDNSfalse; query->qTXT.QuestionCallback = FoundServiceInfoTXT; query->qTXT.QuestionContext = query; @@ -5138,6 +5692,9 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv4.qname.c[0] = 0; query->qAv4.qtype = kDNSType_A; query->qAv4.qclass = kDNSClass_IN; + query->qAv4.LongLived = mDNSfalse; + query->qAv4.ExpectUnique = mDNStrue; + query->qAv4.ForceMCast = mDNSfalse; query->qAv4.QuestionCallback = FoundServiceInfo; query->qAv4.QuestionContext = query; @@ -5147,6 +5704,9 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv6.qname.c[0] = 0; query->qAv6.qtype = kDNSType_AAAA; query->qAv6.qclass = kDNSClass_IN; + query->qAv6.LongLived = mDNSfalse; + query->qAv6.ExpectUnique = mDNStrue; + query->qAv6.ForceMCast = mDNSfalse; query->qAv6.QuestionCallback = FoundServiceInfo; query->qAv6.QuestionContext = query; @@ -5165,6 +5725,7 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, info->port = zeroIPPort; info->TXTlen = 0; + // We use mDNS_StartQuery_internal here because we're already holding the lock status = mDNS_StartQuery_internal(m, &query->qSRV); if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT); if (status != mStatus_NoError) mDNS_StopResolveService(m, query); @@ -5176,13 +5737,14 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query) { mDNS_Lock(m); - if (query->qSRV.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qSRV, &m->uDNS_info)) + // We use mDNS_StopQuery_internal here because we're already holding the lock + if (query->qSRV.ThisQInterval >= 0 || uDNS_IsActiveQuery(&query->qSRV, &m->uDNS_info)) mDNS_StopQuery_internal(m, &query->qSRV); - if (query->qTXT.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qTXT, &m->uDNS_info)) + if (query->qTXT.ThisQInterval >= 0 || uDNS_IsActiveQuery(&query->qTXT, &m->uDNS_info)) mDNS_StopQuery_internal(m, &query->qTXT); - if (query->qAv4.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qAv4, &m->uDNS_info)) + if (query->qAv4.ThisQInterval >= 0 || uDNS_IsActiveQuery(&query->qAv4, &m->uDNS_info)) mDNS_StopQuery_internal(m, &query->qAv4); - if (query->qAv6.ThisQInterval >= 0 || IsActiveUnicastQuery(&query->qAv6, &m->uDNS_info)) + if (query->qAv6.ThisQInterval >= 0 || uDNS_IsActiveQuery(&query->qAv6, &m->uDNS_info)) mDNS_StopQuery_internal(m, &query->qAv6); mDNS_Unlock(m); } @@ -5194,11 +5756,14 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->Target = zeroAddr; question->qtype = kDNSType_PTR; question->qclass = kDNSClass_IN; + question->LongLived = mDNSfalse; + question->ExpectUnique = mDNSfalse; + question->ForceMCast = mDNSfalse; question->QuestionCallback = Callback; question->QuestionContext = Context; - if (DomainType > mDNS_DomainTypeRegistrationDefault) return(mStatus_BadParamErr); + if (DomainType > mDNS_DomainTypeBrowseLegacy) return(mStatus_BadParamErr); if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); - if (!dom) dom = (const domainname *)"\x5local"; + if (!dom) dom = &localdomain; if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr); return(mDNS_StartQuery(m, question)); } @@ -5214,11 +5779,12 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context) { + mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo)); // Don't try to store a TTL bigger than we can represent in platform time units if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond) ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; else if (ttl == 0) // And Zero TTL is illegal - ttl = kDefaultTTLforShared; + ttl = DefaultTTLforRRType(rrtype); // Field Group 1: Persistent metadata for Authoritative Records rr->Additional1 = mDNSNULL; @@ -5230,6 +5796,8 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD rr->resrec.RecordType = RecordType; rr->HostTarget = mDNSfalse; + rr->AllowRemoteQuery = mDNSfalse; + rr->ForceMCast = mDNSfalse; // Field Group 2: Transient state for Authoritative Records (set in mDNS_Register_internal) // Field Group 3: Transient state for Cache Records (set in mDNS_Register_internal) @@ -5263,19 +5831,17 @@ mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) } mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl, - const mDNSu16 newrdlength, - RData *const newrdata, mDNSRecordUpdateCallback *Callback) + 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; - } +#ifndef UNICAST_DISABLED + mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&rr->resrec.name)); +#else + mDNSBool unicast = mDNSfalse; +#endif 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); } - + { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer)); return(mStatus_Invalid); } + mDNS_Lock(m); // If TTL is unspecified, leave TTL unchanged @@ -5283,7 +5849,7 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt // If we already have an update queued up which has not gone through yet, // give the client a chance to free that memory - if (rr->NewRData) + if (!unicast && rr->NewRData) { RData *n = rr->NewRData; rr->NewRData = mDNSNULL; // Clear the NewRData pointer ... @@ -5294,7 +5860,9 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt rr->NewRData = newrdata; rr->newrdlength = newrdlength; rr->UpdateCallback = Callback; - + + if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); } + if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)) CompleteRDataUpdate(m, rr); else @@ -5302,8 +5870,7 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt domainlabel name; domainname type, domain; DeconstructServiceName(&rr->resrec.name, &name, &type, &domain); - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; + rr->AnnounceCount = InitialAnnounceCount; // 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 @@ -5314,15 +5881,15 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt 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->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval); 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; + mDNSs32 delay = 6 - rr->UpdateCredits; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum + if (!rr->UpdateBlocked) rr->UpdateBlocked = NonZeroTime(m->timenow + delay * mDNSPlatformOneSecond); rr->ThisAPInterval *= 4; - LogMsg("Excessive update rate for %##s; delaying announcement by %d second%s", rr->resrec.name.c, delay, delay > 1 ? "s" : ""); + rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval; + LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr->resrec.name.c, delay, delay > 1 ? "s" : ""); } rr->resrec.rroriginalttl = newttl; } @@ -5360,18 +5927,22 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary // 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); - + mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, mDNS_HostNameCallback, set); + mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); + +#if ANSWER_REMOTE_HOSTNAME_QUERIES + set->RR_A .AllowRemoteQuery = mDNStrue; + set->RR_PTR .AllowRemoteQuery = mDNStrue; + set->RR_HINFO.AllowRemoteQuery = mDNStrue; +#endif // 1. Set up Address record to map from host name ("foo.local.") to IP address // 2. Set up reverse-lookup PTR record to map from our address back to our host name - AssignDomainName(set->RR_A.resrec.name, m->hostname); + AssignDomainName(set->RR_A.resrec.name, m->MulticastHostname); if (set->ip.type == mDNSAddrType_IPv4) { set->RR_A.resrec.rrtype = kDNSType_A; - set->RR_A.resrec.rdata->u.ip = set->ip.ip.v4; + set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4; // Note: This is reverse order compared to a normal dotted-decimal IP address mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]); @@ -5394,6 +5965,7 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) MakeDomainNameFromDNSNameString(&set->RR_PTR.resrec.name, buffer); set->RR_PTR.HostTarget = mDNStrue; // Tell mDNS that the target of this PTR is to be kept in sync with our host name + set->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server set->RR_A.RRSet = &primary->RR_A; // May refer to self @@ -5403,7 +5975,7 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) if (m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254) { mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data; - AssignDomainName(set->RR_HINFO.resrec.name, m->hostname); + AssignDomainName(set->RR_HINFO.resrec.name, m->MulticastHostname); set->RR_HINFO.DependentOn = &set->RR_A; mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]); p += 1 + (int)p[0]; @@ -5438,56 +6010,34 @@ mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); } -mDNSlocal void GenerateFQDN(mDNS *const m, const char *domain, mDNSBool local) +mDNSexport void mDNS_SetFQDN(mDNS *const m) { - domainname newname; - mDNS_Lock(m); - - newname.c[0] = 0; - 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; - - if (local) m->hostname = newname; - else m->uDNS_info.hostname = newname; + domainname newmname; + NetworkInterfaceInfo *intf; + AuthRecord *rr; + newmname.c[0] = 0; - //!!!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) - local ? DeadvertiseInterface(m, intf) : uDNS_DeadvertiseInterface(m, intf); + if (!AppendDomainLabel(&newmname, &m->hostlabel)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } + if (!AppendLiteralLabelString(&newmname, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; } + if (SameDomainName(&m->MulticastHostname, &newmname)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; } - // 2. Start advertising our address records using the new name - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) - local ? AdvertiseInterface(m, intf) : uDNS_AdvertiseInterface(m, intf); + mDNS_Lock(m); + AssignDomainName(m->MulticastHostname, newmname); - // 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 - 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); - } + // 1. Stop advertising our address records on all interfaces + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) DeadvertiseInterface(m, intf); -mDNSexport void mDNS_GenerateFQDN(mDNS *const m) - { - GenerateFQDN(m, "local.", mDNStrue); - } + // 2. Start advertising our address records using the new name + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->Advertise) AdvertiseInterface(m, intf); -mDNSexport void mDNS_GenerateGlobalFQDN(mDNS *const m) - { - if (!m->uDNS_info.NameRegDomain[0]) return; - GenerateFQDN(m, m->uDNS_info.NameRegDomain, mDNSfalse); + // 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); + + mDNS_Unlock(m); } mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) @@ -5516,7 +6066,7 @@ mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStat 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) { @@ -5531,21 +6081,17 @@ mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStat // 3. Generate the FQDNs from the hostlabel, // and make sure all SRV records, etc., are updated to reference our new hostname - 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); - } + mDNS_SetFQDN(m); + LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c); + } + else if (result == mStatus_MemFree) + { + // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by + // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface + debugf("mDNS_HostNameCallback: MemFree (ignored)"); } - else LogMsg("mDNS_HostNameCallback: Unknown error %d for registration of record %s", result, rr->resrec.name.c); + else + LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result, rr->resrec.name.c); } mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active) @@ -5565,6 +6111,13 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s { mDNSBool FirstOfType = mDNStrue; NetworkInterfaceInfo **p = &m->HostInterfaces; + + if (!set->InterfaceID) + { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); } + + if (!mDNSAddressIsValidNonZero(&set->mask)) + { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); } + mDNS_Lock(m); // Assume this interface will be active @@ -5616,9 +6169,9 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s for (q = m->Questions; q; q=q->next) // Scan our list of questions if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, { // then reactivate this question - q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question - q->LastQTime = m->timenow - q->ThisQInterval; - q->RecentAnswers = 0; + q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + q->LastQTime = m->timenow - q->ThisQInterval; + q->RecentAnswerPkts = 0; if (ActiveQuestion(q)) m->NextScheduledQuery = m->timenow; } @@ -5628,10 +6181,9 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID) { if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; - rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - if (rr->AnnounceCount < ReannounceCount) - rr->AnnounceCount = ReannounceCount; - rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); + rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->AnnounceCount = InitialAnnounceCount; + rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType); InitializeLastAPTime(m, rr); } } @@ -5717,11 +6269,8 @@ 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) - { - DeadvertiseInterface(m, set); - uDNS_DeadvertiseInterface(m, set); - } + if (set->Advertise) 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. @@ -5755,12 +6304,14 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu } #endif + // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing) + if (result == mStatus_NoError && rr != &sr->RR_SRV) return; + // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that if (result == mStatus_NameConflict) { - sr->Conflict = mDNStrue; // Record that this service set had a conflict - sr->RR_PTR.AnnounceCount = InitialAnnounceCount; // Make sure we don't send a goodbye for the PTR record - mDNS_DeregisterService(m, sr); // Unlink the records from our list + sr->Conflict = mDNStrue; // Record that this service set had a conflict + mDNS_DeregisterService(m, sr); // Unlink the records from our list return; } @@ -5783,6 +6334,13 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu sr->ServiceCallback(m, sr, result); } +mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; + if (sr->ServiceCallback) + sr->ServiceCallback(m, sr, result); + } + // 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. "_ipp._tcp.") @@ -5790,7 +6348,7 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu // We always register a TXT, even if it is empty (so that clients are not // left waiting forever looking for a nonexistent record.) // If the host parameter is mDNSNULL or the root domain (ASCII NUL), -// then the default host name (m->hostname1) is automatically used +// then the default host name (m->MulticastHostname) is automatically used mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, const domainlabel *const name, const domainname *const type, const domainname *const domain, const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, @@ -5809,11 +6367,14 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, if (host && host->c[0]) sr->Host = *host; else sr->Host.c[0] = 0; + // If port number is zero, that means the client is really trying to do a RegisterNoSuchService + if (!port.NotAnInteger) return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr)); + // Initialize the AuthRecord objects to sane values - mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeAdvisory, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kDefaultTTLforUnique, kDNSRecordTypeUnique, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kDefaultTTLforUnique, kDNSRecordTypeUnique, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, 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 @@ -5843,9 +6404,11 @@ 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; - mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, ServiceCallback, sr); - if (ConstructServiceName(&sr->SubTypes[i].resrec.name, &s, type, domain) == mDNSNULL) return(mStatus_BadParamErr); + domainname st; + AssignDomainName(st, sr->SubTypes[i].resrec.name); + AppendDomainName(&st, type); + mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr); + if (ConstructServiceName(&sr->SubTypes[i].resrec.name, mDNSNULL, &st, 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; @@ -5871,11 +6434,21 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, } sr->RR_TXT.DependentOn = &sr->RR_SRV; +#ifndef UNICAST_DISABLED // 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); - + if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&sr->RR_SRV.resrec.name))) + { + mStatus status; + mDNS_Lock(m); + // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates with zero-length rdata. + // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck) + if (!sr->RR_TXT.resrec.rdlength) { sr->RR_TXT.resrec.rdlength = 1; sr->RR_TXT.resrec.rdata->u.txt.c[0] = 0; } + status = uDNS_RegisterService(m, sr); + mDNS_Unlock(m); + return(status); + } +#endif mDNS_Lock(m); err = mDNS_Register_internal(m, &sr->RR_SRV); if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT); @@ -5900,22 +6473,30 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord **e; mStatus status; - if (!sr->RR_SRV.resrec.InterfaceID && !IsLocalDomain(&sr->RR_SRV.resrec.name)) + extra->next = mDNSNULL; + mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr); + AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name); + +#ifndef UNICAST_DISABLED + if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&sr->RR_SRV.resrec.name))) { - LogMsg("mDNS_AddRecordToService: Not implemented for unicast DNS"); - return mStatus_UnsupportedErr; + mDNS_Lock(m); + // Some (or perhaps all) versions of BIND named (name daemon) don't allow updates with zero-length rdata. + // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck) + if (extra->r.resrec.rrtype == kDNSType_TXT && extra->r.resrec.rdlength == 0) + { extra->r.resrec.rdlength = 1; extra->r.resrec.rdata->u.txt.c[0] = 0; } + status = uDNS_AddRecordToService(m, sr, extra); + mDNS_Unlock(m); + return status; } - +#endif + 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 - if (ttl == 0) ttl = kDefaultTTLforUnique; + if (ttl == 0) ttl = kStandardTTL; - extra->next = mDNSNULL; - mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID, extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr); - AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name); extra->r.DependentOn = &sr->RR_SRV; debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name.c); @@ -5926,17 +6507,11 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, return(status); } -mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra) +mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context) { 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; @@ -5948,7 +6523,14 @@ mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet else { debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name.c); + extra->r.RecordCallback = MemFreeCallback; + extra->r.RecordContext = Context; *e = (*e)->next; +#ifndef UNICAST_DISABLED + if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&sr->RR_SRV.resrec.name))) + status = uDNS_DeregisterRecord(m, &extra->r); + else +#endif status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal); } mDNS_Unlock(m); @@ -5998,17 +6580,27 @@ 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 port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService() + if (!sr->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV)); +#ifndef UNICAST_DISABLED + if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&sr->RR_SRV.resrec.name))) + { + mStatus status; + mDNS_Lock(m); + status = uDNS_DeregisterService(m, sr); + mDNS_Unlock(m); + return(status); + } +#endif if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered) { - debugf("Service set for %##s already deregistered", sr->RR_PTR.resrec.name.c); + debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name.c); return(mStatus_BadReferenceErr); } else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering) { - debugf("Service set for %##s already in the process of deregistering", sr->RR_PTR.resrec.name.c); + debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name.c); return(mStatus_NoError); } else @@ -6059,7 +6651,7 @@ mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const r const domainname *const host, const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context) { - mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kDefaultTTLforUnique, kDNSRecordTypeUnique, Callback, Context); + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context); if (ConstructServiceName(&rr->resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr); rr->resrec.rdata->u.srv.priority = 0; rr->resrec.rdata->u.srv.weight = 0; @@ -6072,7 +6664,7 @@ mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const r mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname) { - mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kDefaultTTLforShared, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); + mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); if (!MakeDomainNameFromDNSNameString(&rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr); if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr); return(mDNS_Register(m, rr)); @@ -6109,19 +6701,21 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context) { mDNSu32 slot; - mDNSs32 timenow; - mStatus result = mDNSPlatformTimeInit(&timenow); - if (result != mStatus_NoError) return(result); + mDNSs32 timenow, timenow_adjust; + mStatus result; if (!rrcachestorage) rrcachesize = 0; m->p = p; m->KnownBugs = 0; - m->CanReceiveUnicast = mDNSfalse; // Assume we can't receive unicasts, unless platform layer tells us otherwise + m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; m->mDNSPlatformStatus = mStatus_Waiting; + m->UnicastPort4 = zeroIPPort; + m->UnicastPort6 = zeroIPPort; m->MainCallback = Callback; m->MainContext = Context; + m->rec.r.resrec.RecordType = 0; // For debugging: To catch and report locking failures m->mDNS_busy = 0; @@ -6132,9 +6726,14 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->lock_Records = 0; // Task Scheduling variables + result = mDNSPlatformTimeInit(); + if (result != mStatus_NoError) return(result); + timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF); + timenow = mDNSPlatformRawTime() + timenow_adjust; + m->timenow = 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section m->timenow_last = timenow; - m->timenow_adjust = 0; + m->timenow_adjust = timenow_adjust; m->NextScheduledEvent = timenow; m->SuppressSending = timenow; m->NextCacheCheck = timenow + 0x78000000; @@ -6143,6 +6742,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->NextScheduledResponse = timenow + 0x78000000; m->ExpectUnicastResponse = timenow + 0x78000000; m->RandomQueryDelay = 0; + m->PktNum = 0; m->SendDeregistrations = mDNSfalse; m->SendImmediateAnswers = mDNSfalse; m->SleepState = mDNSfalse; @@ -6171,7 +6771,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, // Fields below only required for mDNS Responder... m->hostlabel.c[0] = 0; m->nicelabel.c[0] = 0; - m->hostname.c[0] = 0; + m->MulticastHostname.c[0] = 0; m->HIHardware.c[0] = 0; m->HISoftware.c[0] = 0; m->ResourceRecords = mDNSNULL; @@ -6185,7 +6785,9 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->NumFailedProbes = 0; m->SuppressProbes = 0; +#ifndef UNICAST_DISABLED uDNS_Init(m); +#endif result = mDNSPlatformInit(m); return(result); @@ -6214,6 +6816,9 @@ mDNSexport void mDNS_Close(mDNS *const m) m->mDNS_shutdown = mDNStrue; +#ifndef UNICAST_DISABLED + uDNS_Close(m); +#endif rrcache_totalused = m->rrcache_totalused; for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) { @@ -6228,7 +6833,7 @@ mDNSexport void mDNS_Close(mDNS *const m) // Reset tail pointer back to empty state (not that it really matters on exit, but we'll do it anyway, for the sake of completeness) m->rrcache_tail[slot] = &m->rrcache_hash[slot]; } - debugf("mDNS_Close: RR Cache was using %ld records, %d active", rrcache_totalused, rrcache_active); + debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active); if (rrcache_active != m->rrcache_active) LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active); @@ -6252,8 +6857,6 @@ 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/mDNSDebug.h b/mDNSCore/mDNSDebug.h index bcb0851..8f95d42 100755 --- a/mDNSCore/mDNSDebug.h +++ b/mDNSCore/mDNSDebug.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: mDNSDebug.h,v $ +Revision 1.24 2004/09/16 01:58:21 cheshire +Fix compiler warnings + Revision 1.23 2004/05/18 23:51:25 cheshire Tidy up all checkin comments to use consistent "" format for bug numbers @@ -88,6 +89,7 @@ Merge in license terms from Quinn's copy, in preparation for Darwin release // (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.) +//#undef MDNS_DEBUGMSGS //#define MDNS_DEBUGMSGS 2 // Set MDNS_CHECK_PRINTF_STYLE_FUNCTIONS to 1 to enable extra GCC compiler warnings @@ -136,8 +138,8 @@ extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1 // 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, ...); +extern void LogMsgIdent(const char *ident, const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(2,3); +extern void LogMsgNoIdent(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); // 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() diff --git a/mDNSCore/mDNSClientAPI.h b/mDNSCore/mDNSEmbeddedAPI.h similarity index 72% rename from mDNSCore/mDNSClientAPI.h rename to mDNSCore/mDNSEmbeddedAPI.h index df404f7..ac0215d 100755 --- a/mDNSCore/mDNSClientAPI.h +++ b/mDNSCore/mDNSEmbeddedAPI.h @@ -3,8 +3,6 @@ * * @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,9 +20,317 @@ * * @APPLE_LICENSE_HEADER_END@ + + NOTE: + If you're building an application that uses DNS Service Discovery + this is probably NOT the header file you're looking for. + In most cases you will want to use /usr/include/dns_sd.h instead. + + This header file defines the lowest level raw interface to mDNSCore, + which is appropriate *only* on tiny embedded systems where everything + runs in a single address space and memory is extremely constrained. + All the APIs here are malloc-free, which means that the caller is + responsible for passing in a pointer to the relevant storage that + will be used in the execution of that call, and (when called with + correct parameters) all the calls are guaranteed to succeed. There + is never a case where a call can suffer intermittent failures because + the implementation calls malloc() and sometimes malloc() returns NULL + because memory is so limited that no more is available. + This is primarily for devices that need to have precisely known fixed + memory requirements, with absolutely no uncertainty or run-time variation, + but that certainty comes at a cost of more difficult programming. + + For applications running on general-purpose desktop operating systems + (Mac OS, Linux, Solaris, Windows, etc.) the API you should use is + /usr/include/dns_sd.h, which defines the API by which multiple + independent client processes communicate their DNS Service Discovery + requests to a single "mdnsd" daemon running in the background. + + Even on platforms that don't run multiple independent processes in + multiple independent address spaces, you can still use the preferred + dns_sd.h APIs by linking in "dnssd_clientshim.c", which implements + the standard "dns_sd.h" API calls, allocates any required storage + using malloc(), and then calls through to the low-level malloc-free + mDNSCore routines defined here. This has the benefit that even though + you're running on a small embedded system with a single address space, + you can still use the exact same client C code as you'd use on a + general-purpose desktop system. + + Change History (most recent first): -$Log: mDNSClientAPI.h,v $ +$Log: mDNSEmbeddedAPI.h,v $ +Revision 1.260 2004/12/12 23:51:42 ksekar + Wide-area registrations should fallback to using DHCP hostname as target + +Revision 1.259 2004/12/11 20:55:29 ksekar + Clean up registration state machines + +Revision 1.258 2004/12/10 20:48:32 cheshire + Need to pick final EDNS numbers for LLQ and GC + +Revision 1.257 2004/12/10 02:09:23 cheshire + Modify default TTLs + +Revision 1.256 2004/12/09 03:15:40 ksekar + use _legacy instead of _default to find "empty string" browse domains + +Revision 1.255 2004/12/07 22:48:37 cheshire +Tidying + +Revision 1.254 2004/12/07 21:26:04 ksekar + DNSServiceRegisterRecord() can crash on deregistration + +Revision 1.253 2004/12/07 20:42:33 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.252 2004/12/07 03:02:12 ksekar +Fixed comments, grouped unicast-specific routines together + +Revision 1.251 2004/12/06 21:15:22 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.250 2004/12/04 02:12:45 cheshire + mDNSResponder puts LargeCacheRecord on the stack + +Revision 1.249 2004/12/03 05:18:33 ksekar + mDNSResponder needs to return more specific TSIG errors + +Revision 1.248 2004/12/02 20:03:48 ksekar + Rendezvous still publishes wide-area domains even after switching to a local subnet + +Revision 1.247 2004/12/01 20:57:19 ksekar + Wide Area Rendezvous must be split-DNS aware + +Revision 1.246 2004/11/29 23:26:32 cheshire +Added NonZeroTime() function, which usually returns the value given, with the exception +that if the value given is zero, it returns one instead. For timer values where zero is +used to mean "not set", this can be used to ensure that setting them to the result of an +interval computation (e.g. "now+interval") does not inadvertently result in a zero value. + +Revision 1.245 2004/11/25 01:28:09 cheshire + Need to implement random delay for 'QU' unicast replies (and set cache flush bit too) + +Revision 1.244 2004/11/24 22:00:59 cheshire +Move definition of mDNSAddressIsAllDNSLinkGroup() from mDNSMacOSX.c to mDNSEmbeddedAPI.h + +Revision 1.243 2004/11/23 22:43:53 cheshire +Tidy up code alignment + +Revision 1.242 2004/11/23 03:39:46 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.241 2004/11/22 17:16:19 ksekar + Unicast services don't disappear when you disable all networking + +Revision 1.240 2004/11/19 02:32:43 ksekar +Wide-Area Rendezvous Security: Add LLQ-ID to events + +Revision 1.239 2004/11/15 20:09:23 ksekar + Wide Area support for Add/Remove record + +Revision 1.238 2004/11/12 03:16:48 rpantos +rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName + +Revision 1.237 2004/11/10 20:40:53 ksekar + LLQ mobility fragile on non-primary interface + +Revision 1.236 2004/11/01 20:36:11 ksekar + mDNSResponder should not receive Keychain Notifications + +Revision 1.235 2004/11/01 17:48:14 cheshire +Changed SOA serial number back to signed. RFC 1035 may describe it as "unsigned", but +it's wrong. The SOA serial is a modular counter, as explained in "DNS & BIND", page +137. Since C doesn't have a modular type, we used signed, C's closest approximation. + +Revision 1.234 2004/10/29 21:59:02 ksekar +SOA serial should be a unsigned integer, as per RFC 1035 + +Revision 1.233 2004/10/28 03:24:41 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.232 2004/10/26 06:20:23 cheshire +Add mDNSAddressIsValidNonZero() macro + +Revision 1.231 2004/10/26 06:11:41 cheshire +Add improved logging to aid in diagnosis of mDNSResponder crashed + +Revision 1.230 2004/10/26 03:52:02 cheshire +Update checkin comments + +Revision 1.229 2004/10/25 19:30:52 ksekar + Simplify dynamic host name structures + +Revision 1.228 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.227 2004/10/22 20:52:07 ksekar + Create NAT port mappings for Long Lived Queries + +Revision 1.226 2004/10/20 01:50:40 cheshire + Cannot resolve non-local registrations using the mach API +Implemented ForceMCast mode for AuthRecords as well as for Questions + +Revision 1.225 2004/10/19 21:33:17 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.224 2004/10/16 00:16:59 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.223 2004/10/15 23:00:17 ksekar + Need to update LLQs on location changes + +Revision 1.222 2004/10/12 02:49:20 ksekar + Clean up LLQ sleep/wake, error handling + +Revision 1.221 2004/10/10 06:57:15 cheshire +Change definition of "localdomain" to make code compile a little smaller + +Revision 1.220 2004/10/06 01:44:19 cheshire + Resolving too quickly sometimes returns stale TXT record + +Revision 1.219 2004/10/03 23:18:58 cheshire +Move address comparison macros from DNSCommon.h to mDNSEmbeddedAPI.h + +Revision 1.218 2004/10/03 23:14:12 cheshire +Add "mDNSEthAddr" type and "zeroEthAddr" constant + +Revision 1.217 2004/09/30 00:24:56 ksekar + Dynamically update default registration domains on config change + +Revision 1.216 2004/09/27 23:24:32 cheshire +Fix typo: SOA refresh interval is supposed to be unsigned + +Revision 1.215 2004/09/26 23:20:35 ksekar + Allow default registrations in multiple wide-area domains + +Revision 1.214 2004/09/25 02:41:39 cheshire + Deliver near-pending "remove" events before new "add" events + +Revision 1.213 2004/09/25 02:24:27 cheshire +Removed unused rr->UseCount + +Revision 1.212 2004/09/24 20:57:39 cheshire + Eliminate inappropriate casts that cause misaligned-address errors + +Revision 1.211 2004/09/24 20:33:22 cheshire +Remove unused DNSDigest_MD5 declaration + +Revision 1.210 2004/09/23 20:21:07 cheshire + Refine "immediate answer burst; restarting exponential backoff sequence" logic +Associate a unique sequence number with each received packet, and only increment the count of recent answer +packets if the packet sequence number for this answer record is not one we've already seen and counted. + +Revision 1.209 2004/09/23 20:14:39 cheshire +Rename "question->RecentAnswers" to "question->RecentAnswerPkts" + +Revision 1.208 2004/09/23 00:50:53 cheshire + Don't send a (DE) if a service is unregistered after wake from sleep + +Revision 1.207 2004/09/22 02:34:46 cheshire +Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h + +Revision 1.206 2004/09/22 00:41:59 cheshire +Move tcp connection status codes into the legal range allocated for mDNS use + +Revision 1.205 2004/09/21 23:40:11 ksekar + mDNSResponder to return errors on NAT traversal failure + +Revision 1.204 2004/09/21 23:29:50 cheshire + DNSServiceResolve should delay sending packets + +Revision 1.203 2004/09/21 20:58:22 cheshire +Add ifname field to NetworkInterfaceInfo_struct + +Revision 1.202 2004/09/17 00:46:34 cheshire +mDNS_TimeNow should take const mDNS parameter + +Revision 1.201 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.200 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.199 2004/09/16 21:59:16 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.198 2004/09/16 21:36:36 cheshire + Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.197 2004/09/16 00:24:48 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.196 2004/09/14 23:42:35 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.195 2004/09/14 23:27:46 cheshire +Fix compile errors + +Revision 1.194 2004/09/10 00:49:57 cheshire + Add error code kDNSServiceErr_Firewall, for future use + +Revision 1.193 2004/09/03 19:23:05 ksekar +: Need retransmission mechanism for wide-area service registrations + +Revision 1.192 2004/09/02 03:48:47 cheshire + Disable targeted unicast query support by default +1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record +2. New field AllowRemoteQuery in AuthRecord structure +3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set +4. mDNS.c only answers remote queries if AllowRemoteQuery is set + +Revision 1.191 2004/08/25 00:37:27 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.190 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.189 2004/08/14 03:22:41 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.188 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.187 2004/08/13 23:37:02 cheshire +Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with +"uDNS_info.UnicastHostname" for clarity + +Revision 1.186 2004/08/13 23:25:00 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + +Revision 1.185 2004/08/12 00:32:36 ksekar +: LLQ Refreshes never terminate if unanswered + +Revision 1.184 2004/08/11 17:09:31 cheshire +Add comment clarifying the applicability of these APIs + +Revision 1.183 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.182 2004/07/30 17:40:06 ksekar +: TXT Record updates not available for wide-area services + +Revision 1.181 2004/07/29 19:27:15 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.180 2004/07/29 02:03:35 ksekar +Delete unused #define and structure field + +Revision 1.179 2004/07/26 22:49:30 ksekar +: Feature #9516: Need support for NATPMP in client + +Revision 1.178 2004/07/13 21:24:24 rpantos +Fix for . + Revision 1.177 2004/06/05 00:04:26 cheshire : wide-area domains should be returned in reg. domain enumeration @@ -54,7 +360,7 @@ 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" +from mDNSMacOSX.h to mDNSEmbeddedAPI.h), implemented 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. @@ -119,9 +425,9 @@ 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 +Added an asynchronous 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 +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 @@ -162,7 +468,7 @@ 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 +Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSEmbeddedAPI.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" @@ -202,7 +508,7 @@ Revision 1.125 2003/11/22 00:18:27 cheshire Add compile-time asserts to verify correct sizes of mDNSu32, mDNSOpaque16, etc. Revision 1.124 2003/11/20 22:59:54 cheshire -Changed runtime checks in mDNS.c to be compile-time checks in mDNSClientAPI.h +Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work. Revision 1.123 2003/11/20 22:53:01 cheshire @@ -216,13 +522,13 @@ 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. +Best solution is just to combine mDNSEmbeddedAPI.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 +Move AssignDomainName macro to mDNSEmbeddedAPI.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. @@ -302,7 +608,7 @@ We want to avoid touching the rdata pages, so we don't page them in. Revision 1.100 2003/08/14 19:29:04 cheshire Include cache records in SIGINFO output -Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSClientAPI.h so daemon.c can use them +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 @@ -346,10 +652,10 @@ To eliminate compiler warnings, changed definition of mDNSBool from for the result of comparison operators (a: Feature: New Rendezvous APIs (#7875) (mDNSResponder component) +: Feature: New DNS-SD APIs (#7875) (mDNSResponder component) Added error type for incompatibility between daemon and client versions Revision 1.85 2003/07/19 03:23:13 cheshire @@ -589,7 +895,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-existance +Added mDNS_RegisterNoSuchService() function for assertion of non-existence of a particular named service Revision 1.23 2002/09/19 21:25:34 cheshire @@ -739,13 +1045,15 @@ typedef struct mDNSInterfaceID_dummystruct { void *dummy; } *mDNSInterfaceID; // 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[ 2]; mDNSu16 NotAnInteger; } mDNSOpaque16; +typedef packedunion { mDNSu8 b[ 4]; mDNSu32 NotAnInteger; } mDNSOpaque32; +typedef packedunion { mDNSu8 b[ 6]; mDNSu16 w[3]; mDNSu32 l[1]; } mDNSOpaque48; 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) +typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opaque identifier (not an integer) enum { @@ -775,7 +1083,7 @@ enum // the bottom end of the range (FFFE FF00) is used for non-error values; // Error codes: - mStatus_UnknownErr = -65537, // 0xFFFE FFFF + mStatus_UnknownErr = -65537, // First value: 0xFFFE FFFF mStatus_NoSuchNameErr = -65538, mStatus_NoMemoryErr = -65539, mStatus_BadParamErr = -65540, @@ -788,23 +1096,32 @@ enum mStatus_AlreadyRegistered = -65547, mStatus_NameConflict = -65548, mStatus_Invalid = -65549, - // = -65550, + mStatus_Firewall = -65550, mStatus_Incompatible = -65551, mStatus_BadInterfaceErr = -65552, mStatus_Refused = -65553, mStatus_NoSuchRecord = -65554, - mStatus_NoAuth = -65555, - // -65556 - -65789 currently unused + mStatus_NoAuth = -65555, + mStatus_NoSuchKey = -65556, + mStatus_NATTraversal = -65557, + mStatus_DblNAT = -65558, + mStatus_BadTime = -65559, + mStatus_BadSig = -65560, // while we define this per RFC 2845, BIND 9 returns Refused for bad/missing signatures + mStatus_BadKey = -65561, + + // -65562 to -65786 currently unused; available for allocation + + // tcp connection status + mStatus_ConnPending = -65787, + mStatus_ConnFailed = -65788, + mStatus_ConnEstablished = -65789, // Non-error values: mStatus_GrowCache = -65790, mStatus_ConfigChanged = -65791, - mStatus_MemFree = -65792, // 0xFFFE FF00 - - // tcp connection status - mStatus_ConnectionPending = -65793, - mStatus_ConnectionFailed = -65794, - mStatus_ConnectionEstablished = -65795 + mStatus_MemFree = -65792 // Last value: 0xFFFE FF00 + + // mStatus_MemFree is the last legal mDNS error code, at the end of the range allocated for mDNS }; typedef mDNSs32 mStatus; @@ -834,6 +1151,15 @@ typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string #define MAX_ESCAPED_DOMAIN_LABEL 254 #define MAX_ESCAPED_DOMAIN_NAME 1005 +// Most records have a TTL of 75 minutes, so that their 80% cache-renewal query occurs once per hour. +// For records containing a hostname (in the name on the left, or in the rdata on the right), +// like A, AAAA, reverse-mapping PTR, and SRV, we use a two-minute TTL by default, because we don't want +// them to hang around for too long in the cache if the host in question crashes or otherwise goes away. +#define kStandardTTL (3600 * 100 / 80) +#define kHostNameTTL 120 + +#define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL) + // *************************************************************************** #if 0 #pragma mark - DNS Message structures @@ -938,24 +1264,34 @@ enum 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 + kDNSRecordTypePacketAdd = 0x80, // Received in the Additional Section of a DNS Response + kDNSRecordTypePacketAddUnique = 0x90, // Received in the Additional Section of a DNS Response with kDNSClass_UniqueRRSet set + kDNSRecordTypePacketAuth = 0xA0, // Received in the Authorities Section of a DNS Response + kDNSRecordTypePacketAuthUnique = 0xB0, // Received in the Authorities Section of a DNS Response with kDNSClass_UniqueRRSet set + kDNSRecordTypePacketAns = 0xC0, // Received in the Answer Section of a DNS Response + kDNSRecordTypePacketAnsUnique = 0xD0, // 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 + kDNSRecordTypePacketAnsMask = 0x40, // True for PacketAns and PacketAnsUnique + kDNSRecordTypePacketUniqueMask = 0x10 // True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique }; 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 + { + domainname mname; + domainname rname; + mDNSs32 serial; // Modular counter; increases when zone changes + mDNSu32 refresh; // Time in seconds that a slave waits after successful replication of the database before it attempts replication again + mDNSu32 retry; // Time in seconds that a slave waits after an unsuccessful replication attempt before it attempts replication again + mDNSu32 expire; // Time in seconds that a slave holds on to old data while replication attempts remain unsuccessful + mDNSu32 min; // Nominally the minimum record TTL for this zone, in seconds; also used for negative caching. + } rdataSOA; typedef packedstruct { mDNSu16 vers; - mDNSu16 llqOp; + mDNSu16 llqOp; mDNSu16 err; mDNSu8 id[8]; mDNSu32 lease; @@ -989,14 +1325,14 @@ typedef packedstruct typedef union { mDNSu8 data[StandardAuthRDSize]; - mDNSv4Addr ip; // For 'A' record + mDNSv4Addr ipv4; // For 'A' record mDNSv6Addr ipv6; // For 'AAAA' record 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 + rdataSOA soa; // For SOA record + rdataOpt opt; // For eDNS0 opt record } RDataBody; typedef struct @@ -1012,6 +1348,7 @@ typedef struct ResourceRecord_struct ResourceRecord; typedef struct DNSQuestion_struct DNSQuestion; typedef struct mDNS_struct mDNS; typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; +typedef struct NATTraversalInfo_struct NATTraversalInfo; // 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); @@ -1041,7 +1378,8 @@ struct ResourceRecord_struct RData *rdata; // Pointer to storage for this rdata }; -enum +// Unless otherwise noted, states may apply to either independent record registrations or service registrations +typedef enum { regState_FetchingZoneData = 1, // getting info - update not sent regState_Pending = 2, // update sent, reply not received @@ -1049,24 +1387,45 @@ enum 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; - + regState_Refresh = 9, // outstanding refresh (or target change) message + regState_NATMap = 10, // establishing NAT port mapping or learning public address + regState_UpdatePending = 11, // update in flight as result of mDNS_Update call + regState_NoTarget = 12, // service registration pending registration of hostname (ServiceRegistrations only) + regState_ExtraQueued = 13 // extra record to be registered upon completion of service registration (RecordRegistrations only) + } regState_t; + +// context for both ServiceRecordSet and individual AuthRec structs 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) + // registration/lease state + regState_t state; + mDNSBool lease; // dynamic update contains (should contain) lease option + mDNSs32 expire; // expiration of lease (-1 for static) + mDNSBool TestForSelfConflict; // on name conflict, check if we're just seeing our own orphaned records + + // identifier to match update request and response + mDNSOpaque16 id; + + // server info + 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 + + // NAT traversal context + NATTraversalInfo *NATinfo; // may be NULL + + // state for deferred operations + mDNSBool ClientCallbackDeferred; // invoke client callback on completion of pending operation(s) + mStatus DeferredStatus; // status to deliver when above flag is set + mDNSBool SRVUpdateDeferred; // do we need to change target or port once current operation completes? + mDNSBool LostTarget; // temporarily deregistered service because its target was deregistered + + // uDNS_UpdateRecord support fields + mDNSBool UpdateQueued; // Update the rdata once the current pending operation completes + RData *UpdateRData; // Pointer to new RData while a record update is in flight + mDNSu16 UpdateRDLen; // length of above field + mDNSRecordUpdateCallback *UpdateRDCallback; // client callback to free old rdata } uDNS_RegInfo; struct AuthRecord_struct @@ -1078,23 +1437,27 @@ struct AuthRecord_struct AuthRecord *next; // Next in list; first element of structure for efficiency reasons ResourceRecord resrec; - uDNS_RegInfo uDNS_info; + 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 AuthRecord *RRSet; // This unique record is part of an RRSet - mDNSRecordCallback *RecordCallback; // Callback function to call for state changes + mDNSRecordCallback *RecordCallback; // Callback function to call for state changes, and to free memory asynchronously on deregistration 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 + mDNSu8 AllowRemoteQuery; // Set if we allow hosts not on the local link to query this record + mDNSu8 ForceMCast; // Set by client to advertise solely via multicast, even for apparently unicast names // 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 RequireGoodbye; // Set if this RR has been announced on the wire and will require a goodbye packet 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) + mDNSu8 ImmedUnicast; // Set if we may send our response directly via unicast to the requester #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES mDNSs32 ImmedAnswerMarkTime; #endif @@ -1123,6 +1486,13 @@ struct AuthRecord_struct // DO NOT ADD ANY MORE FIELDS HERE }; +// Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field +typedef struct ARListElem + { + struct ARListElem *next; + AuthRecord ar; // Note: Must be last struct in field to accomodate oversized AuthRecords + } ARListElem; + struct CacheRecord_struct { CacheRecord *next; // Next in list; first element of structure for efficiency reasons @@ -1131,9 +1501,9 @@ struct CacheRecord_struct // 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 DelayDelivery; // Set if we want to defer delivery of this answer to local clients 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 @@ -1152,14 +1522,22 @@ typedef struct mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize]; // Glue on the necessary number of extra bytes } LargeCacheRecord; -typedef struct NetworkInterfaceInfo_struct NetworkInterfaceInfo; +typedef struct uDNS_HostnameInfo + { + struct uDNS_HostnameInfo *next; + AuthRecord *ar; // registered address record + mDNSRecordCallback *StatusCallback; // callback to deliver success or error code to client layer + const void *StatusContext; // Client Context + } uDNS_HostnameInfo; -typedef struct +typedef struct DNSServer { - 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; + struct DNSServer *next; + mDNSAddr addr; + domainname domain; // name->server matching for "split dns" + } DNSServer; + +typedef struct NetworkInterfaceInfo_struct 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 @@ -1184,11 +1562,11 @@ 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; // Identifies physical interface; MUST NOT be 0, -1, or -2 mDNSAddr ip; // The IPv4 or IPv6 address to advertise + mDNSAddr mask; + char ifname[16]; mDNSBool Advertise; // False if you are only searching on this interface mDNSBool McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ? }; @@ -1197,6 +1575,7 @@ typedef struct ExtraResourceRecord_struct ExtraResourceRecord; struct ExtraResourceRecord_struct { ExtraResourceRecord *next; + mDNSu32 ClientID; // Opaque ID field to be used by client to map an AddRecord call to a set of Extra records 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 @@ -1211,9 +1590,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. - ServiceRecordSet *next; - uDNS_RegInfo uDNS_info; - 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; @@ -1248,15 +1627,20 @@ typedef struct typedef enum { + // Setup states 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_Refresh = 4, + LLQ_Retry = 5, + LLQ_Established = 6, + LLQ_Suspended = 7, + LLQ_SuspendDeferred = 8, // suspend once we get zone info + LLQ_SuspendedPoll = 9, // suspended from polling state + LLQ_NatMapWait = 10, + + // Established/error states LLQ_Static = 16, LLQ_Poll = 17, LLQ_Error = 18, @@ -1265,21 +1649,22 @@ typedef enum typedef struct { - LLQ_State state; - mDNSAddr servAddr; - mDNSIPPort servPort; - DNSQuestion *question; - mDNSu32 origLease; // seconds (relative) - mDNSs32 retry; // ticks (absolute) - mDNSs32 expire; // ticks (absolute) + 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; + mDNSu8 id[8]; + mDNSBool deriveRemovesOnResume; + mDNSBool NATMap; // does this LLQ use the global LLQ NAT mapping? } LLQ_Info; // LLQ constants -#define kDNSOpt_LLQ 1 //!!!KRS -#define kDNSOpt_Lease 2 //!!!KRS +#define kDNSOpt_LLQ 1 +#define kDNSOpt_Lease 2 #define kLLQ_Vers 0 // prerelease #define kLLQ_DefLease 7200 // 2 hours #define kUpdate_DefLease 7200 @@ -1287,8 +1672,9 @@ typedef struct #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 kLLQOp_Setup 1 +#define kLLQOp_Refresh 2 +#define kLLQOp_Event 3 #define LLQ_OPT_SIZE (2 * sizeof(mDNSu16)) + sizeof(LLQOptData) #define LEASE_OPT_SIZE (2 * sizeof(mDNSu16)) + sizeof(mDNSs32) @@ -1307,15 +1693,16 @@ enum 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 + { + mDNSOpaque16 id; + mDNSBool internal; + InternalResponseHndlr responseCallback; // NULL if internal field is false + LLQ_Info *llq; // NULL for 1-shot queries + mDNSBool Answered; // have we received an answer (including NXDOMAIN) for this question? CacheRecord *knownAnswers; + mDNSs32 RestartTime; // Mark when we restart a suspended query void *context; - } uDNS_QuestionInfo; + } 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); @@ -1324,12 +1711,14 @@ 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 DelayAnswering; // Set if we want to defer answering this question until the cache settles 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 + mDNSs32 LastAnswerPktNum; // The sequence number of the last response packet containing an answer to this Q + mDNSu32 RecentAnswerPkts; // 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 @@ -1339,7 +1728,7 @@ 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; + 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 queries only on a single specific IP interface @@ -1349,10 +1738,11 @@ struct DNSQuestion_struct domainname qname; mDNSu16 qtype; mDNSu16 qclass; + mDNSBool LongLived; // Set by client for calls to mDNS_StartQuery to indicate LLQs to unicast layer. + mDNSBool ExpectUnique; // Set by client if it's expecting unique RR(s) for this question, not shared RRs + mDNSBool ForceMCast; // Set by client to force mDNS query, even for apparently uDNS names 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 @@ -1391,6 +1781,72 @@ struct ServiceInfoQuery_struct void *ServiceInfoQueryContext; }; +// *************************************************************************** +#if 0 +#pragma mark - NAT Traversal structures and constants +#endif + +#define NATMAP_INIT_RETRY (mDNSPlatformOneSecond / 4) // start at 250ms w/ exponential decay +#define NATMAP_MAX_RETRY mDNSPlatformOneSecond // back off to once per second +#define NATMAP_MAX_TRIES 3 // for max 3 tries +#define NATMAP_DEFAULT_LEASE (60 * 60) // lease life in seconds +#define NATMAP_VERS 0 +#define NATMAP_PORT 5351 +#define ADDR_REQUEST_PKTLEN 2 +#define ADDR_REPLY_PKTLEN 8 +#define PORTMAP_PKTLEN 12 +#define NATMAP_RESPONSE_MASK 0x80 + +typedef enum + { + NATOp_AddrRequest = 0, + NATOp_MapUDP = 1, + NATOp_MapTCP = 2 + } NATOp_t; + +enum + { + NATErr_None = 0, + NATErr_Vers = 1, + NATErr_Refused = 2, + NATErr_NetFail = 3, + NATErr_Res = 4, + NATErr_Opcode = 5 + }; + +typedef mDNSu16 NATErr_t; + +typedef enum + { + NATState_Init, + NATState_Request, + NATState_Established, + NATState_Legacy, + NATState_Error, + NATState_Refresh, + NATState_Deleted + } NATState_t; +// Note: we have no explicit "cancelled" state, where a service/interface is deregistered while we + // have an outstanding NAT request. This is conveyed by the "reg" pointer being set to NULL + +// Pass NULL for pkt on error (including timeout) +typedef void (*NATResponseHndlr)(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len); + +struct NATTraversalInfo_struct + { + NATOp_t op; + NATResponseHndlr ReceiveResponse; + union { AuthRecord *RecordRegistration; ServiceRecordSet *ServiceRegistration; } reg; + mDNSIPPort PublicPort; + mDNSu8 request[PORTMAP_PKTLEN]; // buffer for request messages + int requestlen; // length of buffer used + mDNSs32 retry; // absolute time when we retry + mDNSs32 RetryInterval; // delta between time sent and retry + int ntries; + NATState_t state; + NATTraversalInfo *next; + }; + // *************************************************************************** #if 0 #pragma mark - Main mDNS object, used to hold all the mDNS state @@ -1406,27 +1862,28 @@ enum }; 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; + { + 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; + NATTraversalInfo *NATTraversals; + mDNSu16 NextMessageID; + DNSServer *Servers; // list of DNS servers + mDNSAddr Router; + mDNSAddr PrimaryIP; // Address of primary interface + mDNSAddr MappedPrimaryIP; // Cache of public address if PrimaryIP is behind a NAT + NATTraversalInfo *LLQNatInfo; // Nat port mapping to receive LLQ events + domainname ServiceRegDomain; // (going away w/ multi-user support) + struct uDNS_AuthInfo *AuthInfoList; // list of domains requiring authentication for updates. + uDNS_HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata + DNSQuestion ReverseMap; // Reverse-map query to find static hostname for service target + mDNSBool ReverseMapActive; // Is above query active? + domainname StaticHostname; // Current answer to reverse-map query (above) + } uDNS_GlobalInfo; struct mDNS_struct { @@ -1437,9 +1894,11 @@ struct mDNS_struct mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size mDNSu32 KnownBugs; - mDNSBool CanReceiveUnicast; + mDNSBool CanReceiveUnicastOn5353; mDNSBool AdvertiseLocalAddresses; mStatus mDNSPlatformStatus; + mDNSIPPort UnicastPort4; + mDNSIPPort UnicastPort6; mDNSCallback *MainCallback; void *MainContext; @@ -1464,6 +1923,7 @@ struct mDNS_struct 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 + mDNSs32 PktNum; // Unique sequence number assigned to each received packet 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) @@ -1486,9 +1946,9 @@ struct mDNS_struct // 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; + domainname MulticastHostname; // Fully Qualified "dot-local" 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 mDNSInterface_LocalOnly @@ -1500,8 +1960,13 @@ struct mDNS_struct mDNSu32 NumFailedProbes; mDNSs32 SuppressProbes; - // unicast-specific data - uDNS_GlobalInfo uDNS_info; + // unicast-specific data + uDNS_GlobalInfo uDNS_info; + + // Fixed storage, to avoid creating large objects on the stack + DNSMessage imsg; // Incoming message received from wire + DNSMessage omsg; // Outgoing message we're building + LargeCacheRecord rec; // Resource Record extracted from received message }; // *************************************************************************** @@ -1510,20 +1975,20 @@ struct mDNS_struct #endif extern const mDNSIPPort zeroIPPort; -extern const mDNSv4Addr zeroIPAddr; +extern const mDNSv4Addr zerov4Addr; extern const mDNSv6Addr zerov6Addr; +extern const mDNSEthAddr zeroEthAddr; extern const mDNSv4Addr onesIPv4Addr; extern const mDNSv6Addr onesIPv6Addr; 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; extern const mDNSv4Addr AllDNSAdminGroup; -extern const mDNSv4Addr AllDNSLinkGroup; +extern const mDNSv4Addr AllDNSLinkGroupv4; extern const mDNSv6Addr AllDNSLinkGroupv6; extern const mDNSAddr AllDNSLinkGroup_v4; extern const mDNSAddr AllDNSLinkGroup_v6; @@ -1534,6 +1999,8 @@ extern const mDNSOpaque16 ResponseFlags; extern const mDNSOpaque16 UpdateReqFlags; extern const mDNSOpaque16 UpdateRespFlags; +#define localdomain (*(const domainname *)"\x5local") + // *************************************************************************** #if 0 #pragma mark - Inline functions @@ -1561,6 +2028,8 @@ extern mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v); #ifdef mDNSinline +mDNSinline mDNSs32 NonZeroTime(mDNSs32 t) { if (t) return(t); else return(1); } + 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])); } @@ -1619,6 +2088,8 @@ mDNSinline mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v) // 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). +// Following deregistration, the RecordCallback will be called with result mStatus_MemFree to signal that it is safe to deallocate +// the record's storage (memory must be freed asynchronously to allow for goodbye packets and dynamic update deregistration). // // 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 @@ -1650,14 +2121,14 @@ 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); + 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); +extern mDNSs32 mDNS_TimeNow(const mDNS *const m); // *************************************************************************** #if 0 @@ -1665,7 +2136,6 @@ extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr); #endif extern mDNSs32 mDNSPlatformOneSecond; -extern mDNSs32 mDNSPlatformTimeNow(void); // *************************************************************************** #if 0 @@ -1680,6 +2150,11 @@ extern mDNSs32 mDNSPlatformTimeNow(void); // 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_AddRecordToService adds an additional record to a Service Record Set. This record may be deregistered +// via mDNS_RemoveRecordFromService, or by deregistering the service. mDNS_RemoveRecordFromService is passed a +// callback to free the memory associated with the extra RR when it is safe to do so. The ExtraResourceRecord +// object can be found in the record's context pointer. + // 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. @@ -1698,7 +2173,7 @@ extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, 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_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context); extern mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname); extern mStatus mDNS_DeregisterService(mDNS *const m, ServiceRecordSet *sr); @@ -1710,7 +2185,7 @@ extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr, 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); + const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context); #define mDNS_StopBrowse mDNS_StopQuery extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context); @@ -1721,7 +2196,8 @@ typedef enum mDNS_DomainTypeBrowse = 0, mDNS_DomainTypeBrowseDefault = 1, mDNS_DomainTypeRegistration = 2, - mDNS_DomainTypeRegistrationDefault = 3 + mDNS_DomainTypeRegistrationDefault = 3, + mDNS_DomainTypeBrowseLegacy = 4, } mDNS_DomainType; extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, @@ -1805,17 +2281,51 @@ extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel // *************************************************************************** #if 0 -#pragma mark - Other utility functions +#pragma mark - Other utility functions and macros #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 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 char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer); +#define RRDisplayString(m, rr) GetRRDisplayString_rdb(rr, &(rr)->rdata->u, (m)->MsgBuffer) +#define ARDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) +#define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); +extern mDNSBool IsPrivateV4Addr(mDNSAddr *addr); // returns true for RFC1918 private addresses + +#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 mDNSSameEthAddress(A,B) ((A)->w[0] == (B)->w[0] && (A)->w[1] == (B)->w[1] && (A)->w[2] == (B)->w[2]) + +#define mDNSIPv4AddressIsZero(A) mDNSSameIPv4Address((A), zerov4Addr) +#define mDNSIPv6AddressIsZero(A) mDNSSameIPv6Address((A), zerov6Addr) + +#define mDNSIPv4AddressIsOnes(A) mDNSSameIPv4Address((A), onesIPv4Addr) +#define mDNSIPv6AddressIsOnes(A) mDNSSameIPv6Address((A), onesIPv6Addr) + +#define mDNSAddressIsAllDNSLinkGroup(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroupv4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) ) + +#define mDNSAddressIsZero(X) ( \ + ((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero((X)->ip.v4)) || \ + ((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsZero((X)->ip.v6)) ) + +#define mDNSAddressIsValidNonZero(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 0 @@ -1830,47 +2340,66 @@ extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); // padded keys for inned/outer hash rounds typedef struct { - mDNSu8 ipad[HMAC_LEN]; - mDNSu8 opad[HMAC_LEN]; - } HMAC_Key; + 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; + 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) +// Unicast DNS and Dynamic Update specific Client Calls +// +// mDNS_SetSecretForZone tells the core to authenticate (via TSIG with an HMAC_MD5 hash of the shared secret) // 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); - +// Calling this routine multiple times for a zone replaces previously entered values. Call with a NULL key +// to dissable authentication for the zone. + +extern mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64); + +// Hostname/Unicast Interface Configuration + +// All hostnames advertised point to a single IP address, set via SetPrimaryInterfaceInfo. Invoking this routine +// updates all existing hostnames to point to the new address. + +// A hostname is added via AddDynDNSHostName, which points to the primary interface's IP address. + +// The status callback is invoked to convey success or failure codes - the callback should not modify the AuthRecord or free memory. +// Added hostnames may be removed (deregistered) via mDNS_RemoveDynDNSHostName. + +// Host domains added prior to specification of the primary interface address and computer name will be deferred until +// these values are initialized. + +// When routable V4 interfaces are added or removed, mDNS_UpdateLLQs should be called to re-estabish LLQs in case the +// destination address for events (i.e. the route) has changed. For performance reasons, the caller is responsible for +// batching changes, e.g. calling the routine only once if multiple interfaces are simultanously removed or added. + +// DNS servers used to resolve unicast queries are specified by mDNS_AddDNSServer, and may later be removed via mDNS_DeleteDNSServers. +// For "split" DNS configurations, in which queries for different domains are sent to different servers (e.g. VPN and external), +// a domain may be associated with a DNS server. For standard configurations, specify the root label (".") or NULL. + +extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext); +extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn); +extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *addr, const mDNSAddr *router); +extern void mDNS_UpdateLLQs(mDNS *m); +extern void mDNS_AddDNSServer(mDNS *const m, const mDNSAddr *dnsAddr, const domainname *domain); +extern void mDNS_DeleteDNSServers(mDNS *const 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); +extern void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, const 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 @@ -1878,9 +2407,6 @@ extern void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, mDNSu8 *key, mDNSu32 // 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 @@ -1897,15 +2423,24 @@ extern mStatus DNSDigest_MD5(const DNSMessage *msg, mDNSu32 msglen, mDNSOpaque16 // 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 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 // 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). +// +// USE CAUTION WHEN CALLING mDNSPlatformRawTime: The m->timenow_adjust correction factor needs to be added +// Generally speaking: +// Code that's protected by the main mDNS lock should just use the m->timenow value +// Code outside the main mDNS lock should use mDNS_TimeNow(m) to get properly adjusted time +// +// mDNSPlatformUTC returns the time, in seconds, since Jan 1st 1970 UTC and is required for generating TSIG records + 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, +extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport); extern void mDNSPlatformLock (const mDNS *const m); @@ -1918,7 +2453,10 @@ extern mDNSBool mDNSPlatformMemSame (const void *src, const void *dst, mDNSu extern void mDNSPlatformMemZero ( void *dst, mDNSu32 len); extern void * mDNSPlatformMemAllocate (mDNSu32 len); extern void mDNSPlatformMemFree (void *mem); -extern mStatus mDNSPlatformTimeInit (mDNSs32 *timenow); +extern mDNSu32 mDNSPlatformRandomSeed (void); +extern mStatus mDNSPlatformTimeInit (void); +extern mDNSs32 mDNSPlatformRawTime (void); +extern mDNSs32 mDNSPlatformUTC (void); // 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 @@ -1935,9 +2473,9 @@ extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m, mD // 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 +// asynchronously 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 +// with limited asynchronous 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. @@ -1957,9 +2495,9 @@ extern int mDNSPlatformWriteTCP(int sd, const char *msg, int len); typedef struct DNameListElem { - domainname name; - struct DNameListElem *next; - } DNameListElem; + domainname name; + struct DNameListElem *next; + } DNameListElem; extern DNameListElem *mDNSPlatformGetSearchDomainList(void); extern DNameListElem *mDNSPlatformGetRegDomainList(void); @@ -1968,16 +2506,22 @@ extern DNameListElem *mDNSPlatformGetRegDomainList(void); extern DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig); extern void mDNS_FreeDNameList(DNameListElem *list); +#ifdef _LEGACY_NAT_TRAVERSAL_ +// Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core. + +#define DYN_PORT_MIN 49152 // ephemeral port range +#define DYN_PORT_MAX 65535 +#define LEGACY_NATMAP_MAX_TRIES 4 // if our desired mapping is taken, how many times we try mapping to a random port + +extern mStatus LNT_GetPublicIP(mDNSOpaque32 *ip); +extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp); +extern mStatus LNT_UnmapPort(mDNSIPPort PubPort, mDNSBool tcp); +#endif // _LEGACY_NAT_TRAVERSAL_ + // 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_SetFQDN() is called once on startup (typically from mDNSPlatformInit()) +// and then again on each subsequent change of the 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. @@ -1989,9 +2533,9 @@ extern void mDNS_FreeDNameList(DNameListElem *list); // -- 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_RegisterInterface does not result in the registration of global hostnames via dynamic update - +// see mDNS_SetPrimaryInterfaceInfo, mDNS_AddDynDNSHostName, etc. for this purpose. +// Note that the set may be deallocated immediately after it is deregistered via mDNS_DeegisterInterface. // // 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 @@ -2009,18 +2553,13 @@ extern void mDNS_FreeDNameList(DNameListElem *list); // (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 void mDNS_GenerateGlobalFQDN(mDNS *const m); +extern void mDNS_SetFQDN(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, +extern void mDNSCoreReceive(mDNS *const m, void *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); + const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake); extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip); diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c index 90fd363..b2f8f3f 100755 --- a/mDNSCore/uDNS.c +++ b/mDNSCore/uDNS.c @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,327 @@ Change History (most recent first): $Log: uDNS.c,v $ +Revision 1.151 2004/12/13 21:45:08 ksekar +uDNS_DeregisterService should return NoError if called twice (to follow mDNS behavior expected by daemon layer) + +Revision 1.150 2004/12/13 20:42:41 ksekar +Fixed LogMsg + +Revision 1.149 2004/12/13 18:10:03 ksekar +Fixed LogMsg + +Revision 1.148 2004/12/13 01:18:04 ksekar +Fixed unused variable warning for non-debug builds + +Revision 1.147 2004/12/12 23:51:42 ksekar + Wide-area registrations should fallback to using DHCP hostname as target + +Revision 1.146 2004/12/12 23:30:40 ksekar + Extra RRs not properly unlinked when parent service registration fails + +Revision 1.145 2004/12/12 22:56:29 ksekar + Need to properly handle duplicate long-lived queries + +Revision 1.144 2004/12/11 20:55:29 ksekar + Clean up registration state machines + +Revision 1.143 2004/12/10 01:21:27 cheshire + Get rid of "LLQ Responses over TCP not currently supported" message + +Revision 1.142 2004/12/08 02:03:31 ksekar + Looping on NAT Traversal error - check for +NULL RR on error + +Revision 1.141 2004/12/07 01:39:28 cheshire +Don't fail if the same server is responsible for more than one domain +(e.g. the same DNS server may be responsible for both apple.com. and 17.in-addr.arpa.) + +Revision 1.140 2004/12/06 21:15:22 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.139 2004/12/06 19:08:03 cheshire +Add clarifying comment -- CountLabels() excludes the final root label. + +Revision 1.138 2004/12/06 01:45:54 ksekar +Correct wording in LogMsg + +Revision 1.137 2004/12/03 20:40:35 ksekar + Looping on NAT Traversal error + +Revision 1.136 2004/12/03 07:20:50 ksekar + Wide-Area: Registration of large TXT record fails + +Revision 1.135 2004/12/03 05:18:33 ksekar + mDNSResponder needs to return more specific TSIG errors + +Revision 1.134 2004/12/02 20:03:49 ksekar + Rendezvous still publishes wide-area domains even after switching to a local subnet + +Revision 1.133 2004/12/02 18:37:52 ksekar + Registering with port number zero should not create a port mapping + +Revision 1.132 2004/12/01 20:57:19 ksekar + Wide Area Rendezvous must be split-DNS aware + +Revision 1.131 2004/12/01 19:59:27 cheshire + Crash in mDNSPlatformTCPConnect +If a TCP response has the TC bit set, don't respond by just trying another TCP connection + +Revision 1.130 2004/12/01 02:43:23 cheshire +Don't call StatusCallback if function pointer is null + +Revision 1.129 2004/11/30 23:51:06 cheshire +Remove double semicolons + +Revision 1.128 2004/11/25 01:48:30 ksekar + Logging into VPN does not trigger registration of address record + +Revision 1.127 2004/11/25 01:41:36 ksekar +Changed unnecessary LogMsgs to debugfs + +Revision 1.126 2004/11/23 23:54:17 ksekar + Wide-Area DNSServiceRegisterRecord() failures +can crash mDNSResponder + +Revision 1.125 2004/11/23 04:16:48 cheshire +Removed receiveMsg() routine. + +Revision 1.124 2004/11/23 04:06:51 cheshire +Get rid of floating point constant -- in a small embedded device, bringing in all +the floating point libraries just to halve an integer value is a bit too heavyweight. + +Revision 1.123 2004/11/22 17:16:20 ksekar + Unicast services don't disappear when you disable all networking + +Revision 1.122 2004/11/19 18:00:34 ksekar + Security: use random ID for one-shot unicast queries + +Revision 1.121 2004/11/19 04:24:08 ksekar + Security: Enforce a "window" on one-shot wide-area queries + +Revision 1.120 2004/11/19 02:32:43 ksekar + Wide-Area Rendezvous Security: Add LLQ-ID to events + +Revision 1.119 2004/11/18 23:21:24 ksekar + LLQ Security: Need to verify src port/address for LLQ handshake + +Revision 1.118 2004/11/18 22:58:37 ksekar +Removed old comment. + +Revision 1.117 2004/11/18 18:04:21 ksekar +Restore checkins lost due to repository disk failure: Update comments & + +Revision 1.xxx 2004/11/17 06:17:57 cheshire +Update comments to show correct SRV names: _dns-update._udp.. and _dns-llq._udp.. + +Revision 1.xxx 2004/11/17 00:45:28 ksekar + Result of putUpdateLease not error-checked + +Revision 1.116 2004/11/16 01:41:47 ksekar +Fixed typo in debugf + +Revision 1.115 2004/11/15 20:09:24 ksekar + Wide Area support for Add/Remove record + +Revision 1.114 2004/11/13 02:32:47 ksekar + LLQ mobility fragile on non-primary interface +- fixed incorrect state comparison in CheckQueries + +Revision 1.113 2004/11/13 02:29:52 ksekar + LLQ refreshes not reliable + +Revision 1.112 2004/11/11 20:45:14 ksekar + self-conflict test not compatible with some BIND servers + +Revision 1.111 2004/11/11 20:14:55 ksekar + Wide-Area registrations not deregistered on sleep + +Revision 1.110 2004/11/10 23:53:53 ksekar +Remove no longer relevant comment + +Revision 1.109 2004/11/10 20:40:53 ksekar + LLQ mobility fragile on non-primary interface + +Revision 1.108 2004/11/01 20:36:16 ksekar + mDNSResponder should not receive Keychain Notifications + +Revision 1.107 2004/10/26 06:11:41 cheshire +Add improved logging to aid in diagnosis of mDNSResponder crashed + +Revision 1.106 2004/10/26 03:52:03 cheshire +Update checkin comments + +Revision 1.105 2004/10/26 01:15:06 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.104 2004/10/25 21:41:38 ksekar + wide-area name conflicts can cause crash + +Revision 1.103 2004/10/25 19:30:52 ksekar + Simplify dynamic host name structures + +Revision 1.102 2004/10/23 01:16:00 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.101 2004/10/22 20:52:07 ksekar + Create NAT port mappings for Long Lived Queries + +Revision 1.100 2004/10/20 02:16:41 cheshire +Improve "could not confirm existence of NS record" error message +Don't call newRR->RecordCallback if it is NULL + +Revision 1.99 2004/10/19 21:33:18 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.98 2004/10/16 00:16:59 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.97 2004/10/15 23:00:18 ksekar + Need to update LLQs on location changes + +Revision 1.96 2004/10/12 23:30:44 ksekar + mDNSResponder needs to follow CNAME referrals + +Revision 1.95 2004/10/12 03:15:09 ksekar + mDNS_StartQuery shouldn't return transient no-server error + +Revision 1.94 2004/10/12 02:49:20 ksekar + Clean up LLQ sleep/wake, error handling + +Revision 1.93 2004/10/08 04:17:25 ksekar + Don't use DNS extensions if the server does not advertise required SRV record + +Revision 1.92 2004/10/08 03:54:35 ksekar + Refine unicast polling intervals + +Revision 1.91 2004/09/30 17:45:34 ksekar + lots of log messages: mDNS_SetPrimaryIP: IP address unchanged + +Revision 1.90 2004/09/25 00:22:13 ksekar + Crash in uDNS_RegisterService + +Revision 1.89 2004/09/24 19:14:53 cheshire +Remove unused "extern mDNS mDNSStorage" + +Revision 1.88 2004/09/23 20:48:15 ksekar +Clarify retransmission debugf messages. + +Revision 1.87 2004/09/22 00:41:59 cheshire +Move tcp connection status codes into the legal range allocated for mDNS use + +Revision 1.86 2004/09/21 23:40:11 ksekar + mDNSResponder to return errors on NAT traversal failure + +Revision 1.85 2004/09/21 22:38:27 ksekar + PrimaryIP type uninitialized + +Revision 1.84 2004/09/18 00:30:39 cheshire + Infinite loop in CheckServiceRegistrations + +Revision 1.83 2004/09/17 00:31:51 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.82 2004/09/16 21:36:36 cheshire + Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.81 2004/09/16 02:29:39 cheshire +Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around +uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService + +Revision 1.80 2004/09/16 01:58:21 cheshire +Fix compiler warnings + +Revision 1.79 2004/09/16 00:24:48 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.78 2004/09/15 01:16:57 ksekar + mDNSResponder printing too many messages + +Revision 1.77 2004/09/14 23:27:47 cheshire +Fix compile errors + +Revision 1.76 2004/09/14 22:22:00 ksekar + Legacy browses broken against some BIND versions + +Revision 1.75 2004/09/03 19:23:05 ksekar +: Need retransmission mechanism for wide-area service registrations + +Revision 1.74 2004/09/02 17:49:04 ksekar +: 8A246: mDNSResponder crash while logging on restart +Fixed incorrect conversions, changed %s to %##s for all domain names. + +Revision 1.73 2004/09/02 01:39:40 cheshire +For better readability, follow consistent convention that QR bit comes first, followed by OP bits + +Revision 1.72 2004/09/01 03:59:29 ksekar +: Conditionally compile out uDNS code on Windows + +Revision 1.71 2004/08/27 17:51:53 ksekar +Replaced unnecessary LogMsg with debugf. + +Revision 1.70 2004/08/25 00:37:27 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.69 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.68 2004/08/14 03:22:41 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.67 2004/08/13 23:46:58 cheshire +"asyncronous" -> "asynchronous" + +Revision 1.66 2004/08/13 23:37:02 cheshire +Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with +"uDNS_info.UnicastHostname" for clarity + +Revision 1.65 2004/08/13 23:12:32 cheshire +Don't use strcpy() and strlen() on "struct domainname" objects; +use AssignDomainName() and DomainNameLength() instead +(A "struct domainname" is a collection of packed pascal strings, not a C string.) + +Revision 1.64 2004/08/13 23:01:05 cheshire +Use platform-independent mDNSNULL instead of NULL + +Revision 1.63 2004/08/12 00:32:36 ksekar +: LLQ Refreshes never terminate if unanswered + +Revision 1.62 2004/08/10 23:19:14 ksekar +: DNS Extension daemon for Wide Area Rendezvous +Moved routines/constants to allow extern access for garbage collection daemon + +Revision 1.61 2004/07/30 17:40:06 ksekar +: TXT Record updates not available for wide-area services + +Revision 1.60 2004/07/29 19:40:05 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.59 2004/07/29 19:27:15 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.58 2004/07/27 07:35:38 shersche +fix syntax error, variables declared in the middle of a block + +Revision 1.57 2004/07/26 22:49:30 ksekar +: Feature #9516: Need support for NATPMP in client + +Revision 1.56 2004/07/26 19:14:44 ksekar +: 8A210: mDNSResponder crashed in startLLQHandshakeCallback + +Revision 1.55 2004/07/15 19:01:33 ksekar +: Check for incorrect time comparisons + 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 +: mDNSResponder crash while location cycling Revision 1.52 2004/06/17 01:13:11 ksekar : polling interval too short @@ -162,9 +476,9 @@ 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 +Added an asynchronous 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 +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 @@ -220,20 +534,12 @@ Revision 1.1 2003/12/13 03:05:27 ksekar #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 +// Asynchronous operation types typedef enum { @@ -269,28 +575,70 @@ typedef void AsyncOpCallback(mStatus err, mDNS *const m, void *info, const Async // 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 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); +mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs); +mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result); +mDNSlocal void SendRecordUpdate(mDNS *m, AuthRecord *rr, uDNS_RegInfo *info); +mDNSlocal mStatus RegisterService(mDNS *m, ServiceRecordSet *srs); +mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive); +mDNSlocal void RestartQueries(mDNS *m); +mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer); +mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context); + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Temporary workaround +#endif + +// 17 Places in this file directly call mDNSPlatformTimeNow(), which is unsafe +// The platform function is now called mDNSPlatformRawTime(), and +// mDNSPlatformTimeNow() is defined here as a temporary workaround. +// This is a gross hack, and after this change has been tested for a while, +// all these calls should be replaced by simple references to m->timenow + +mDNSlocal mDNSs32 mDNSPlatformTimeNow(mDNS *m) + { + if (m->mDNS_busy && m->timenow) return(m->timenow); + LogMsg("ERROR: uDNS.c code executing without holding main mDNS lock"); + + // To get a quick and easy stack trace to find out *how* this routine + // is being called without holding main mDNS lock, uncomment the line below: + // *(long*)0=0; + + return(mDNS_TimeNow(m)); + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - General Utility Functions #endif +// CountLabels() returns number of labels in name, excluding final root label +// (e.g. for "apple.com." CountLabels returns 2.) +mDNSlocal int CountLabels(const domainname *d) + { + int count = 0; + const mDNSu8 *ptr; + + for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++; + return count; + } + 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; + static mDNSBool randomized = mDNSfalse; + + if (!randomized) { u->NextMessageID = mDNSRandom(~0); randomized = mDNStrue; } return mDNSOpaque16fromIntVal(u->NextMessageID++); } // unlink an AuthRecord from a linked list mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr) { - AuthRecord *rptr, *prev = NULL; + AuthRecord *rptr, *prev = mDNSNULL; for (rptr = *list; rptr; rptr = rptr->next) { @@ -298,7 +646,7 @@ mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr) { if (prev) prev->next = rptr->next; else *list = rptr->next; - rptr->next = NULL; + rptr->next = mDNSNULL; return mStatus_NoError; } prev = rptr; @@ -307,106 +655,154 @@ mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr) return mStatus_UnknownErr; } +mDNSlocal void unlinkSRS(uDNS_GlobalInfo *u, ServiceRecordSet *srs) + { + ServiceRecordSet **p; + for (p = &u->ServiceRegistrations; *p; p = &(*p)->next) + if (*p == srs) { *p = srs->next; srs->next = mDNSNULL; return; } + LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list"); + } + 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; } + if (uDNS_IsActiveQuery(q, u)) + { LogMsg("LinkActiveQuestion - %##s (%d) already in list!", q->qname.c, q->qtype); return; } q->next = u->ActiveQueries; u->ActiveQueries = q; } +mDNSlocal void SwapRData(mDNS *m, AuthRecord *rr, mDNSBool DeallocOld) + { + RData *oldrd = rr->resrec.rdata; + mDNSu16 oldrdlen = rr->resrec.rdlength; + + if (!rr->uDNS_info.UpdateRData) { LogMsg("SwapRData invoked with NULL UpdateRData field"); return; } + SetNewRData(&rr->resrec, rr->uDNS_info.UpdateRData, rr->uDNS_info.UpdateRDLen); + if (DeallocOld) + { + rr->uDNS_info.UpdateRData = mDNSNULL; // Clear the NewRData pointer ... + if (rr->uDNS_info.UpdateRDCallback) rr->uDNS_info.UpdateRDCallback(m, rr, oldrd); // ... and let the client know + } + else + { + rr->uDNS_info.UpdateRData = oldrd; + rr->uDNS_info.UpdateRDLen = oldrdlen; + } + } + +// set retry timestamp for record with exponential backoff +// (for service record sets, use RR_SRV as representative for time checks +mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr) + { + rr->LastAPTime = mDNSPlatformTimeNow(m); + if (rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL) { rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL; return; } + if (rr->ThisAPInterval*2 <= MAX_UCAST_POLL_INTERVAL) { rr->ThisAPInterval *= 2; return; } + if (rr->ThisAPInterval != MAX_UCAST_POLL_INTERVAL) { rr->ThisAPInterval = MAX_UCAST_POLL_INTERVAL; } + } + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - Name Server List Management #endif -mDNSexport void mDNS_RegisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr) +mDNSexport void mDNS_AddDNSServer(mDNS *const m, const mDNSAddr *addr, const domainname *d) { - //!!!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; - } + DNSServer *s, **p = &u->Servers; + + mDNS_Lock(m); + if (!d) d = (domainname *)""; - 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"); } + while (*p) // Check if we already have this {server,domain} pair registered + { + if (mDNSSameAddress(&(*p)->addr, addr) && SameDomainName(&(*p)->domain, d)) + LogMsg("Note: DNS Server %#a for domain %##s registered more than once", addr, d->c); + p=&(*p)->next; + } + // allocate, add to list + s = umalloc(sizeof(*s)); + if (!s) { LogMsg("Error: mDNS_AddDNSServer - malloc"); goto end; } + s->addr = *addr; + AssignDomainName(s->domain, *d); + s->next = mDNSNULL; + *p = s; + + end: + mDNS_Unlock(m); } -mDNSexport void mDNS_DeregisterDNS(mDNS *const m, mDNSv4Addr *const dnsAddr) +mDNSexport void mDNS_DeleteDNSServers(mDNS *const m) { - uDNS_GlobalInfo *u = &m->uDNS_info; - int i; + DNSServer *s; + mDNS_Lock(m); - 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"); } - } + s = m->uDNS_info.Servers; + m->uDNS_info.Servers = mDNSNULL; + while (s) + { + DNSServer *tmp = s; + s = s->next; + ufree(tmp); + } -mDNSexport void mDNS_DeregisterDNSList(mDNS *const m) - { - ubzero(m->uDNS_info.Servers, 32 * sizeof(mDNSAddr)); + mDNS_Unlock(m); } -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 +mDNSlocal uDNS_AuthInfo *GetAuthInfoForZone(const uDNS_GlobalInfo *u, const domainname *zone) + { + uDNS_AuthInfo *ptr; + while (zone->c[0]) + { + for (ptr = u->AuthInfoList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->zone, zone)) return(ptr); + zone = (const domainname *)(zone->c + 1 + zone->c[0]); + } + return mDNSNULL; + } + +mDNSlocal void DeleteAuthInfoForZone(uDNS_GlobalInfo *u, const domainname *zone) + { + uDNS_AuthInfo *ptr, *prev = mDNSNULL; + + for (ptr = u->AuthInfoList; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->zone, zone)) + { + if (prev) prev->next = ptr->next; + else u->AuthInfoList = ptr->next; + ufree(ptr); + return; + } + prev = ptr; + } + } -mDNSexport mStatus mDNS_UpdateDomainRequiresAuthentication(mDNS *m, domainname *zone, domainname *key, - mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64) +mDNSexport mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const mDNSu8 *sharedSecret, mDNSu32 ssLen, mDNSBool base64) { uDNS_AuthInfo *info; mDNSu8 keybuf[1024]; mDNSs32 keylen; + uDNS_GlobalInfo *u = &m->uDNS_info; + mStatus status = mStatus_NoError; + + mDNS_Lock(m); + + if (GetAuthInfoForZone(u, zone)) DeleteAuthInfoForZone(u, zone); + if (!key) goto exit; info = (uDNS_AuthInfo*)umalloc(sizeof(uDNS_AuthInfo) + ssLen); - if (!info) { LogMsg("ERROR: umalloc"); return mStatus_NoMemoryErr; } + if (!info) { LogMsg("ERROR: umalloc"); status = mStatus_NoMemoryErr; goto exit; } ubzero(info, sizeof(uDNS_AuthInfo)); - ustrcpy(info->zone.c, zone->c); - ustrcpy(info->keyname.c, key->c); + AssignDomainName(info->zone, *zone); + AssignDomainName(info->keyname, *key); if (base64) { @@ -415,181 +811,836 @@ mDNSexport mStatus mDNS_UpdateDomainRequiresAuthentication(mDNS *m, domainname * { LogMsg("ERROR: mDNS_UpdateDomainRequiresAuthentication - could not convert shared secret from base64"); ufree(info); - return mStatus_UnknownErr; + status = mStatus_UnknownErr; + goto exit; } 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; +exit: + mDNS_Unlock(m); + return status; + } + + // *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - NAT Traversal +#endif + +mDNSlocal mDNSBool MapServicePort(mDNS *m) + { + uDNS_HostnameInfo *i; + + //!!!KRS this could be cached + for (i = m->uDNS_info.Hostnames; i; i= i->next) + if (i->ar->uDNS_info.NATinfo && i->ar->uDNS_info.NATinfo->state != NATState_Error) return mDNStrue; + + return mDNSfalse; + } + +mDNSlocal mDNSBool DomainContainsLabelString(const domainname *d, const char *str) + { + const domainlabel *l; + domainlabel buf; + + if (!MakeDomainLabelFromLiteralString(&buf, str)) return mDNSfalse; + + for (l = (const domainlabel *)d; l->c[0]; l = (const domainlabel *)(l->c + l->c[0]+1)) + if (SameDomainLabel(l->c, buf.c)) return mDNStrue; + return mDNSfalse; + } + +// allocate struct, link into global list, initialize +mDNSlocal NATTraversalInfo *AllocNATInfo(mDNS *const m, NATOp_t op, NATResponseHndlr callback) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + NATTraversalInfo *info = umalloc(sizeof(NATTraversalInfo)); + if (!info) { LogMsg("ERROR: malloc"); return mDNSNULL; } + ubzero(info, sizeof(NATTraversalInfo)); + info->next = u->NATTraversals; + u->NATTraversals = info; + info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY; + info->op = op; + info->state = NATState_Init; + info->ReceiveResponse = callback; + info->PublicPort.NotAnInteger = 0; + return info; } -mDNSexport void mDNS_ClearAuthenticationList(mDNS *m) +// unlink from list, deallocate +mDNSlocal mDNSBool FreeNATInfo(mDNS *m, NATTraversalInfo *n) { - uDNS_AuthInfo *fptr, *ptr = m->uDNS_info.AuthInfoList; + NATTraversalInfo *ptr, *prev = mDNSNULL; + if (n == m->uDNS_info.LLQNatInfo) m->uDNS_info.LLQNatInfo = mDNSNULL; + ptr = m->uDNS_info.NATTraversals; while (ptr) { - fptr = ptr; + if (ptr == n) + { + if (prev) prev->next = ptr->next; + else m->uDNS_info.NATTraversals = ptr->next; + ufree(n); + return mDNStrue; + } + prev = ptr; ptr = ptr->next; - ufree(fptr); } - m->uDNS_info.AuthInfoList = NULL; + LogMsg("FreeNATInfo: NATTraversalInfo not found in list"); + return mDNSfalse; } -mDNSlocal uDNS_AuthInfo *GetAuthInfoForZone(const uDNS_GlobalInfo *u, const domainname *zone) +mDNSlocal void SendNATMsg(NATTraversalInfo *info, mDNS *m) { - uDNS_AuthInfo *ptr; - domainname *z; - mDNSu32 zoneLen, ptrZoneLen; + mStatus err; + mDNSAddr dst; + mDNSIPPort dstport; + uDNS_GlobalInfo *u = &m->uDNS_info; - zoneLen = ustrlen(zone->c); - for (ptr = u->AuthInfoList; ptr; ptr = ptr->next) + if (info->state != NATState_Request && info->state != NATState_Refresh) + { LogMsg("SendNATMsg: Bad state %d", info->state); return; } + + if (u->Router.ip.v4.NotAnInteger) { - 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; - } + // send msg if we have a router + dst.type = u->Router.type; + dst.ip.v4 = u->Router.ip.v4; + dstport = mDNSOpaque16fromIntVal(NATMAP_PORT); + err = mDNSPlatformSendUDP(m, info->request, info->request+info->requestlen, 0, &dst, dstport); + if (!err) (info->ntries++); // don't increment attempt counter if the send failed + } + // set retry + if (info->RetryInterval < NATMAP_INIT_RETRY) info->RetryInterval = NATMAP_INIT_RETRY; + else if (info->RetryInterval * 2 > NATMAP_MAX_RETRY) info->RetryInterval = NATMAP_MAX_RETRY; + else info->RetryInterval *= 2; + info->retry = mDNSPlatformTimeNow(m) + info->RetryInterval; + } - - - // *************************************************************************** -#if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - host name and interface management -#endif - - -mDNSlocal void hostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +mDNSlocal void ReceiveNATAddrResponse(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len) { - // note that the rr is already unlinked if result is non-zero + NATErr_t NatErr = NATErr_None; + mStatus err = mStatus_NoError; + AuthRecord *rr = mDNSNULL; + mDNSAddr addr; - if (result == mStatus_MemFree) return; - if (result == mStatus_NameConflict && rr->resrec.RecordType == kDNSRecordTypeUnique) + if (n->state != NATState_Request) { - // 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; + LogMsg("ReceiveNATAddrResponse: bad state %d", n->state); + err = mStatus_UnknownErr; + goto end; } - - if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) - // we've already tried to re-register. reset RecordType before returning RR to client + + rr = n->reg.RecordRegistration; + if (!rr) { - if (result == mStatus_NoSuchRecord) // name is advertised for some other address - result = mStatus_NameConflict; - rr->resrec.RecordType = kDNSRecordTypeUnique; + LogMsg("ReceiveNATAddrResponse: registration cancelled"); + err = mStatus_UnknownErr; + goto end; } - - if (!result) rr->resrec.RecordType = kDNSRecordTypeVerified; - if (result) - ((NetworkInterfaceInfo *)(rr->RecordContext))->uDNS_info.registered = mDNSfalse; - mDNS_HostNameCallback(m, rr, result); - } + addr.type = mDNSAddrType_IPv4; + addr.ip.v4 = rr->resrec.rdata->u.ipv4; -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; + if (!pkt) // timeout + { +#ifdef _LEGACY_NAT_TRAVERSAL_ + err = LNT_GetPublicIP(&addr.ip.v4); + if (err) goto end; + else n->state = NATState_Legacy; +#else + debugf("ReceiveNATAddrResponse: timeout"); + err = mStatus_NATTraversal; + goto end; +#endif // _LEGACY_NAT_TRAVERSAL_ + } + else + { + if (len < ADDR_REPLY_PKTLEN) + { + LogMsg("ReceiveNATAddrResponse: response too short (%d bytes)", len); + err = mStatus_NATTraversal; + goto end; + } + if (pkt[0] != NATMAP_VERS) + { + LogMsg("ReceiveNATAddrResponse: received version %d (expect version %d)", pkt[0], NATMAP_VERS); + err = mStatus_NATTraversal; + goto end; + } + if (pkt[1] != (NATOp_AddrRequest | NATMAP_RESPONSE_MASK)) + { + LogMsg("ReceiveNATAddrResponse: bad response code %d", pkt[1]); + err = mStatus_NATTraversal; + goto end; + } + NatErr = (NATErr_t)((NATErr_t)pkt[2] << 8 | (NATErr_t)pkt[3]); + if (NatErr) { LogMsg("ReceiveAddrResponse: received error %d", err); err = mStatus_NATTraversal; goto end; } - // unlink the original - unlinkAR(&m->uDNS_info.RecordRegistrations, rr); - rr->uDNS_info.state = regState_Unregistered; - set->uDNS_info.registered = mDNSfalse; - uDNS_DeregisterRecord(m, copy); + addr.ip.v4.b[0] = pkt[4]; + addr.ip.v4.b[1] = pkt[5]; + addr.ip.v4.b[2] = pkt[6]; + addr.ip.v4.b[3] = pkt[7]; + n->state = NATState_Established; + } + + if (IsPrivateV4Addr(&addr)) + { + LogMsg("ReceiveNATAddrResponse: Double NAT"); + err = mStatus_DblNAT; + goto end; + } + + end: + if (err) + { + FreeNATInfo(m, n); + if (rr) + { + rr->uDNS_info.NATinfo = mDNSNULL; + rr->uDNS_info.state = regState_Unregistered; // note that rr is not yet in global list + rr->RecordCallback(m, rr, mStatus_NATTraversal); + // note - unsafe to touch rr after callback + } + return; + } + else LogMsg("Received public IP address %d.%d.%d.%d from NAT.", addr.ip.v4.b[0], addr.ip.v4.b[1], addr.ip.v4.b[2], addr.ip.v4.b[3]); + rr->resrec.rdata->u.ipv4 = addr.ip.v4; // replace rdata w/ public address + uDNS_RegisterRecord(m, rr); + } + + +mDNSlocal void StartGetPublicAddr(mDNS *m, uDNS_HostnameInfo *hInfo) + { + mDNSu8 *msg; + uDNS_GlobalInfo *u = &m->uDNS_info; + + NATTraversalInfo *info = AllocNATInfo(m, NATOp_AddrRequest, ReceiveNATAddrResponse); + if (!info) { uDNS_RegisterRecord(m, hInfo->ar); return; } + hInfo->ar->uDNS_info.NATinfo = info; + info->reg.RecordRegistration = hInfo->ar; + info->state = NATState_Request; + + // format message + msg = info->request; + msg[0] = NATMAP_VERS; + msg[1] = NATOp_AddrRequest; + info->requestlen = ADDR_REQUEST_PKTLEN; + + if (!u->Router.ip.v4.NotAnInteger) + { + debugf("No router. Will retry NAT traversal in %ld ticks", NATMAP_INIT_RETRY); + return; } - else debugf("uDNS_DeadvertiseInterface - interface not registered"); - return; + + SendNATMsg(info, m); + } + + +mDNSlocal void RefreshNATMapping(NATTraversalInfo *n, mDNS *m) + { + n->state = NATState_Refresh; + n->RetryInterval = NATMAP_INIT_RETRY; + n->ntries = 0; + SendNATMsg(n, m); } -mDNSexport void uDNS_AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) +mDNSlocal void LLQNatMapComplete(mDNS *m) { - 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; + uDNS_GlobalInfo *u = &m->uDNS_info; + LLQ_Info *llqInfo; + NATTraversalInfo *n = u->LLQNatInfo; - if (set->uDNS_info.registered && SameDomainName(&m->uDNS_info.hostname, &set->uDNS_info.regname)) - return; // already registered + if (!n) { LogMsg("Error: LLQNatMapComplete called with NULL LLQNatInfo"); return; } + if (n->state != NATState_Established && n->state != NATState_Legacy && n->state != NATState_Error) + { LogMsg("LLQNatMapComplete - bad nat state %d", n->state); return; } + + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) + { + DNSQuestion *q = u->CurrentQuery; + u->CurrentQuery = u->CurrentQuery->next; + llqInfo = q->uDNS_info.llq; + if (q->LongLived && llqInfo->state == LLQ_NatMapWait) + { + if (n->state == NATState_Error) + { + llqInfo->NATMap = mDNSfalse; + llqInfo->question->uDNS_info.responseCallback = llqResponseHndlr; + llqInfo->state = LLQ_Poll; + llqInfo->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + llqInfo->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + } + else { llqInfo->state = LLQ_GetZoneInfo; startLLQHandshake(m, llqInfo, mDNSfalse); } + } + } + } - if (!m->uDNS_info.hostname.c[0]) +mDNSlocal void ReceivePortMapReply(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len) + { + NATErr_t err; + ServiceRecordSet *srs; + mDNSIPPort priv, pktpriv, pub; + mDNSu32 lease; + mDNSBool deletion; + + if (n->state != NATState_Request && n->state != NATState_Refresh) + { LogMsg("ReceivePortMapReply: bad state %d", n->state); return; } + + deletion = !(n->request[8] | n->request[9] | n->request[10] | n->request[11]); // zero lease + if (deletion) { n->state = NATState_Deleted; return; } + // note that we explicitly ignore timeouts here + // we keep the context struct around so that the SendServiceDeregistration code can reference + // it to determine the public port to delete in the SRV record. + + srs = n->reg.ServiceRegistration; + if (!srs && n != m->uDNS_info.LLQNatInfo) { - // no hostname available - set->uDNS_info.registered = mDNSfalse; + debugf("ReceivePortMapReply: registration cancelled"); + FreeNATInfo(m, n); 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); + + priv = srs ? srs->RR_SRV.resrec.rdata->u.srv.port : m->UnicastPort4; + + if (!pkt) // timeout + { +#ifdef _LEGACY_NAT_TRAVERSAL_ + int ntries = 0; + mStatus err; + mDNSBool tcp = (srs && DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_tcp")); + + pub = priv; // initially request priv == pub + while (1) + { + err = LNT_MapPort(priv, pub, tcp); + if (!err) + { + n->PublicPort = pub; + n->state = NATState_Legacy; + goto end; + } + else if (err != mStatus_AlreadyRegistered || ++ntries > LEGACY_NATMAP_MAX_TRIES) + { + n->state = NATState_Error; + goto end; + } + else + { + // the mapping we want is taken - try a random port + mDNSu16 RandPort = mDNSRandom(DYN_PORT_MAX - DYN_PORT_MIN) + DYN_PORT_MIN; + pub = mDNSOpaque16fromIntVal(RandPort); + } + } +#else + goto end; +#endif // _LEGACY_NAT_TRAVERSAL_ + } + + if (len < PORTMAP_PKTLEN) { LogMsg("ReceivePortMapReply: response too short (%d bytes)", len); goto end; } + if (pkt[0] != NATMAP_VERS) { LogMsg("ReceivePortMapReply: received version %d (expect version %d)", pkt[0], NATMAP_VERS); goto end; } + if (pkt[1] != (n->op | NATMAP_RESPONSE_MASK)) { LogMsg("ReceivePortMapReply: bad response code %d", pkt[1]); goto end; } + err = (NATErr_t)((NATErr_t)pkt[2] << 8 | (NATErr_t)pkt[3]); + if (err) { LogMsg("ReceivePortMapReply: received error %d", err); goto end; } + + pktpriv.b[0] = pkt[4]; + pktpriv.b[1] = pkt[5]; + pub.b[0] = pkt[6]; + pub.b[1] = pkt[7]; + + lease = (mDNSu32) ((mDNSu32)pkt[8] << 24 | (mDNSu32)pkt[9] << 16 | (mDNSu32)pkt[10] << 8 | pkt[11]); + if (lease > 0x70000000UL / mDNSPlatformOneSecond) + lease = 0x70000000UL / mDNSPlatformOneSecond; + + if (priv.NotAnInteger != pktpriv.NotAnInteger) + { LogMsg("ReceivePortMapReply: reply private port does not match requested private port"); goto end; } + + if (n->state == NATState_Refresh && pub.NotAnInteger != n->PublicPort.NotAnInteger) + LogMsg("ReceivePortMapReply: NAT refresh changed public port from %d to %d", mDNSVal16(n->PublicPort), mDNSVal16(pub)); + // !!!KRS we need to update the SRV here! + n->PublicPort = pub; + + n->retry = mDNSPlatformTimeNow(m) + ((mDNSs32)lease * mDNSPlatformOneSecond/2); // retry half way to expiration + + if (n->state == NATState_Refresh) { n->state = NATState_Established; return; } + n->state = NATState_Established; + + end: + if (n->state != NATState_Established && n->state != NATState_Legacy) + { + LogMsg("NAT Port Mapping: timeout"); + n->state = NATState_Error; + if (!srs) { LLQNatMapComplete(m); return; } + FreeNATInfo(m, n); + srs->uDNS_info.NATinfo = mDNSNULL; + unlinkSRS(&m->uDNS_info, srs); + srs->uDNS_info.state = regState_Unregistered; + srs->ServiceCallback(m, srs, mStatus_NATTraversal); + return; // note - unsafe to touch srs here + } + + LogMsg("Mapped private port %d to public port %d", mDNSVal16(priv), mDNSVal16(n->PublicPort)); + if (!srs) { LLQNatMapComplete(m); return; } + srs->uDNS_info.state = regState_FetchingZoneData; + startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); } +mDNSlocal void FormatPortMaprequest(NATTraversalInfo *info, mDNSIPPort port) + { + mDNSu8 *msg = info->request; + mDNSOpaque32 lease = mDNSOpaque32fromIntVal(NATMAP_DEFAULT_LEASE); + + msg[0] = NATMAP_VERS; + msg[1] = info->op; + msg[2] = 0; // reserved + msg[3] = 0; // reserved + msg[4] = port.b[0]; // private port + msg[5] = port.b[1]; + msg[6] = port.b[0]; // requested pub. port + msg[7] = port.b[1]; + + msg[8] = lease.b[0]; + msg[9] = lease.b[1]; + msg[10] = lease.b[2]; + msg[11] = lease.b[3]; + } -// *************************************************************************** +mDNSlocal void SendInitialPMapReq(mDNS *m, NATTraversalInfo *info) + { + if (!m->uDNS_info.Router.ip.v4.NotAnInteger) + { + debugf("No router. Will retry NAT traversal in %ld seconds", NATMAP_INIT_RETRY); + info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY; + info->RetryInterval = NATMAP_INIT_RETRY; + return; + } + SendNATMsg(info, m); + return; + } + +mDNSlocal void StartNATPortMap(mDNS *m, ServiceRecordSet *srs) + { + NATOp_t op; + NATTraversalInfo *info; + + if (DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_tcp")) op = NATOp_MapTCP; + else if (DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_udp")) op = NATOp_MapUDP; + else { LogMsg("StartNATPortMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name.c); goto error; } + + info = AllocNATInfo(m, op, ReceivePortMapReply); + srs->uDNS_info.NATinfo = info; + info->reg.ServiceRegistration = srs; + info->state = NATState_Request; + info->requestlen = PORTMAP_PKTLEN; + + FormatPortMaprequest(info, srs->RR_SRV.resrec.rdata->u.srv.port); + SendInitialPMapReq(m, info); + return; + + error: + startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + } + +mDNSlocal void DeleteNATPortMapping(mDNS *m, NATTraversalInfo *nat, ServiceRecordSet *srs) + { + if (nat->state == NATState_Established) // let other edge-case states expire for simplicity + { + // zero lease + nat->request[8] = 0; + nat->request[9] = 0; + nat->request[10] = 0 ; + nat->request[11] = 0; + nat->state = NATState_Request; + SendNATMsg(nat, m); + } +#ifdef _LEGACY_NAT_TRAVERSAL_ + else if (nat->state == NATState_Legacy) + { + mStatus err = mStatus_NoError; + mDNSBool tcp = srs ? DomainContainsLabelString(&srs->RR_PTR.resrec.name, "_tcp") : mDNSfalse; + err = LNT_UnmapPort(nat->PublicPort, tcp); + if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %ld", err); + } +#else + (void)srs; // unused +#endif // _LEGACY_NAT_TRAVERSAL_ + } + +mDNSlocal void StartLLQNatMap(mDNS *m) + { + NATTraversalInfo *info = AllocNATInfo(m, NATOp_MapUDP, ReceivePortMapReply); + uDNS_GlobalInfo *u = &m->uDNS_info; + + u->LLQNatInfo = info; + + info->reg.RecordRegistration = mDNSNULL; + info->reg.ServiceRegistration = mDNSNULL; + info->state = NATState_Request; + info->requestlen = PORTMAP_PKTLEN; + FormatPortMaprequest(info, m->UnicastPort4); + SendInitialPMapReq(m, info); + return; + } + +// if LLQ NAT context unreferenced, delete the mapping +mDNSlocal void CheckForUnreferencedLLQMapping(mDNS *m) + { + NATTraversalInfo *nat = m->uDNS_info.LLQNatInfo; + DNSQuestion *q; + + if (!nat) return; + + for (q = m->uDNS_info.ActiveQueries; q; q = q->next) + if (q->LongLived && q->uDNS_info.llq->NATMap) return; + + //to avoid race condition if we need to recreate before this finishes, we do one-shot deregistration + if (nat->state == NATState_Established || nat->state == NATState_Legacy) + DeleteNATPortMapping(m, nat, mDNSNULL); // for simplicity we allow other states to expire + FreeNATInfo(m, nat); // note: this clears the global LLQNatInfo pointer + } + + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK -#pragma mark - Incoming Message Processing +#pragma mark - host name and interface management #endif -mDNSlocal mDNSBool sameResourceRecord(ResourceRecord *r1, ResourceRecord *r2) +// if we ever want to refine support for multiple hostnames, we can add logic matching service names to a particular hostname +// for now, we grab the first registered DynDNS name, if any, or a static name we learned via a reverse-map query +mDNSlocal mDNSBool GetServiceTarget(uDNS_GlobalInfo *u, AuthRecord *srv, domainname *dst) + { + uDNS_HostnameInfo *hi = u->Hostnames; + (void)srv; // unused + + dst->c[0] = 0; + while (hi) + { + if (hi->ar->uDNS_info.state == regState_Registered || hi->ar->uDNS_info.state == regState_Refresh) + { AssignDomainName(*dst, hi->ar->resrec.name); return mDNStrue; } + hi = hi->next; + } + + if (u->StaticHostname.c[0]) { AssignDomainName(*dst, u->StaticHostname); return mDNStrue; } + return mDNSfalse; + } + +mDNSlocal void UpdateSRV(mDNS *m, ServiceRecordSet *srs) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + ExtraResourceRecord *e; + domainname newtarget; + domainname *curtarget = &srs->RR_SRV.resrec.rdata->u.srv.target; + mDNSBool havetarget = GetServiceTarget(u, &srs->RR_SRV, &newtarget); + + if ((!havetarget && srs->uDNS_info.state == regState_NoTarget) || (havetarget && SameDomainName(curtarget, &newtarget))) return; // target unchanged + + switch(srs->uDNS_info.state) + { + case regState_FetchingZoneData: + case regState_Cancelled: + case regState_DeregPending: + case regState_DeregDeferred: + case regState_Unregistered: + case regState_NATMap: + case regState_ExtraQueued: + // In these states, the SRV has either not yet been registered (it will get up-to-date information when it is) + // or is in the process of, or has already been, deregistered + return; + + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // let the in-flight operation complete before updating + srs->uDNS_info.SRVUpdateDeferred = mDNStrue; + return; + + case regState_NoTarget: + // Service has not been registered due to lack of a target hostname + if (havetarget) SendServiceRegistration(m, srs); + return; + + case regState_Registered: + // target lost or changed. deregister service. upon completion, we'll look for a new target + // extra will be re-registed if the service is re-registered + for (e = srs->Extras; e; e = e->next) e->r.uDNS_info.state = regState_ExtraQueued; + + srs->uDNS_info.LostTarget = mDNStrue; + SendServiceDeregistration(m, srs); + return; + } + } + +mDNSlocal void UpdateSRVRecords(mDNS *m) + { + ServiceRecordSet *srs; + + for (srs = m->uDNS_info.ServiceRegistrations; srs; srs = srs->next) UpdateSRV(m, srs); + } + +mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + uDNS_HostnameInfo *hi = (uDNS_HostnameInfo *)rr->RecordContext; + mDNSu8 *ip = rr->resrec.rdata->u.ipv4.b; + + if (result == mStatus_MemFree) + { + debugf("MemFree: %##s IP %d.%d.%d.%d", rr->resrec.name.c, ip[0], ip[1], ip[2], ip[3]); + if (hi) ufree(hi); + ufree(rr); + 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 + debugf("Name in use - retrying as type KnownUnique"); + 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; + } + + if (result) + { + // don't unlink or free - we can retry when we get a new address/router + LogMsg("HostnameCallback: Error %ld for registration of %##s IP %d.%d.%d.%d", result, rr->resrec.name.c, ip[0], ip[1], ip[2], ip[3]); + if (!hi) { ufree(rr); return; } + if (hi->ar->uDNS_info.state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!"); + (const void *)rr->RecordContext = hi->StatusContext; + if (hi->StatusCallback) + hi->StatusCallback(m, rr, result); // client may NOT make API calls here + rr->RecordContext = (void *)hi; + return; + } + + // register any pending services that require a target + UpdateSRVRecords(m); + + // Deliver success to client + if (!hi) { LogMsg("HostnameCallback invoked with orphaned address record"); return; } + LogMsg("Registered hostname %##s IP %d.%d.%d.%d", rr->resrec.name.c, ip[0], ip[1], ip[2], ip[3]); + (const void *)rr->RecordContext = hi->StatusContext; + if (hi->StatusCallback) + hi->StatusCallback(m, rr, result); // client may NOT make API calls here + rr->RecordContext = (void *)hi; + } + +// register record or begin NAT traversal +mDNSlocal void AdvertiseHostname(mDNS *m, uDNS_HostnameInfo *h) + { + if (IsPrivateV4Addr(&m->uDNS_info.PrimaryIP)) + StartGetPublicAddr(m, h); + else + { + mDNSu8 *ip = m->uDNS_info.PrimaryIP.ip.v4.b; + LogMsg("Advertising %##s IP %d.%d.%d.%d", h->ar->resrec.name.c, ip[0], ip[1], ip[2], ip[3]); + uDNS_RegisterRecord(m, h->ar); + } + } + +mDNSlocal void FoundStaticHostname(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) + { + const domainname *pktname = &answer->rdata->u.name; + domainname *storedname = &m->uDNS_info.StaticHostname; + (void)question; + + debugf("FoundStaticHostname: %##s -> %##s (%s)", question->qname.c, answer->rdata->u.name.c, AddRecord ? "added" : "removed"); + if (AddRecord && !SameDomainName(pktname, storedname)) + { + AssignDomainName(*storedname, *pktname); + UpdateSRVRecords(m); + } + else if (!AddRecord && SameDomainName(pktname, storedname)) + { + storedname->c[0] = 0; + UpdateSRVRecords(m); + } + } + +mDNSlocal void GetStaticHostname(mDNS *m) + { + char buf[MAX_ESCAPED_DOMAIN_NAME]; + DNSQuestion *q = &m->uDNS_info.ReverseMap; + mDNSu8 *ip = m->uDNS_info.PrimaryIP.ip.v4.b; + mStatus err; + + ubzero(q, sizeof(*q)); + + if (m->uDNS_info.ReverseMapActive) + { + uDNS_StopQuery(m, q); + m->uDNS_info.ReverseMapActive = mDNSfalse; + } + + if (!m->uDNS_info.PrimaryIP.ip.v4.NotAnInteger) return; + mDNS_snprintf(buf, MAX_ESCAPED_DOMAIN_NAME, "%d.%d.%d.%d.in-addr.arpa.", ip[3], ip[2], ip[1], ip[0]); + if (!MakeDomainNameFromDNSNameString(&q->qname, buf)) { LogMsg("Error: GetStaticHostname - bad name %s", buf); return; } + + q->InterfaceID = mDNSInterface_Any; + q->Target = zeroAddr; + q->qtype = kDNSType_PTR; + q->qclass = kDNSClass_IN; + q->LongLived = mDNSfalse; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = mDNSfalse; + q->QuestionCallback = FoundStaticHostname; + q->QuestionContext = mDNSNULL; + + err = uDNS_StartQuery(m, q); + if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err); + else m->uDNS_info.ReverseMapActive = mDNStrue; + } + +// Deregister hostnames and register new names for each host domain with the current global +// values for the hostlabel and primary IP address +mDNSlocal void UpdateHostnameRegistrations(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + AuthRecord *new; + uDNS_HostnameInfo *i; + + for (i = u->Hostnames; i; i = i->next) + { + // only allocate new record if existing record is actually registered (i.e. it wasn't pending the hostname or IP being set) + if (i->ar->uDNS_info.state == regState_Unregistered) new = i->ar; + else + { + new = umalloc(sizeof(AuthRecord)); + if (!new) { LogMsg("ERROR: UpdateHostnameRegistration - malloc"); return; } + mDNS_SetupResourceRecord(new, mDNSNULL, 0, kDNSType_A, 1, kDNSRecordTypeUnique, HostnameCallback, i); + } + + // setup new record + AssignDomainName(new->resrec.name, i->ar->resrec.name); + new->resrec.rdata->u.ipv4 = u->PrimaryIP.ip.v4; + + if (i->ar->uDNS_info.state != regState_Unregistered) + { + // delete old record + i->ar->RecordContext = mDNSNULL; // clear backpointer to HostnameInfo + uDNS_DeregisterRecord(m, i->ar); + i->ar = new; + } + + // advertise new + AdvertiseHostname(m, i); + } + } + +mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext) { - return (r1->namehash == r2->namehash && - r1->rrtype == r2->rrtype && - SameDomainName(&r1->name, &r2->name) && - SameRData(r1, r2)); + uDNS_GlobalInfo *u = &m->uDNS_info; + uDNS_HostnameInfo *ptr, *new; + + mDNS_Lock(m); + + // check if domain already registered + for (ptr = u->Hostnames; ptr; ptr = ptr->next) + { + if (SameDomainName(fqdn, &ptr->ar->resrec.name)) + { LogMsg("Host Domain %##s already in list", fqdn->c); goto exit; } + } + + // allocate and format new address record + new = umalloc(sizeof(*new)); + if (new) new->ar = umalloc(sizeof(AuthRecord)); + if (!new || !new->ar) { LogMsg("ERROR: mDNS_AddDynDNSHostname - malloc"); goto exit; } + new->StatusCallback = StatusCallback; + new->StatusContext = StatusContext; + mDNS_SetupResourceRecord(new->ar, mDNSNULL, 0, kDNSType_A, 1, kDNSRecordTypeUnique, HostnameCallback, new); + AppendDomainName(&new->ar->resrec.name, fqdn); + new->next = u->Hostnames; + u->Hostnames = new; + if (u->PrimaryIP.ip.v4.NotAnInteger) + { + // only set RData if we have a valid IP + if (u->MappedPrimaryIP.ip.v4.NotAnInteger) new->ar->resrec.rdata->u.ipv4 = u->MappedPrimaryIP.ip.v4; //!!!KRS implement code that caches this + else new->ar->resrec.rdata->u.ipv4 = u->PrimaryIP.ip.v4; + AdvertiseHostname(m, new); + } + else new->ar->uDNS_info.state = regState_Unregistered; +exit: + mDNS_Unlock(m); } +mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + uDNS_HostnameInfo **ptr = &u->Hostnames; + + mDNS_Lock(m); + + while (*ptr && !SameDomainName(fqdn, &(*ptr)->ar->resrec.name)) ptr = &(*ptr)->next; + if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c); + else + { + uDNS_HostnameInfo *hi = *ptr; + *ptr = (*ptr)->next; // unlink + hi->ar->RecordContext = mDNSNULL; // about to free wrapper struct + if (hi->ar->uDNS_info.state != regState_Unregistered) uDNS_DeregisterRecord(m, hi->ar); + else { ufree(hi->ar); hi->ar = mDNSNULL; } + ufree(hi); + } + UpdateSRVRecords(m); + mDNS_Unlock(m); + } + +mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *addr, const mDNSAddr *router) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSBool AddrChanged, RouterChanged; + + if (addr && addr->type !=mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-V4 address. Discarding."); return; } + if (router && router->type !=mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-V4 address. Discarding."); return; } + mDNS_Lock(m); + + AddrChanged = addr ? (addr->ip.v4.NotAnInteger != u->PrimaryIP.ip.v4.NotAnInteger) : u->PrimaryIP.ip.v4.NotAnInteger; + RouterChanged = router ? (router->ip.v4.NotAnInteger != u->Router.ip.v4.NotAnInteger) : u->Router.ip.v4.NotAnInteger; + +#if MDNS_DEBUGMSGS + if (addr && (AddrChanged || RouterChanged)) + LogMsg("mDNS_SetPrimaryInterfaceInfo: address changed from %d.%d.%d.%d to %d.%d.%d.%d:%d", + u->PrimaryIP.ip.v4.b[0], u->PrimaryIP.ip.v4.b[1], u->PrimaryIP.ip.v4.b[2], u->PrimaryIP.ip.v4.b[3], + addr->ip.v4.b[0], addr->ip.v4.b[1], addr->ip.v4.b[2], addr->ip.v4.b[3], mDNSVal16(m->UnicastPort4)); +#endif // MDNS_DEBUGMSGS + + if (addr) u->PrimaryIP = *addr; + if (router) u->Router = *router; + else u->Router.ip.v4.NotAnInteger = 0; // setting router to zero indicates that nat mappings must be reestablished when router is reset + + if (AddrChanged) + { + if (addr) + { + UpdateHostnameRegistrations(m); + UpdateSRVRecords(m); + } + GetStaticHostname(m); // look up reverse map record to find any static hostnames for our IP address + } + + mDNS_Unlock(m); + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Incoming Message Processing +#endif + 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; + if (SameResourceRecord(&ptr->resrec, &rr->resrec)) return mDNStrue; return mDNSfalse; } @@ -597,11 +1648,11 @@ mDNSlocal mDNSBool kaListContainsAnswer(DNSQuestion *question, CacheRecord *rr) mDNSlocal void removeKnownAnswer(DNSQuestion *question, CacheRecord *rr) { - CacheRecord *ptr, *prev = NULL; + CacheRecord *ptr, *prev = mDNSNULL; for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next) { - if (sameResourceRecord(&ptr->resrec, &rr->resrec)) + if (SameResourceRecord(&ptr->resrec, &rr->resrec)) { if (prev) prev->next = ptr->next; else question->uDNS_info.knownAnswers = ptr->next; @@ -616,7 +1667,7 @@ mDNSlocal void removeKnownAnswer(DNSQuestion *question, CacheRecord *rr) mDNSlocal void addKnownAnswer(DNSQuestion *question, const CacheRecord *rr) { - CacheRecord *newCR = NULL; + CacheRecord *newCR = mDNSNULL; mDNSu32 size; size = sizeof(CacheRecord) + rr->resrec.rdlength - InlineCacheRDSize; @@ -633,7 +1684,7 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en { const mDNSu8 *ptr; int i; - CacheRecord *fptr, *ka, *cr, *answers = NULL, *prev = NULL; + CacheRecord *fptr, *ka, *cr, *answers = mDNSNULL, *prev = mDNSNULL; LargeCacheRecord *lcr; if (question != m->uDNS_info.CurrentQuery) { LogMsg("ERROR: deriveGoodbyes called without CurrentQuery set!"); return; } @@ -647,8 +1698,11 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en ka = question->uDNS_info.knownAnswers; while (ka) { - debugf("deriving goodbye for %s", ka->resrec.name.c); + debugf("deriving goodbye for %##s", ka->resrec.name.c); + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback question->QuestionCallback(m, question, &ka->resrec, mDNSfalse); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again if (question != m->uDNS_info.CurrentQuery) { debugf("deriveGoodbyes - question removed via callback. returning."); @@ -658,7 +1712,7 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en ka = ka->next; ufree(fptr); } - question->uDNS_info.knownAnswers = NULL; + question->uDNS_info.knownAnswers = mDNSNULL; return; } @@ -684,14 +1738,16 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en while (ka) { for (cr = answers; cr; cr = cr->next) - { if (sameResourceRecord(&ka->resrec, &cr->resrec)) break; } + { 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); + debugf("deriving goodbye for %##s", ka->resrec.name.c); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback question->QuestionCallback(m, question, &ka->resrec, mDNSfalse); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again if (question != m->uDNS_info.CurrentQuery) { debugf("deriveGoodbyes - question removed via callback. returning."); @@ -715,7 +1771,7 @@ mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *en return; pkt_error: - LogMsg("ERROR: deriveGoodbyes - received malformed response to query for %s (%d)", + LogMsg("ERROR: deriveGoodbyes - received malformed response to query for %##s (%d)", question->qname.c, question->qtype); return; @@ -729,12 +1785,15 @@ mDNSlocal void pktResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 * int i; LargeCacheRecord lcr; CacheRecord *cr = &lcr.r; - mDNSBool goodbye, inKAList; + mDNSBool goodbye, inKAList, followedCName = mDNSfalse; LLQ_Info *llqInfo = question->uDNS_info.llq; + domainname origname; if (question != m->uDNS_info.CurrentQuery) { LogMsg("ERROR: pktResponseHdnlr called without CurrentQuery ptr set!"); return; } - + + question->uDNS_info.Answered = mDNStrue; + ptr = LocateAnswers(msg, end); if (!ptr) goto pkt_error; @@ -744,32 +1803,53 @@ mDNSlocal void pktResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 * if (!ptr) goto pkt_error; if (ResourceRecordAnswersQuestion(&cr->resrec, question)) { + if (cr->resrec.rrtype == kDNSType_CNAME) + { + if (followedCName) LogMsg("Error: multiple CNAME referals for question %##s", question->qname.c); + else + { + debugf("Following cname %##s -> %##s", question->qname.c, cr->resrec.rdata->u.name.c); + AssignDomainName(origname, question->qname); + AssignDomainName(question->qname, cr->resrec.rdata->u.name); + question->qnamehash = DomainNameHashValue(&question->qname); + followedCName = mDNStrue; + i = -1; // restart packet answer matching + ptr = LocateAnswers(msg, end); + continue; + } + } + 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); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback question->QuestionCallback(m, question, &cr->resrec, !goodbye); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again if (question != m->uDNS_info.CurrentQuery) { debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning"); return; } } - else - { - LogMsg("unexpected answer: %s", cr->resrec.name.c); - } + else if (!followedCName || !SameDomainName(&cr->resrec.name, &origname)) + LogMsg("Question %##s type %d - unexpected answer %##s type %d", question->qname.c, question->qtype, cr->resrec.name.c, cr->resrec.rrtype); + } + + if (!llq || llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume) + { + deriveGoodbyes(m, msg, end,question); + if (llq && llqInfo->deriveRemovesOnResume) llqInfo->deriveRemovesOnResume = mDNSfalse; } - if (llq && (llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume)) - { deriveGoodbyes(m, msg, end,question); llqInfo->deriveRemovesOnResume = mDNSfalse; } - //!!!KRS should we derive goodbyes for non-LLQs? + // our interval may be set lower to recover from failures - now that we have an answer, fully back off retry + if (question->ThisQInterval < MAX_UCAST_POLL_INTERVAL) question->ThisQInterval = MAX_UCAST_POLL_INTERVAL; return; pkt_error: - LogMsg("ERROR: pktResponseHndlr - received malformed response to query for %s (%d)", + LogMsg("ERROR: pktResponseHndlr - received malformed response to query for %##s (%d)", question->qname.c, question->qtype); return; } @@ -786,134 +1866,248 @@ mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 * pktResponseHndlr(m, msg, end, question, mDNStrue); } - - -mDNSlocal void unlinkSRS(uDNS_GlobalInfo *u, ServiceRecordSet *srs) +mDNSlocal mStatus ParseTSIGError(mDNS *m, const DNSMessage *msg, const mDNSu8 *end, const domainname *displayname) { - ServiceRecordSet *ptr, *prev = NULL; + LargeCacheRecord lcr; + const mDNSu8 *ptr; + mStatus err = mStatus_NoError; + int i; + + ptr = LocateAdditionals(msg, end); + if (!ptr) goto finish; - for (ptr = u->ServiceRegistrations; ptr; ptr = ptr->next) + for (i = 0; i < msg->h.numAdditionals; i++) { - if (ptr == srs) + ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);; + if (!ptr) goto finish; + if (lcr.r.resrec.rrtype == kDNSType_TSIG) { - if (prev) prev->next = ptr->next; - else u->ServiceRegistrations = ptr->next; - ptr->next = NULL; - return; + mDNSu32 macsize; + mDNSu8 *rd = lcr.r.resrec.rdata->u.data; + mDNSu8 *rdend = rd + MaximumRDSize; + int alglen = DomainNameLength(&lcr.r.resrec.rdata->u.name); + + if (rd + alglen > rdend) goto finish; + rd += alglen; // algorithm name + if (rd + 6 > rdend) goto finish; + rd += 6; // 48-bit timestamp + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + rd += sizeof(mDNSOpaque16); // fudge + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + macsize = mDNSVal16(*(mDNSOpaque16 *)rd); + rd += sizeof(mDNSOpaque16); // MAC size + if (rd + macsize > rdend) goto finish; + rd += macsize; + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + rd += sizeof(mDNSOpaque16); // orig id + if (rd + sizeof(mDNSOpaque16) > rdend) goto finish; + err = mDNSVal16(*(mDNSOpaque16 *)rd); // error code + + if (err == TSIG_ErrBadSig) { LogMsg("%##s: bad signature", displayname->c); err = mStatus_BadSig; } + else if (err == TSIG_ErrBadKey) { LogMsg("%##s: bad key", displayname->c); err = mStatus_BadKey; } + else if (err == TSIG_ErrBadTime) { LogMsg("%##s: bad time", displayname->c); err = mStatus_BadTime; } + else if (err) { LogMsg("%##s: unknown tsig error %d", err); err = mStatus_UnknownErr; } + goto finish; } - prev = ptr; } - LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list"); + + finish: + return err; } - -mDNSlocal mStatus checkUpdateResult(domainname *name, mDNSu8 rcode, const DNSMessage *msg) - { +mDNSlocal mStatus checkUpdateResult(domainname *displayname, mDNSu8 rcode, mDNS *m, const DNSMessage *msg, const mDNSu8 *end) + { (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); + LogMsg("name in use: %##s", displayname->c); return mStatus_NameConflict; } else if (rcode == kDNSFlag1_RC_Refused) { - LogMsg("Update %s refused", name->c); - return mStatus_Refused; + LogMsg("Update %##s refused", displayname->c); + return mStatus_Refused; } else if (rcode == kDNSFlag1_RC_NXRRSet) { - LogMsg("Reregister refusted (NXRRSET): %s", name->c); + LogMsg("Reregister refused (NXRRSET): %##s", displayname->c); return mStatus_NoSuchRecord; } else if (rcode == kDNSFlag1_RC_NotAuth) { - LogMsg("Permission denied (NOAUTH): %s", name->c); - return mStatus_NoAuth; + // TSIG errors should come with FmtErr as per RFC 2845, but BIND 9 sends them with NotAuth so we look here too + mStatus tsigerr = ParseTSIGError(m, msg, end, displayname); + if (!tsigerr) + { + LogMsg("Permission denied (NOAUTH): %##s", displayname->c); + return mStatus_UnknownErr; + } + else return tsigerr; } else if (rcode == kDNSFlag1_RC_FmtErr) { - LogMsg("Format Error: %s", name->c); - return mStatus_UnknownErr; - //!!!KRS need to parse message for TSIG errors + mStatus tsigerr = ParseTSIGError(m, msg, end, displayname); + if (!tsigerr) + { + LogMsg("Format Error: %##s", displayname->c); + return mStatus_UnknownErr; + } + else return tsigerr; } else { - LogMsg("Update %s failed with rcode %d", name->c, rcode); + LogMsg("Update %##s failed with rcode %d", displayname->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 + mDNSBool InvokeCallback = mDNSfalse; + AuthRecord *UpdateR = mDNSNULL; + uDNS_RegInfo *info = &srs->uDNS_info; + NATTraversalInfo *nat = srs->uDNS_info.NATinfo; + ExtraResourceRecord **e = &srs->Extras; - switch (srs->uDNS_info.state) + switch (info->state) { case regState_Pending: - case regState_Refresh: - if (err) + if (err == mStatus_NameConflict && !info->TestForSelfConflict) { - 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; - } + info->TestForSelfConflict = mDNStrue; + debugf("checking for self-conflict of service %##s", srs->RR_SRV.resrec.name.c); + SendServiceRegistration(m, srs); + return; + } + else if (info->TestForSelfConflict) + { + info->TestForSelfConflict = mDNSfalse; + if (err == mStatus_NoSuchRecord) err = mStatus_NameConflict; // NoSuchRecord implies that our prereq was not met, so we actually have a name conflict + if (err) info->state = regState_Unregistered; + else info->state = regState_Registered; + InvokeCallback = mDNStrue; + break; + } + else if (err == mStatus_UnknownErr && info->lease) + { + LogMsg("Re-trying update of service %##s without lease option", srs->RR_SRV.resrec.name.c); + info->lease = mDNSfalse; + info->expire = -1; + SendServiceRegistration(m, srs); + return; } else { - if (srs->uDNS_info.state == regState_Refresh) - { - srs->uDNS_info.state = regState_Registered; - return; - } - srs->uDNS_info.state = regState_Registered; + if (err) { LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name.c); info->state = regState_Unregistered; } //!!!KRS make sure all structs will still get cleaned up when client calls DeregisterService with this state + else info->state = regState_Registered; + InvokeCallback = mDNStrue; break; - } + } + case regState_Refresh: + if (err) + { + LogMsg("Error %ld for refresh of service %##s", err, srs->RR_SRV.resrec.name.c); + InvokeCallback = mDNStrue; + info->state = regState_Unregistered; + } + else 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; + if (err) LogMsg("Error %ld for deregistration of service %##s", err, srs->RR_SRV.resrec.name.c); + if (info->LostTarget) + { + info->state = regState_NoTarget; + break; + } + err = mStatus_MemFree; + InvokeCallback = mDNStrue; + if (nat) + { + if (nat->state == NATState_Deleted) { FreeNATInfo(m, nat); info->NATinfo = mDNSNULL; } // deletion copmleted + else nat->reg.ServiceRegistration = mDNSNULL; // allow mapping deletion to continue + } + info->state = regState_Unregistered; 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) { debugf("Error %ld received prior to deferred derigstration of %##s", err, srs->RR_SRV.resrec.name.c); } + debugf("Performing deferred deregistration of %##s", srs->RR_SRV.resrec.name.c); + info->state = regState_Registered; // benign to set even if we've got an error + SendServiceDeregistration(m, srs); + return; + case regState_UpdatePending: + // find the record being updated + UpdateR = &srs->RR_TXT; 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 + LogMsg("hndlServiceUpdateReply: error updating resource record"); + UpdateR->uDNS_info.state = regState_Unregistered; + InvokeCallback = mDNStrue; // signal error via service callback } - else srs->uDNS_info.state = regState_Registered; + else + { + UpdateR->uDNS_info.state = regState_Registered; + SwapRData(m, UpdateR, mDNStrue); + } + 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); + case regState_FetchingZoneData: + case regState_Registered: + case regState_Cancelled: + case regState_Unregistered: + case regState_NATMap: + case regState_NoTarget: + case regState_ExtraQueued: + LogMsg("hndlServiceUpdateReply called for service %##s in unexpected state %d with error %ld. Unlinking.", + srs->RR_SRV.resrec.name.c, info->state, err); err = mStatus_UnknownErr; } - if (err) + if ((info->LostTarget || info->SRVUpdateDeferred) && (info->state == regState_NoTarget || info->state == regState_Registered)) { - unlinkSRS(&m->uDNS_info, srs); // name conflicts, force dereg, and errors - srs->uDNS_info.state = regState_Unregistered; + UpdateSRV(m, srs); + return; } - - srs->ServiceCallback(m, srs, err); + + while (*e) + { + uDNS_RegInfo *einfo = &(*e)->r.uDNS_info; + if (einfo->state == regState_ExtraQueued) + { + if (info->state == regState_Registered && !err) + { + // extra resource record queued for this service - copy zone info and register + AssignDomainName(einfo->zone, info->zone); + einfo->ns = info->ns; + einfo->port = info->port; + einfo->lease = info->lease; + sendRecordRegistration(m, &(*e)->r); + e = &(*e)->next; + } + else if (err && einfo->state != regState_Unregistered) + { + // unlink extra from list + einfo->state = regState_Unregistered; + *e = (*e)->next; + } + else e = &(*e)->next; + } + else e = &(*e)->next; + } + + srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; // reset retry delay for future refreshes, dereg, etc. + if (srs->RR_TXT.uDNS_info.UpdateQueued) SendRecordUpdate(m, &srs->RR_TXT, &srs->uDNS_info); + + if (info->state == regState_Unregistered) unlinkSRS(&m->uDNS_info, srs); + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (InvokeCallback) srs->ServiceCallback(m, srs, err); + else if (info->ClientCallbackDeferred) + { + info->ClientCallbackDeferred = mDNSfalse; + srs->ServiceCallback(m, srs, info->DeferredStatus); + } + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again // NOTE: do not touch structures after calling ServiceCallback } @@ -921,16 +2115,37 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err) { uDNS_GlobalInfo *u = &m->uDNS_info; + if (rr->uDNS_info.state == regState_UpdatePending) + { + if (err) + { + LogMsg("Update record failed for %##s (err %d)", rr->resrec.name.c, err); + rr->uDNS_info.state = regState_Unregistered; + } + else + { + debugf("Update record %##s - success", rr->resrec.name.c); + rr->uDNS_info.state = regState_Registered; + SwapRData(m, rr, mDNStrue); + } + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return; + } + 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", + 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 %d failed with error %ld", rr->resrec.name.c, rr->resrec.rrtype, err); - else err = mStatus_MemFree; + 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); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again return; } @@ -938,13 +2153,13 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err) { if (err) { - LogMsg("Cancelling deferred deregistration record %s type %d due to registration error %d", + LogMsg("Cancelling deferred deregistration record %##s type %d due to registration error %ld", 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", + 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); @@ -957,32 +2172,45 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err) { if (rr->uDNS_info.lease && err == mStatus_UnknownErr) { - LogMsg("Re-trying update of record %s without lease option", rr->resrec.name.c); + 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", + LogMsg("Registration of record %##s type %d failed with error %ld", rr->resrec.name.c, rr->resrec.rrtype, err); unlinkAR(&u->RecordRegistrations, rr); rr->uDNS_info.state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return; } else { + if (rr->uDNS_info.UpdateQueued) + { + debugf("%##s: sending queued update", rr->resrec.name.c); + rr->uDNS_info.state = regState_Registered; + SendRecordUpdate(m ,rr, &rr->uDNS_info); + return; + } 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); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again } return; } } - LogMsg("Received unexpected response for record %s type %d, in state %d, with response error %d", + LogMsg("Received unexpected response for record %##s type %d, in state %d, with response error %ld", rr->resrec.name.c, rr->resrec.rrtype, rr->uDNS_info.state, err); } @@ -993,7 +2221,8 @@ mDNSlocal void SetUpdateExpiration(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSu8 *ptr; int i; mDNSu32 lease = 0; - + mDNSs32 expire; + ptr = LocateAdditionals(msg, end); if (info->lease && (ptr = LocateAdditionals(msg, end))) @@ -1013,13 +2242,43 @@ mDNSlocal void SetUpdateExpiration(mDNS *m, DNSMessage *msg, const mDNSu8 *end, } if (lease > 0) - info->expire = (mDNSPlatformTimeNow() + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4); + { + expire = (mDNSPlatformTimeNow(m) + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4); + if (info->state == regState_UpdatePending) + // if updating individual record, the service record set may expire sooner + { if (expire - info->expire < 0) info->expire = expire; } + else info->expire = expire; + } else info->expire = -1; } +mDNSexport void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + NATTraversalInfo *ptr, *cur; + NATOp_t op; + + // check length, version, opcode + if (len < ADDR_REPLY_PKTLEN && len < PORTMAP_PKTLEN) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; } + if (pkt[0] != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expect version %d)", pkt[0], NATMAP_VERS); return; } + op = pkt[1]; + if (!(op & NATMAP_RESPONSE_MASK)) { LogMsg("Received NAT Traversal message that is not a response (opcode %d)", op); return; } + + ptr = u->NATTraversals; + while (ptr) + { + cur = ptr; + ptr = ptr->next; + if ((cur->state == NATState_Request || cur->state == NATState_Refresh) && + (cur->op | NATMAP_RESPONSE_MASK) == op) + cur->ReceiveResponse(cur, m, pkt, len); // callback may delete "cur" + // specific request/reply matching logic handled by callback - we don't know if this was a match, so we don't break here + } + } + 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) + const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) { DNSQuestion *qptr; AuthRecord *rptr; @@ -1028,35 +2287,37 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS uDNS_GlobalInfo *u = &m->uDNS_info; mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; - mDNSu8 UpdateR = kDNSFlag0_OP_Update | kDNSFlag0_QR_Response; + mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); mDNSu8 rcode = (mDNSu8)(msg->h.flags.b[1] & kDNSFlag1_RC); - + + mDNSs32 timenow = mDNSPlatformTimeNow(m); + // 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; + // LLQ Responses over TCP not currently supported + if (srcaddr && 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 (timenow - (qptr->LastQTime + RESPONSE_WINDOW) > 0) + { LogMsg("uDNS_ReceiveMsg - response received after maximum allowed window. Discarding"); return; } 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; + u->CurrentQuery = mDNSNULL; // Note: responseCallback can invalidate qptr return; } @@ -1069,7 +2330,7 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS { if (sptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) { - err = checkUpdateResult(&sptr->RR_SRV.resrec.name, rcode, msg); + err = checkUpdateResult(&sptr->RR_SRV.resrec.name, rcode, m, msg, end); if (!err) SetUpdateExpiration(m, msg, end, &sptr->uDNS_info); hndlServiceUpdateReply(m, sptr, err); return; @@ -1079,7 +2340,7 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS { if (rptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger) { - err = checkUpdateResult(&rptr->resrec.name, rcode, msg); + err = checkUpdateResult(&rptr->resrec.name, rcode, m, msg, end); if (!err) SetUpdateExpiration(m, msg, end, &rptr->uDNS_info); hndlRecordUpdateReply(m, rptr, err); return; @@ -1089,28 +2350,35 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS 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) +// lookup a DNS Server, matching by name in split-dns configurations. Result stored in addr parameter if successful +mDNSlocal mDNSBool GetServerForName(uDNS_GlobalInfo *u, const domainname *name, mDNSAddr *addr) { - int i; - for (i = 0; i < 32; i++) - if (u->Servers[i].ip.v4.NotAnInteger) return &u->Servers[i]; + DNSServer *curmatch = mDNSNULL, *p = u->Servers; + int i, ncount, scount, curmatchlen = -1; - return NULL; - } + *addr = zeroAddr; + ncount = name ? CountLabels(name) : 0; + while (p) + { + scount = CountLabels(&p->domain); + if (scount <= ncount && scount > curmatchlen) + { + // only inspect if server's domain is longer than current best match and shorter than the name itself + const domainname *tail = name; + for (i = 0; i < ncount - scount; i++) + tail = (domainname *)(tail->c + 1 + tail->c[0]); // find "tail" (scount labels) of name + if (SameDomainName(tail, &p->domain)) { curmatch = p; curmatchlen = scount; } + } + p = p->next; + } + + if (curmatch) + { + *addr = curmatch->addr; + return mDNStrue; + } + else return mDNSfalse; + } // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -1151,7 +2419,7 @@ mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, DNSQuestion *questi if (includeQuestion) { ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass); - if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return NULL; } + if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return mDNSNULL; } } // locate OptRR if it exists, set pointer to end // !!!KRS implement me @@ -1170,8 +2438,8 @@ mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, DNSQuestion *questi 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; } + ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0); + if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; } return ptr; } @@ -1193,7 +2461,7 @@ mDNSlocal mDNSBool getLLQAtIndex(mDNS *m, DNSMessage *msg, const mDNSu8 *end, LL { 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)); + umemcpy(llq, (mDNSu8 *)&lcr.r.resrec.rdata->u.opt.OptData.llq + (index * sizeof(LLQOptData)), sizeof(LLQOptData)); // !!! Should convert to host byte order? return mDNStrue; } @@ -1204,12 +2472,12 @@ mDNSlocal void recvRefreshReply(mDNS *m, DNSMessage *msg, const mDNSu8 *end, DNS qInfo = q->uDNS_info.llq; if (!getLLQAtIndex(m, msg, end, &pktData, 0)) { LogMsg("ERROR recvRefreshReply - getLLQAtIndex"); return; } - if (pktData.llqOp != kLLQ_Refresh) return; + if (pktData.llqOp != kLLQOp_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->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond); + qInfo->retry = qInfo->expire - ((mDNSs32)pktData.lease * mDNSPlatformOneSecond/2); qInfo->origLease = pktData.lease; qInfo->state = LLQ_Established; @@ -1222,19 +2490,22 @@ mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease) LLQOptData llq; LLQ_Info *info = q->uDNS_info.llq; mStatus err; + mDNSs32 timenow; - if (info->state == kLLQ_Refresh && info->ntries >= kLLQ_MAX_TRIES) + timenow = mDNSPlatformTimeNow(m); + if ((info->state == LLQ_Refresh && info->ntries >= kLLQ_MAX_TRIES) || + info->expire - timenow < 0) { - LogMsg("sendLLQRefresh - %d failed attempts for llq %s", info->ntries, q->qname.c); + LogMsg("Unable to refresh LLQ %##s - will retry in %d minutes", q->qname.c, kLLQ_DEF_RETRY/60); info->state = LLQ_Retry; - info->retry = mDNSPlatformTimeNow() + kLLQ_DEF_RETRY * mDNSPlatformOneSecond; + info->retry = mDNSPlatformTimeNow(m) + 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.llqOp = kLLQOp_Refresh; llq.err = LLQErr_NoError; umemcpy(llq.id, info->id, 8); llq.lease = lease; @@ -1243,34 +2514,44 @@ mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease) 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); + err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL); + if (err) LogMsg("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %ld", err); if (info->state == LLQ_Established) info->ntries = 1; else info->ntries++; info->state = LLQ_Refresh; - q->LastQTime = mDNSPlatformTimeNow(); + q->LastQTime = timenow; 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) +mDNSlocal mDNSBool 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; + LLQOptData opt; + + (void)InterfaceID; // unused + + // find Opt RR, verify correct ID + if (!getLLQAtIndex(m, msg, end, &opt, 0)) { debugf("Pkt does not contain LLQ Opt"); return mDNSfalse; } + if (opt.llqOp != kLLQOp_Event) { LogMsg("recvLLQEvent - Bad LLQ Opcode %d", opt.llqOp); return mDNSfalse; } + if (!q->uDNS_info.llq) { LogMsg("Error: recvLLQEvent - question onject does not contain LLQ metadata"); return mDNSfalse; } + if (!sameID(opt.id, q->uDNS_info.llq->id)) { LogMsg("recvLLQEvent - incorrect ID. Discarding"); return mDNSfalse; } // 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; + if (m->uDNS_info.CurrentQuery != q) return mDNStrue; // 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); - } + if (!ackEnd) { LogMsg("ERROR: recvLLQEvent - putQuestion"); return mDNSfalse; } + err = mDNSSendDNSMessage(m, &ack, ackEnd, mDNSInterface_Any, srcaddr, srcport, -1, mDNSNULL); + if (err) LogMsg("ERROR: recvLLQEvent - mDNSSendDNSMessage returned %ld", err); + return mDNStrue; + } @@ -1280,13 +2561,14 @@ mDNSlocal void hndlChallengeResponseAck(mDNS *m, DNSMessage *pktMsg, const mDNSu 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->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)llq->lease * mDNSPlatformOneSecond); + info->retry = info->expire - ((mDNSs32)llq->lease * mDNSPlatformOneSecond / 2); info->origLease = llq->lease; info->state = LLQ_Established; + q->uDNS_info.responseCallback = llqResponseHndlr; - llqResponseHndlr(m, pktMsg, end, q, NULL); + llqResponseHndlr(m, pktMsg, end, q, mDNSNULL); return; error: @@ -1300,11 +2582,11 @@ mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) mDNSu8 *responsePtr = response.data; mStatus err; LLQOptData llqBuf; - mDNSs32 timenow = mDNSPlatformTimeNow(); + mDNSs32 timenow = mDNSPlatformTimeNow(m); if (info->ntries++ == kLLQ_MAX_TRIES) { - LogMsg("sendChallengeResponse: %d failed attempts for LLQ %s. Will re-try in %d minutes", + 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); @@ -1317,7 +2599,7 @@ mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) { llq = &llqBuf; llq->vers = kLLQ_Vers; - llq->llqOp = kLLQ_Setup; + llq->llqOp = kLLQOp_Setup; llq->err = LLQErr_NoError; umemcpy(llq->id, info->id, 8); llq->lease = info->origLease; @@ -1330,8 +2612,8 @@ mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) 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); + err = mDNSSendDNSMessage(m, &response, responsePtr, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL); + if (err) LogMsg("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %ld", err); // on error, we procede as normal and retry after the appropriate interval return; @@ -1345,43 +2627,43 @@ mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq) mDNSlocal void hndlRequestChallenge(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q) { LLQ_Info *info = q->uDNS_info.llq; - mDNSs32 timenow = mDNSPlatformTimeNow(); + mDNSs32 timenow = mDNSPlatformTimeNow(m); 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); + LogMsg("hndlRequestChallenge - received ServFull from server for LLQ %##s. Retry in %lu 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 + simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL); // 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); + LogMsg("LLQ %##s: static", q->qname.c); + simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL); return; case LLQErr_FormErr: - LogMsg("ERROR: hndlRequestChallenge - received FormErr from server for LLQ %s", q->qname.c); + 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); + 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); + 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); + LogMsg("hndlRequestChallenge: requested lease %lu, granted lease %lu", 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 + // update state info->state = LLQ_SecondaryRequest; umemcpy(info->id, llq->id, 8); info->ntries = 0; // first attempt to send response @@ -1406,58 +2688,61 @@ mDNSlocal void recvSetupResponse(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, (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; - } + if (rcode && rcode != kDNSFlag1_RC_NXDomain) goto poll; ptr = getQuestion(pktMsg, ptr, end, 0, &pktQuestion); - if (!ptr) { LogMsg("ERROR: recvSetupResponse - getQuestion"); goto error; } + if (!ptr) { LogMsg("ERROR: recvSetupResponse - getQuestion"); goto poll; } if (!SameDomainName(&q->qname, &pktQuestion.qname)) - { LogMsg("ERROR: recvSetupResponse - mismatched question in response for llq setup %s", q->qname.c); goto error; } + { LogMsg("ERROR: recvSetupResponse - mismatched question in response for llq setup %##s", q->qname.c); goto poll; } - 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 (!getLLQAtIndex(m, pktMsg, end, &llq, 0)) { debugf("recvSetupResponse - GetLLQAtIndex"); goto poll; } + if (llq.llqOp != kLLQOp_Setup) { LogMsg("ERROR: recvSetupResponse - bad op %d", llq.llqOp); goto poll; } + if (llq.vers != kLLQ_Vers) { LogMsg("ERROR: recvSetupResponse - bad vers %d", llq.vers); goto poll; } 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; + poll: + info->state = LLQ_Poll; + q->uDNS_info.responseCallback = llqResponseHndlr; + info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; } - - -mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info) +mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer) { DNSMessage msg; mDNSu8 *end; LLQOptData llqData; DNSQuestion *q = info->question; - mStatus err; - mDNSs32 timenow = mDNSPlatformTimeNow(); + mStatus err = mStatus_NoError; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + uDNS_GlobalInfo *u = &m->uDNS_info; - if (info->ntries++ == kLLQ_MAX_TRIES) + if (IsPrivateV4Addr(&u->PrimaryIP)) { - 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; + if (!u->LLQNatInfo) + { + info->state = LLQ_NatMapWait; + StartLLQNatMap(m); + return; + } + if (u->LLQNatInfo->state == NATState_Error) goto poll; + if (u->LLQNatInfo->state != NATState_Established && u->LLQNatInfo->state != NATState_Legacy) + { info->state = LLQ_NatMapWait; info->NATMap = mDNStrue; return; } + info->NATMap = mDNStrue; // this llq references the global llq nat mapping + } + + if (info->ntries++ >= kLLQ_MAX_TRIES) + { + debugf("startLLQHandshake: %d failed attempts for LLQ %##s. Polling.", kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60); + goto poll; } // set llq rdata llqData.vers = kLLQ_Vers; - llqData.llqOp = kLLQ_Setup; + llqData.llqOp = kLLQOp_Setup; llqData.err = LLQErr_NoError; ubzero(llqData.id, 8); llqData.lease = kLLQ_DefLease; @@ -1471,10 +2756,13 @@ mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info) 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 - + if (!defer) // if we are to defer, we simply set the retry timers so the request goes out in the future + { + err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL); + if (err) LogMsg("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %ld", 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; @@ -1482,44 +2770,67 @@ mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info) q->LastQTime = timenow; q->uDNS_info.responseCallback = recvSetupResponse; q->uDNS_info.internal = mDNStrue; + return; + + poll: + info->question->uDNS_info.responseCallback = llqResponseHndlr; + info->state = LLQ_Poll; + info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; } // 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; - } - + const zoneData_t *zoneInfo = mDNSNULL; + + // check state first to make sure it is OK to touch question object 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 + debugf("startLLQHandshake - LLQ Cancelled."); + info->question = mDNSNULL; // question may be deallocated ufree(info); return; } + if (!info->question) + { LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL question"); goto error; } + if (info->state != LLQ_GetZoneInfo) - { - LogMsg("ERROR: startLLQHandshake - bad state %d", info->state); - return; - } + { LogMsg("ERROR: startLLQHandshake - bad state %d", info->state); goto error; } + + if (err) + { LogMsg("ERROR: startLLQHandshakeCallback invoked with error code %ld", err); goto poll; } + + if (!result) + { LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL result and no error code"); goto error; } + zoneInfo = &result->zoneData; + + if (!zoneInfo->llqPort.NotAnInteger) + { debugf("LLQ port lookup failed - reverting to polling"); goto poll; } + // 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); + + if (info->state == LLQ_SuspendDeferred) info->state = LLQ_Suspended; + else startLLQHandshake(m, info, mDNSfalse); + return; + + poll: + info->question->uDNS_info.responseCallback = llqResponseHndlr; + info->state = LLQ_Poll; + info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll + info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL; + return; + + error: + info->state = LLQ_Error; } mDNSlocal mStatus startLLQ(mDNS *m, DNSQuestion *question) @@ -1542,10 +2853,10 @@ mDNSlocal mStatus startLLQ(mDNS *m, DNSQuestion *question) err = startGetZoneData(&question->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, info); if (err) { - LogMsg("ERROR: startLLQ - startGetZoneData returned %d", err); - info->question = NULL; + LogMsg("ERROR: startLLQ - startGetZoneData returned %ld", err); + info->question = mDNSNULL; ufree(info); - question->uDNS_info.llq = NULL; + question->uDNS_info.llq = mDNSNULL; return err; } @@ -1559,15 +2870,13 @@ mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, 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) { @@ -1580,20 +2889,24 @@ mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, { 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; } + { if (recvLLQEvent(m, q, msg, end, srcaddr, srcport, InterfaceID)) return mDNStrue; } + else if (msg->h.id.NotAnInteger == q->uDNS_info.id.NotAnInteger) + { + if (llqInfo->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers) + { recvRefreshReply(m, msg, end, q); return mDNStrue; } + if (llqInfo->state < LLQ_Static) + { + if ((llqInfo->state != LLQ_InitialRequest && llqInfo->state != LLQ_SecondaryRequest) || mDNSSameAddress(srcaddr, &llqInfo->servAddr)) + { 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) +mDNSexport mDNSBool uDNS_IsActiveQuery(DNSQuestion *const question, uDNS_GlobalInfo *u) { DNSQuestion *q; @@ -1601,8 +2914,8 @@ mDNSexport mDNSBool IsActiveUnicastQuery(DNSQuestion *const question, uDNS_Globa { 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", + if (!question->uDNS_info.id.NotAnInteger || question->InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&question->qname)) + LogMsg("Warning: Question %##s in Active Unicast Query list with id %d, interfaceID %p", question->qname.c, question->uDNS_info.id.NotAnInteger, question->InterfaceID); return mDNStrue; } @@ -1612,7 +2925,7 @@ mDNSexport mDNSBool IsActiveUnicastQuery(DNSQuestion *const question, uDNS_Globa // stopLLQ happens IN ADDITION to stopQuery mDNSlocal void stopLLQ(mDNS *m, DNSQuestion *question) - { + { LLQ_Info *info = question->uDNS_info.llq; (void)m; // unused @@ -1626,30 +2939,33 @@ mDNSlocal void stopLLQ(mDNS *m, DNSQuestion *question) //!!!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 + case LLQ_SuspendDeferred: + info->question = mDNSNULL; // 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; + goto end; default: debugf("stopLLQ - silently discarding LLQ in state %d", info->state); - goto free_info; + goto end; } - free_info: - info->question = NULL; + end: + if (info->NATMap) info->NATMap = mDNSfalse; + CheckForUnreferencedLLQMapping(m); + info->question = mDNSNULL; ufree(info); - question->uDNS_info.llq = NULL; - + question->uDNS_info.llq = mDNSNULL; + question->LongLived = mDNSfalse; } mDNSexport mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { uDNS_GlobalInfo *u = &m->uDNS_info; - DNSQuestion *qptr, *prev = NULL; + DNSQuestion *qptr, *prev = mDNSNULL; CacheRecord *ka; qptr = u->ActiveQueries; @@ -1674,93 +2990,58 @@ mDNSexport mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question) prev = qptr; qptr = qptr->next; } - LogMsg("uDNS_StopQuery: no such active query (%s)", question->qname.c); + 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; + 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)); + LogMsg("Attempt to start query with invalid qname %##s %##s", question->qname.c, DNSTypeName(question->qtype)); return mStatus_Invalid; } - question->next = NULL; + question->next = mDNSNULL; question->qnamehash = DomainNameHashValue(&question->qname); // to do quick domain name comparisons question->uDNS_info.id = newMessageID(u); + question->uDNS_info.Answered = mDNSfalse; // break here if its and LLQ if (question->LongLived) return startLLQ(m, question); + // else send the query to our server err = constructQueryMsg(&msg, &endPtr, question); if (err) return err; - // else send the query to our server - - question->LastQTime = mDNSPlatformTimeNow(); + question->LastQTime = mDNSPlatformTimeNow(m); 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); } + question->uDNS_info.knownAnswers = mDNSNULL; + if (GetServerForName(u, &question->qname, &server)) + { + err = mDNSSendDNSMessage(m, &msg, endPtr, mDNSInterface_Any, &server, UnicastDNSPort, -1, mDNSNULL); + if (err) { debugf("ERROR: startQuery - %ld (keeping question in list for retransmission", err); } + } - return err; + return mStatus_NoError; // don't return transient errors to caller } 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; + question->uDNS_info.context = mDNSNULL; + //LogOperation("uDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); return startQuery(m, question, 0); } @@ -1784,17 +3065,17 @@ mDNSlocal mStatus startInternalQuery(DNSQuestion *q, mDNS *m, InternalResponseHn /* startGetZoneData * - * asyncronously find the address of the nameserver for the enclosing zone for a given domain name, + * Asynchronously 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. + * by querying for the _dns-update._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 + * _dns-llq._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: * @@ -1878,14 +3159,14 @@ mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdat 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); + AssignDomainName(context->origName, *name); context->state = init; context->m = m; context->callback = callback; context->callbackInfo = callbackInfo; context->findUpdatePort = findUpdatePort; context->findLLQPort = findLLQPort; - getZoneData(m, NULL, NULL, NULL, context); + getZoneData(m, mDNSNULL, mDNSNULL, mDNSNULL, context); return mStatus_NoError; } @@ -1955,7 +3236,7 @@ mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DN 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); + AssignDomainName(result.zoneData.zoneName, context->zone); result.zoneData.zoneClass = context->zoneClass; result.zoneData.llqPort = context->findLLQPort ? context->llqPort : zeroIPPort; result.zoneData.updatePort = context->findUpdatePort ? context->updatePort : zeroIPPort; @@ -1964,7 +3245,7 @@ mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DN error: if (context && context->callback) - context->callback(mStatus_UnknownErr, context->m, context->callbackInfo, NULL); + context->callback(mStatus_UnknownErr, context->m, context->callbackInfo, mDNSNULL); cleanup: if (context && context->questionActive) { @@ -2014,7 +3295,7 @@ mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext 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", + LogMsg("ERROR: hndlLookupSOA - recursed to root label of %##s without finding SOA", context->origName.c); return smError; } @@ -2025,21 +3306,21 @@ mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext else context->curSOA = (domainname *)(context->curSOA->c + context->curSOA->c[0]+1); context->state = lookupSOA; - ustrcpy(query->qname.c, context->curSOA->c); + AssignDomainName(query->qname, *context->curSOA); 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); + if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (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); + AssignDomainName(context->zone, rr->name); context->zoneClass = rr->rrclass; - ustrcpy(context->ns.c, rr->rdata->u.soa.mname.c); + AssignDomainName(context->ns, rr->rdata->u.soa.mname); context->state = foundZone; } @@ -2049,19 +3330,19 @@ mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *con DNSQuestion *query = &context->question; mStatus err; LargeCacheRecord lcr; - ResourceRecord *rr = &lcr.r.resrec; + const ResourceRecord *const 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); + AssignDomainName(query->qname, context->zone); 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); + if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission", err); context->state = lookupNS; return smBreak; // break from SM until we receive another packet } @@ -2079,7 +3360,7 @@ mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *con return smContinue; // next routine will examine additionals section of A record } } - LogMsg("ERROR: could not confirm existance of NS record %s", context->zone.c); + LogMsg("ERROR: could not confirm existence of record %##s NS %##s", context->zone.c, context->ns.c); return smError; } else { LogMsg("ERROR: confirmNS - bad state %d", context->state); return smError; } @@ -2090,12 +3371,12 @@ mDNSlocal smAction queryNSAddr(ntaContext *context) mStatus err; DNSQuestion *query = &context->question; - ustrcpy(query->qname.c, context->ns.c); + AssignDomainName(query->qname, context->ns); 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); + if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err); context->state = lookupA; return smBreak; } @@ -2129,7 +3410,7 @@ mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext * } if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, &rr->name)) { - context->addr.NotAnInteger = rr->rdata->u.ip.NotAnInteger; + context->addr.NotAnInteger = rr->rdata->u.ipv4.NotAnInteger; context->state = foundA; return smContinue; } @@ -2148,7 +3429,7 @@ mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext * 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->addr.NotAnInteger = rr->rdata->u.ipv4.NotAnInteger; context->state = foundA; return smContinue; } @@ -2182,8 +3463,8 @@ mDNSlocal smAction lookupDNSPort(DNSMessage *msg, const mDNSu8 *end, ntaContext return smContinue; } } - LogMsg("hndlLookupUpdatePort %s - answer not contained in reply. Guessing port %d", portName, UnicastDNSPort); - *port = UnicastDNSPort; + debugf("hndlLookupUpdatePort - no answer for type %s", portName); + port->NotAnInteger = 0; context->state = foundPort; return smContinue; } @@ -2192,12 +3473,12 @@ mDNSlocal smAction lookupDNSPort(DNSMessage *msg, const mDNSu8 *end, ntaContext context->state = lookupPort; q = &context->question; MakeDomainNameFromDNSNameString(&q->qname, portName); - ustrcpy((q->qname.c + ustrlen(q->qname.c)), context->zone.c); + AppendDomainName(&q->qname, &context->zone); 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); + if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err); return smBreak; // break from state machine until we receive another packet } @@ -2241,18 +3522,19 @@ mDNSlocal void conQueryCallback(int sd, void *context, mDNSBool ConnectionEstabl tcpInfo_t *info = (tcpInfo_t *)context; DNSQuestion *question = info->question; int n; + mDNS *m = info->m; - question->uDNS_info.id.NotAnInteger = (mDNSu16)~0; - + mDNS_Lock(m); + 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; + if (err) { LogMsg("ERROR: conQueryCallback: constructQueryMsg - %ld", err); goto error; } + err = mDNSSendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, mDNSNULL); + question->LastQTime = mDNSPlatformTimeNow(m); + if (err) { LogMsg("ERROR: conQueryCallback: mDNSSendDNSMessage_tcp - %ld", err); goto error; } } else { @@ -2272,18 +3554,19 @@ mDNSlocal void conQueryCallback(int sd, void *context, mDNSBool ConnectionEstabl if (info->nread == info->replylen) { // finished reading message - receiveMsg(info->m, &info->reply, ((mDNSu8 *)&info->reply) + info->replylen, question->InterfaceID); + uDNS_ReceiveMsg(m, &info->reply, ((mDNSu8 *)&info->reply) + info->replylen, mDNSNULL, zeroIPPort, mDNSNULL, zeroIPPort, question->InterfaceID); mDNSPlatformTCPCloseConnection(sd); ufree(info); - return; } - else return; } + + mDNS_Unlock(m); return; error: mDNSPlatformTCPCloseConnection(sd); ufree(info); + mDNS_Unlock(m); } mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m) @@ -2292,23 +3575,23 @@ mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, uDNS_QuestionInfo *info = &question->uDNS_info; int sd; tcpInfo_t *context; + + if (!src) { LogMsg("hndlTruncatedAnswer: TCP DNS response had TC bit set: ignoring"); return; } 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 + info->id = newMessageID(&m->uDNS_info); connectionStatus = mDNSPlatformTCPConnect(src, UnicastDNSPort, question->InterfaceID, conQueryCallback, context, &sd); - if (connectionStatus == mStatus_ConnectionEstablished) // manually invoke callback if connection completes + if (connectionStatus == mStatus_ConnEstablished) // manually invoke callback if connection completes { conQueryCallback(sd, context, mDNStrue); return; } - if (connectionStatus == mStatus_ConnectionPending) return; // callback will be automatically invoked when connection completes + if (connectionStatus == mStatus_ConnPending) return; // callback will be automatically invoked when connection completes LogMsg("hndlTruncatedAnswer: connection failed"); uDNS_StopQuery(m, question); //!!!KRS can we really call this here? } @@ -2320,68 +3603,6 @@ mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, #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) { @@ -2390,7 +3611,6 @@ mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr) 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; @@ -2403,67 +3623,68 @@ mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr) 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); + ptr = PutResourceRecordTTLJumbo(&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); + ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl); if (!ptr) goto error; if (rr->uDNS_info.lease) - ptr = putUpdateLease(&msg, ptr); + { ptr = putUpdateLease(&msg, ptr, kLLQ_DefLease); if (!ptr) goto error; } 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; } - } + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, ®Info->ns, regInfo->port, -1, GetAuthInfoForZone(u, ®Info->zone)); + if (err) LogMsg("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %ld", err); + SetRecordRetry(m, rr); + if (regInfo->state != regState_Refresh) regInfo->state = regState_Pending; return; error: + LogMsg("sendRecordRegistration: Error formatting message"); if (rr->uDNS_info.state != regState_Unregistered) { unlinkAR(&u->RecordRegistrations, rr); rr->uDNS_info.state = regState_Unregistered; } - rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again // 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; + const zoneData_t *zoneData = mDNSNULL; uDNS_GlobalInfo *u = &m->uDNS_info; AuthRecord *ptr; + // make sure record is still in list 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; } + + // check error/result + if (err) { LogMsg("RecordRegistrationCallback: error %ld", err); goto error; } + if (!result) { LogMsg("ERROR: RecordRegistrationCallback invoked with NULL result and no error"); goto error; } + else zoneData = &result->zoneData; + 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", + debugf("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); @@ -2484,10 +3705,16 @@ mDNSlocal void RecordRegistrationCallback(mStatus err, mDNS *const m, void *auth } // cache zone data - ustrcpy(newRR->uDNS_info.zone.c, zoneData->zoneName.c); + AssignDomainName(newRR->uDNS_info.zone, zoneData->zoneName); 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; + if (zoneData->updatePort.NotAnInteger) newRR->uDNS_info.port = zoneData->updatePort; + else + { + debugf("Update port not advertised via SRV - guessing port 53, no lease option"); + newRR->uDNS_info.port = UnicastDNSPort; + newRR->uDNS_info.lease = mDNSfalse; + } sendRecordRegistration(m, newRR); return; @@ -2498,39 +3725,13 @@ error: unlinkAR(&u->RecordRegistrations, newRR); newRR->uDNS_info.state = regState_Unregistered; } - newRR->RecordCallback(m, newRR, err); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (newRR->RecordCallback) + newRR->RecordCallback(m, newRR, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again // 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; @@ -2538,71 +3739,93 @@ mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs) 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; + mDNSIPPort privport; + NATTraversalInfo *nat = srs->uDNS_info.NATinfo; + mDNSBool mapped = mDNSfalse; + domainname target; + AuthRecord *srv = &srs->RR_SRV; 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_PTR.resrec, mDNSNULL, 0); + SetNewRData(&srs->RR_TXT.resrec, mDNSNULL, 0); - //SetNewRData(&srs->RR_ADV.resrec, NULL, 0); //!!!KRS - SetNewRData(&srs->RR_PTR.resrec, NULL, 0); - SetNewRData(&srs->RR_TXT.resrec, NULL, 0); + // replace port w/ NAT mapping if necessary + if (nat && nat->PublicPort.NotAnInteger && + (nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy)) + { + privport = srv->resrec.rdata->u.srv.port; + srv->resrec.rdata->u.srv.port = nat->PublicPort; + mapped = mDNStrue; + } // construct update packet // set zone - ptr = putZone(&msg, ptr, end, &rInfo->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass)); + ptr = putZone(&msg, ptr, end, &rInfo->zone, mDNSOpaque16fromIntVal(srv->resrec.rrclass)); if (!ptr) goto error; - - if (srs->uDNS_info.state == regState_Refresh) + + if (srs->uDNS_info.TestForSelfConflict) { - // 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; + // update w/ prereq that records already exist to make sure previous registration was ours + if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0))) goto error; + if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_TXT.resrec, 0))) goto error; } - else + + else if (srs->uDNS_info.state != regState_Refresh) { - // use SRV for prereq - ptr = putPrereqNameNotInUse(&srs->RR_SRV.resrec.name, &msg, ptr, end); + // use SRV name for prereq + ptr = putPrereqNameNotInUse(&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 (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_PTR.resrec, srs->RR_PTR.resrec.rroriginalttl))) goto error; + if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) goto error; + + if (!GetServiceTarget(u, srv, &target)) + { + debugf("Couldn't get target for service %##s", srv->resrec.name.c); + rInfo->state = regState_NoTarget; + return; + } + if (!SameDomainName(&target, &srv->resrec.rdata->u.srv.target)) + { + AssignDomainName(srv->resrec.rdata->u.srv.target, target); + SetNewRData(&srv->resrec, mDNSNULL, 0); + } + + ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srv->resrec, srv->resrec.rroriginalttl); + if (!ptr) goto error; + // !!!KRS do subtypes/extras etc. if (srs->uDNS_info.lease) - ptr = putUpdateLease(&msg, ptr); + { ptr = putUpdateLease(&msg, ptr, kLLQ_DefLease); if (!ptr) goto error; } 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; } - } + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rInfo->ns, rInfo->port, -1, GetAuthInfoForZone(u, &rInfo->zone)); + if (err) { LogMsg("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %ld", err); goto error; } + if (rInfo->state != regState_Refresh) rInfo->state = regState_Pending; + + SetRecordRetry(m, &srs->RR_SRV); + rInfo->id.NotAnInteger = id.NotAnInteger; + if (mapped) srv->resrec.rdata->u.srv.port = privport; return; error: + LogMsg("SendServiceRegistration - Error formatting message"); + if (mapped) srv->resrec.rdata->u.srv.port = privport; unlinkSRS(u, srs); rInfo->state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback srs->ServiceCallback(m, srs, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again //!!!KRS will mem still be free'd on error? // NOTE: not safe to touch any client structures here } @@ -2610,10 +3833,13 @@ error: mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result) { ServiceRecordSet *srs = (ServiceRecordSet *)srsPtr; - const zoneData_t *zoneData = &result->zoneData; + const zoneData_t *zoneData = mDNSNULL; uDNS_GlobalInfo *u = &m->uDNS_info; if (err) goto error; + if (!result) { LogMsg("ERROR: serviceRegistrationCallback invoked with NULL result and no error"); goto error; } + else zoneData = &result->zoneData; + if (result->type != zoneDataResult) { LogMsg("ERROR: buildUpdatePacket passed incorrect result type %d", result->type); @@ -2625,95 +3851,54 @@ mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srs // client cancelled registration while fetching zone data srs->uDNS_info.state = regState_Unregistered; unlinkSRS(u, srs); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback srs->ServiceCallback(m, srs, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again return; } - + if (srs->RR_SRV.resrec.rrclass != zoneData->zoneClass) { - LogMsg("Service %s - class does not match zone", srs->RR_SRV.resrec.name.c); + 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); + AssignDomainName(srs->uDNS_info.zone, zoneData->zoneName); 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; - + srs->uDNS_info.ns = zoneData->primaryAddr; + if (zoneData->updatePort.NotAnInteger) srs->uDNS_info.port = zoneData->updatePort; + else + { + debugf("Update port not advertised via SRV - guessing port 53, no lease option"); + srs->uDNS_info.port = UnicastDNSPort; + srs->uDNS_info.lease = mDNSfalse; + } SendServiceRegistration(m, srs); return; error: unlinkSRS(u, srs); srs->uDNS_info.state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback srs->ServiceCallback(m, srs, err); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again //!!!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) +mDNSlocal mStatus SetupRecordRegistration(mDNS *m, AuthRecord *rr) { domainname *target = GetRRDomainNameTarget(&rr->resrec); + AuthRecord *ptr = m->uDNS_info.RecordRegistrations; + + while (ptr && ptr != rr) ptr = ptr->next; + if (ptr) { LogMsg("Error: SetupRecordRegistration - record %##s already in list!", rr->resrec.name.c); return mStatus_AlreadyRegistered; } 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", + LogMsg("Requested double-registration of physical record %##s type %d", rr->resrec.name.c, rr->resrec.rrtype); return mStatus_AlreadyRegistered; } @@ -2723,13 +3908,14 @@ mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr) if (!ValidateDomainName(&rr->resrec.name)) { - LogMsg("Attempt to register record with invalid name: %s", GetRRDisplayString(m, rr)); + LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(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)); + { + LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return mStatus_Invalid; } @@ -2740,262 +3926,812 @@ mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr) 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); - } + return mStatus_NoError; + } +mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr) + { + mStatus err = SetupRecordRegistration(m, rr); + if (err) return err; + else return startGetZoneData(&rr->resrec.name, m, mDNStrue, mDNSfalse, RecordRegistrationCallback, rr); + } -mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr) +mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr) { uDNS_GlobalInfo *u = &m->uDNS_info; DNSMessage msg; mDNSu8 *ptr = msg.data; mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); mStatus err; - uDNS_AuthInfo *authInfo; + + InitializeDNSMessage(&msg.h, rr->uDNS_info.id, UpdateReqFlags); + + 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; + + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rr->uDNS_info.ns, rr->uDNS_info.port, -1, GetAuthInfoForZone(u, &rr->uDNS_info.zone)); + if (err) LogMsg("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err); + + SetRecordRetry(m, rr); + rr->uDNS_info.state = regState_DeregPending; + return; + + error: + LogMsg("Error: SendRecordDeregistration - could not contruct deregistration packet"); + unlinkAR(&u->RecordRegistrations, rr); + rr->uDNS_info.state = regState_Unregistered; + } + + + +mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + NATTraversalInfo *n = rr->uDNS_info.NATinfo; + switch (rr->uDNS_info.state) { + case regState_NATMap: + // we're in the middle of a NAT traversal operation + if (!n) LogMsg("uDNS_DeregisterRecord: no NAT info context"); + else FreeNATInfo(m, n); // cause response to outstanding request to be ignored. + // Note: normally here we're trying to determine our public address, + //in which case there is not state to be torn down. For simplicity, + //we allow other operations to expire. + rr->uDNS_info.NATinfo = mDNSNULL; + rr->uDNS_info.state = regState_Unregistered; + break; + case regState_ExtraQueued: + rr->uDNS_info.state = regState_Unregistered; + break; case regState_FetchingZoneData: rr->uDNS_info.state = regState_Cancelled; return mStatus_NoError; + case regState_Refresh: case regState_Pending: + case regState_UpdatePending: rr->uDNS_info.state = regState_DeregDeferred; - debugf("Deferring deregistration of record %s until registration completes", rr->resrec.name.c); + LogMsg("Deferring deregistration of record %##s until registration completes", rr->resrec.name.c); return mStatus_NoError; case regState_Registered: - break; case regState_DeregPending: + break; + case regState_DeregDeferred: case regState_Cancelled: - LogMsg("Double deregistration of record %s type %d", + 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", + 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); + case regState_NoTarget: + LogMsg("ERROR: uDNS_DeregisterRecord called for record %##s with bad state (regState_NoTarget)", rr->resrec.name.c); 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 + if (rr->uDNS_info.state == regState_Unregistered) { - 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; } + // unlink and deliver memfree + + unlinkAR(&u->RecordRegistrations, rr); + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return mStatus_NoError; } - - return mStatus_NoError; - error: - if (rr->uDNS_info.state != regState_Unregistered) - { - unlinkAR(&u->RecordRegistrations, rr); - rr->uDNS_info.state = regState_Unregistered; - } - return mStatus_UnknownErr; - } + if (n) FreeNATInfo(m, n); + rr->uDNS_info.NATinfo = mDNSNULL; + SendRecordDeregistration(m, rr); + return mStatus_NoError; + } -mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs) +// register a service already in list with initialized state +mDNSlocal mStatus RegisterService(mDNS *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; - } + uDNS_RegInfo *info = &srs->uDNS_info; 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; + info->lease = mDNStrue; + + if (srs->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger && MapServicePort(m)) + { + // !!!KRS if interface is already in NATState_Legacy, don't try NATPMP + info->state = regState_NATMap; + StartNATPortMap(m, srs); + return mStatus_NoError; + } + + info->state = regState_FetchingZoneData; + return startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + } + +mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs) + { + // Note: ServiceRegistrations list is in the order they were created; important for in-order event delivery + ServiceRecordSet **p = &m->uDNS_info.ServiceRegistrations; + while (*p && *p != srs) p=&(*p)->next; + if (*p) { LogMsg("uDNS_RegisterService: %p %##s already in list", srs, srs->RR_SRV.resrec.name.c); return(mStatus_AlreadyRegistered); } + ubzero(&srs->uDNS_info, sizeof(uDNS_RegInfo)); + srs->uDNS_info.state = regState_Unregistered; + *p = srs; + srs->next = mDNSNULL; + return RegisterService(m, srs); + } + +mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs) + { + uDNS_RegInfo *info = &srs->uDNS_info; + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSMessage msg; + mDNSOpaque16 id; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + mStatus err = mStatus_UnknownErr; + + id = newMessageID(u); + InitializeDNSMessage(&msg.h, id, UpdateReqFlags); + + // put zone + ptr = putZone(&msg, ptr, end, &info->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass)); + if (!ptr) { LogMsg("ERROR: SendServiceDeregistration - putZone"); goto error; } + + if (!(ptr = putDeleteAllRRSets(&msg, ptr, &srs->RR_SRV.resrec.name))) goto error; // this deletes SRV, TXT, and Extras + if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_PTR.resrec))) goto error; + + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &info->ns, info->port, -1, GetAuthInfoForZone(u, &info->zone)); + if (err) { LogMsg("ERROR: SendServiceDeregistration - mDNSSendDNSMessage - %ld", err); goto error; } + + SetRecordRetry(m, &srs->RR_SRV); + info->id.NotAnInteger = id.NotAnInteger; + info->state = regState_DeregPending; + + return; - return startGetZoneData(&srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs); + error: + unlinkSRS(u, srs); + info->state = regState_Unregistered; } 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; + NATTraversalInfo *nat = srs->uDNS_info.NATinfo; + AuthRecord **r = &u->RecordRegistrations; + char *errmsg = "Unknown State"; - //!!!KRS make sure we're doing the right thing w/ memfree + // We "silently" unlink any Extras from our RecordRegistration list, as they are implicitly deleted from + // the server when we delete all RRSets for this name + while (*r) + { + if (SameDomainName(&srs->RR_SRV.resrec.name, &(*r)->resrec.name)) *r = (*r)->next; + else r = &(*r)->next; + } + + // don't re-register with a new target following deregistration + srs->uDNS_info.LostTarget = srs->uDNS_info.SRVUpdateDeferred = mDNSfalse; switch (srs->uDNS_info.state) { + case regState_NATMap: + // we're in the middle of nat mapping. clear ptr from NAT info to RR, unlink and give memfree + if (!nat) LogMsg("uDNS_DeregisterRecord: no NAT info context"); + else { nat->reg.ServiceRegistration = mDNSNULL; FreeNATInfo(m, nat); } + unlinkSRS(u, srs); + srs->uDNS_info.state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + srs->ServiceCallback(m, srs, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return mStatus_NoError; case regState_Unregistered: - LogMsg("ERROR: uDNS_DeregisterService - service not registerd"); - return mStatus_UnknownErr; + errmsg = "service not registered"; + goto error; 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_Pending: + case regState_Refresh: + case regState_UpdatePending: + // deregister following completion of in-flight operation + srs->uDNS_info.state = regState_DeregDeferred; + return mStatus_NoError; case regState_DeregPending: case regState_DeregDeferred: case regState_Cancelled: - LogMsg("uDNS_DeregisterService - deregistration in process"); - return mStatus_UnknownErr; + debugf("Double deregistration of service %##s", srs->RR_SRV.resrec.name.c); + return mStatus_NoError; + case regState_NoTarget: + unlinkSRS(u, srs); + srs->uDNS_info.state = regState_Unregistered; + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + srs->ServiceCallback(m, srs, mStatus_MemFree); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + return mStatus_NoError; + case regState_Registered: + if (nat) DeleteNATPortMapping(m, nat, srs); + srs->uDNS_info.state = regState_DeregPending; + SendServiceDeregistration(m, srs); + return mStatus_NoError; + case regState_ExtraQueued: // only for record registrations + errmsg = "bad state (regState_ExtraQueued)"; + goto error; } - srs->uDNS_info.state = regState_DeregPending; - InitializeDNSMessage(&msg.h, srs->uDNS_info.id, UpdateReqFlags); + error: + LogMsg("Error, uDNS_DeregisterService: %s", errmsg); + return mStatus_BadReferenceErr; + } - // 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; } +// note that the RegInfo will be either for the record, or for the parent ServiceRecordSet +mDNSlocal void SendRecordUpdate(mDNS *m, AuthRecord *rr, uDNS_RegInfo *info) + { + DNSMessage msg; + mDNSu8 *ptr = msg.data; + mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSOpaque16 id; + mStatus err = mStatus_UnknownErr; + + rr->uDNS_info.UpdateQueued = mDNSfalse; // if this was queued, clear flag + id = newMessageID(u); + InitializeDNSMessage(&msg.h, id, UpdateReqFlags); + info->id.NotAnInteger = id.NotAnInteger; + + // set zone + ptr = putZone(&msg, ptr, end, &info->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto error; + + // delete the original record + ptr = putDeletionRecord(&msg, ptr, &rr->resrec); + if (!ptr) goto error; + + // change the rdata, add the new record + SwapRData(m, rr, mDNSfalse); + ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl); + SwapRData(m, rr, mDNSfalse); // swap rdata back to original in case we need to retransmit + if (!ptr) goto error; // (rdata gets changed permanently on success) + + if (info->lease) + { ptr = putUpdateLease(&msg, ptr, kLLQ_DefLease); if (!ptr) 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; } + // don't report send errors - retransmission will occurr if necessary + err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &info->ns, info->port, -1, GetAuthInfoForZone(u, &info->zone)); + if (err) debugf("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %ld", err); - 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 + //!!! Update this when we implement retransmission for services + SetRecordRetry(m, rr); + + rr->uDNS_info.state = regState_UpdatePending; + if (&rr->uDNS_info != info) info->state = regState_UpdatePending; // set parent SRS + return; +error: + LogMsg("ERROR: SendRecordUpdate. Error formatting update message."); + info ->state = regState_Registered; + } - authInfo = GetAuthInfoForZone(u, &srs->uDNS_info.zone); - if (authInfo) +mDNSexport mStatus uDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra) + { + mStatus err = mStatus_UnknownErr; + + extra->r.resrec.RecordType = kDNSRecordTypeShared; // don't want it to conflict with the service name + extra->r.RecordCallback = mDNSNULL; // don't generate callbacks for extra RRs + + if (sr->uDNS_info.state == regState_Registered || sr->uDNS_info.state == regState_Refresh) + err = uDNS_RegisterRecord(m, &extra->r); + else { - 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; } + err = SetupRecordRegistration(m, &extra->r); + extra->r.uDNS_info.state = regState_ExtraQueued; } - else + + if (!err) + { + extra->next = sr->Extras; + sr->Extras = extra; + } + return err; + } + +mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + ServiceRecordSet *sptr, *parent = mDNSNULL; + AuthRecord *rptr; + uDNS_RegInfo *info = mDNSNULL; + + // find the record in registered service list + for (sptr = u->ServiceRegistrations; sptr; sptr = sptr->next) + if (&sptr->RR_TXT == rr) { info = &sptr->uDNS_info; parent = sptr; break; } + + if (!parent) { - 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; } + // record not part of a service - check individual record registrations + for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next) + if (rptr == rr) { info = &rr->uDNS_info; break; } } + + if (!info) goto unreg_error; + + // uDNS-private pointers so that mDNS.c layer doesn't nuke rdata of an in-flight update + rr->uDNS_info.UpdateRData = rr->NewRData; + rr->uDNS_info.UpdateRDLen = rr->newrdlength; + rr->uDNS_info.UpdateRDCallback = rr->UpdateCallback; + rr->NewRData = mDNSNULL; - return mStatus_NoError; + switch(info->state) + { + case regState_DeregPending: + case regState_DeregDeferred: + case regState_Cancelled: + case regState_Unregistered: + // not actively registered + goto unreg_error; + + case regState_FetchingZoneData: + case regState_NATMap: + case regState_ExtraQueued: + // change rdata directly since it hasn't been sent yet + SwapRData(m, rr, mDNStrue); + return mStatus_NoError; + + case regState_Pending: + case regState_Refresh: + case regState_UpdatePending: + // registration in-flight. mark for update after service registration completes + rr->uDNS_info.UpdateQueued = mDNStrue; // note that we mark the record's Queued flag, not its parent's + return mStatus_NoError; + + case regState_Registered: + SendRecordUpdate(m, rr, info); + return mStatus_NoError; - error: - unlinkSRS(u, srs); - srs->uDNS_info.state = regState_Unregistered; - return err; + case regState_NoTarget: + LogMsg("Bad state (regState_NoTarget)"); return mStatus_UnknownErr; // state for service records only + } + + unreg_error: + LogMsg("Requested update of record %##s type %d, part of service not currently registered", + rr->resrec.name.c, rr->resrec.rrtype); + return mStatus_Invalid; } -mDNSexport void uDNS_Execute(mDNS *const m) + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Periodic Execution Routines +#endif + + +mDNSlocal mDNSs32 CheckNATMappings(mDNS *m, mDNSs32 timenow) + { + NATTraversalInfo *ptr, *cur; + mDNSs32 nextevent; + + ptr = m->uDNS_info.NATTraversals; + nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + while (ptr) + { + cur = ptr; + ptr = ptr->next; + if (cur->retry - timenow < 0) + { + if (cur->state == NATState_Established) RefreshNATMapping(cur, m); + else if (cur->state == NATState_Request || cur->state == NATState_Refresh) + { + if (cur->ntries >= NATMAP_MAX_TRIES) cur->ReceiveResponse(cur, m, mDNSNULL, 0); // may invalidate "cur" + else SendNATMsg(cur, m); + } + } + else if (cur->retry - nextevent < 0) nextevent = cur->retry; + } + return nextevent; + } + +mDNSlocal mDNSs32 CheckQueries(mDNS *m, mDNSs32 timenow) { DNSQuestion *q; + uDNS_GlobalInfo *u = &m->uDNS_info; + LLQ_Info *llq; + mDNSs32 sendtime; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; 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(); + uDNS_QuestionInfo *info; - u->nextevent = timenow + 0x78000000; - if (!server) { debugf("uDNS_Execute - no DNS server"); return; } - - for (q = u->ActiveQueries; q; q = q->next) + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) { - llq = q->uDNS_info.llq; - if (q->LongLived && llq->state != LLQ_Poll) + q = u->CurrentQuery; + info = &q->uDNS_info; + llq = info->llq; + + if (!info->internal && ((!q->LongLived && !info->Answered) || (llq && llq->state < LLQ_Established)) && + info->RestartTime + RESTART_GOODBYE_DELAY - timenow < 0) { - if (llq->state >= LLQ_InitialRequest && llq->state <= LLQ_Suspended && llq->retry <= timenow) + // if we've been spinning on restart setup, and we have known answers, give goodbyes (they may be re-added later) + while (info->knownAnswers) + { + CacheRecord *cr = info->knownAnswers; + info->knownAnswers = info->knownAnswers->next; + + m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback + q->QuestionCallback(m, q, &cr->resrec, mDNSfalse); + m->mDNS_reentrancy--; // Decrement to block mDNS API calls again + ufree(cr); + if (q != u->CurrentQuery) { debugf("CheckQueries - question removed via callback."); break; } + } + } + if (q != u->CurrentQuery) continue; + + if (q->LongLived && llq->state != LLQ_Poll) + { + if (llq->state >= LLQ_InitialRequest && llq->state <= LLQ_Established) { - - // 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); } + if (llq->retry - timenow < 0) + { + // 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, mDNSfalse); + else if (llq->state == LLQ_SecondaryRequest) + sendChallengeResponse(m, q, mDNSNULL); + else if (llq->state == LLQ_Retry) + { llq->ntries = 0; startLLQHandshake(m, llq, mDNSfalse); } + } + else if (llq->retry - nextevent < 0) nextevent = llq->retry; } } else { sendtime = q->LastQTime + q->ThisQInterval; - if (sendtime <= timenow) + if (sendtime - timenow < 0) { - err = constructQueryMsg(&msg, &end, q); - if (err) + mDNSAddr server; + if (GetServerForName(&m->uDNS_info, &q->qname, &server)) { - LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %s", - q->qname.c); - continue; + err = constructQueryMsg(&msg, &end, q); + if (err) LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %##s", q->qname.c); + else + { + err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &server, UnicastDNSPort, -1, mDNSNULL); + if (err) { debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); } // surpress syslog messages if we have no network + q->LastQTime = timenow; + if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = q->ThisQInterval * 2; + } } - 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; + else if (sendtime - nextevent < 0) nextevent = sendtime; } + u->CurrentQuery = u->CurrentQuery->next; } + return nextevent; + } +mDNSlocal mDNSs32 CheckRecordRegistrations(mDNS *m, mDNSs32 timenow) + { + AuthRecord *rr; + uDNS_RegInfo *rInfo; + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + //!!!KRS list should be pre-sorted by expiration for (rr = u->RecordRegistrations; rr; rr = rr->next) { rInfo = &rr->uDNS_info; + if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_UpdatePending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh) + { + if (rr->LastAPTime + rr->ThisAPInterval - timenow < 0) + { +#if MDNS_DEBUGMSGS + char *op = "(unknown operation)"; + if (rInfo->state == regState_Pending) op = "registration"; + else if (rInfo->state == regState_DeregPending) op = "deregistration"; + else if (rInfo->state == regState_Refresh) op = "refresh"; + debugf("Retransmit record %s %##s", op, rr->resrec.name.c); +#endif + //LogMsg("Retransmit record %##s", rr->resrec.name.c); + if (rInfo->state == regState_DeregPending) SendRecordDeregistration(m, rr); + else if (rInfo->state == regState_UpdatePending) SendRecordUpdate(m, rr, rInfo); + else sendRecordRegistration(m, rr); + } + if (rr->LastAPTime + rr->ThisAPInterval - nextevent < 0) nextevent = rr->LastAPTime + rr->ThisAPInterval; + } if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0) { - if (rInfo->expire < timenow) + if (rInfo->expire - timenow < 0) { - debugf("refreshing record %s", rr->resrec.name.c); + 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; - } + if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire; + } } - //!!!KRS list should be pre-sorted by expiration - for (srs = u->ServiceRegistrations; srs; srs = srs->next) + return nextevent; + } + +mDNSlocal mDNSs32 CheckServiceRegistrations(mDNS *m, mDNSs32 timenow) + { + ServiceRecordSet *s = m->uDNS_info.ServiceRegistrations; + uDNS_RegInfo *rInfo; + mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + // Note: ServiceRegistrations list is in the order they were created; important for in-order event delivery + while (s) { + ServiceRecordSet *srs = s; + // NOTE: Must advance s here -- SendServiceDeregistration may delete the object we're looking at, + // and then if we tried to do srs = srs->next at the end we'd be referencing a dead object + s = s->next; + rInfo = &srs->uDNS_info; + if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh) + { + if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - timenow < 0) + { +#if MDNS_DEBUGMSGS + char *op = "unknown"; + if (rInfo->state == regState_Pending) op = "registration"; + else if (rInfo->state == regState_DeregPending) op = "deregistration"; + else if (rInfo->state == regState_Refresh) op = "refresh"; + debugf("Retransmit service %s %##s", op, srs->RR_SRV.resrec.name.c); +#endif + if (rInfo->state == regState_DeregPending) { SendServiceDeregistration(m, srs); continue; } + else SendServiceRegistration (m, srs); + } + if (nextevent - srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval > 0) + nextevent = srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval; + } + if (rInfo->lease && rInfo->state == regState_Registered && rInfo->expire > 0) { - if (rInfo->expire < timenow) + if (rInfo->expire - timenow < 0) { - debugf("refreshing service %s", srs->RR_SRV.resrec.name.c); + 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; - } + if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire; + } + } + return nextevent; + } + +mDNSexport void uDNS_Execute(mDNS *const m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + mDNSs32 nexte, timenow = mDNSPlatformTimeNow(m); + + u->nextevent = timenow + MIN_UCAST_PERIODIC_EXEC; + + nexte = CheckNATMappings(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + nexte = CheckQueries(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + nexte = CheckRecordRegistrations(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + nexte = CheckServiceRegistrations(m, timenow); + if (nexte - u->nextevent < 0) u->nextevent = nexte; + + } + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Startup, Shutdown, and Sleep +#endif + +// DeregisterActive causes active LLQs to be removed from the server, e.g. before sleep. Pass false +// following a location change, as the server will reject deletions from a source address different +// from the address on which the LLQ was created. + +mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive) + { + DNSQuestion *q; + LLQ_Info *llq; + for (q = m->uDNS_info.ActiveQueries; q; q = q->next) + { + llq = q->uDNS_info.llq; + if (q->LongLived && llq) + { + if (llq->state == LLQ_GetZoneInfo) + { + debugf("Marking %##s suspend-deferred", q->qname.c); + llq->state = LLQ_SuspendDeferred; // suspend once we're done getting zone info + } + else if (llq->state < LLQ_Suspended) + { + if (DeregisterActive && (llq->state == LLQ_Established || llq->state == LLQ_Refresh)) + { debugf("Deleting LLQ %##s", q->qname.c); sendLLQRefresh(m, q, 0); } + debugf("Marking %##s suspended", q->qname.c); + llq->state = LLQ_Suspended; + ubzero(llq->id, 8); + } + else if (llq->state == LLQ_Poll) { debugf("Marking %##s suspended-poll", q->qname.c); llq->state = LLQ_SuspendedPoll; } + if (llq->NATMap) llq->NATMap = mDNSfalse; // may not need nat mapping if we restart with new route + } + } + CheckForUnreferencedLLQMapping(m); + } + +mDNSlocal void RestartQueries(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + DNSQuestion *q; + LLQ_Info *llqInfo; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + + u->CurrentQuery = u->ActiveQueries; + while (u->CurrentQuery) + { + q = u->CurrentQuery; + u->CurrentQuery = u->CurrentQuery->next; + llqInfo = q->uDNS_info.llq; + q->uDNS_info.RestartTime = timenow; + q->uDNS_info.Answered = mDNSfalse; + if (q->LongLived) + { + if (!llqInfo) { LogMsg("Error: RestartQueries - %##s long-lived with NULL info", q->qname.c); continue; } + if (llqInfo->state == LLQ_Suspended || llqInfo->state == LLQ_NatMapWait) + { + llqInfo->ntries = -1; + llqInfo->deriveRemovesOnResume = mDNStrue; + startLLQHandshake(m, llqInfo, mDNStrue); // we set defer to true since several events that may generate restarts often arrive in rapid succession, and this cuts unnecessary packets + } + else if (llqInfo->state == LLQ_SuspendDeferred) + llqInfo->state = LLQ_GetZoneInfo; // we never finished getting zone data - proceed as usual + else if (llqInfo->state == LLQ_SuspendedPoll) + { + // if we were polling, we may have had bad zone data due to firewall, etc. - refetch + llqInfo->ntries = 0; + llqInfo->deriveRemovesOnResume = mDNStrue; + llqInfo->state = LLQ_GetZoneInfo; + startGetZoneData(&q->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, llqInfo); + } + } + else { q->LastQTime = timenow; q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; } // trigger poll in 1 second (to reduce packet rate when restarts come in rapid succession) + } + } + +mDNSexport void mDNS_UpdateLLQs(mDNS *m) + { + uDNS_GlobalInfo *u = &m->uDNS_info; + + mDNS_Lock(m); + if (u->LLQNatInfo) + { + NATTraversalInfo *nat = u->LLQNatInfo; + u->LLQNatInfo = mDNSNULL; + DeleteNATPortMapping(m, nat, mDNSNULL); + } + SuspendLLQs(m, mDNStrue); + RestartQueries(m); + mDNS_Unlock(m); + } + +// simplest sleep logic - rather than having sleep states that must be dealt with explicitly in all parts of +// the code, we simply send a deregistration, and put the service in Refresh state, with a timeout far enough +// in the future that we'll sleep (or the sleep will be cancelled) before it is retransmitted. Then to wake, +// we just move up the timers. + + + +mDNSlocal void SleepRecordRegistrations(mDNS *m) + { + DNSMessage msg; + AuthRecord *rr = m->uDNS_info.RecordRegistrations; + mDNSs32 timenow = mDNSPlatformTimeNow(m); + + while (rr) + { + if (rr->uDNS_info.state == regState_Registered || + rr->uDNS_info.state == regState_Refresh) + { + mDNSu8 *ptr = msg.data, *end = (mDNSu8 *)&msg + sizeof(DNSMessage); + InitializeDNSMessage(&msg.h, newMessageID(&m->uDNS_info), UpdateReqFlags); + + // construct deletion update + ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put zone"); return; } + ptr = putDeletionRecord(&msg, ptr, &rr->resrec); + if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put deletion record"); return; } + + mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rr->uDNS_info.ns, rr->uDNS_info.port, -1, GetAuthInfoForZone(&m->uDNS_info, &rr->uDNS_info.zone)); + rr->uDNS_info.state = regState_Refresh; + rr->LastAPTime = timenow; + rr->ThisAPInterval = 300 * mDNSPlatformOneSecond; + } + rr = rr->next; + } + } + +mDNSlocal void WakeRecordRegistrations(mDNS *m) + { + mDNSs32 timenow = mDNSPlatformTimeNow(m); + AuthRecord *rr = m->uDNS_info.RecordRegistrations; + + while (rr) + { + if (rr->uDNS_info.state == regState_Refresh) + { + // trigger slightly delayed refresh (we usually get this message before kernel is ready to send packets) + rr->LastAPTime = timenow; + rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL; + } + rr = rr->next; + } + } + +mDNSlocal void SleepServiceRegistrations(mDNS *m) + { + mDNSs32 timenow = mDNSPlatformTimeNow(m); + ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations; + while(srs) + { + if (srs->uDNS_info.state == regState_Registered || + srs->uDNS_info.state == regState_Refresh) + { + mDNSOpaque16 origid = srs->uDNS_info.id; + srs->uDNS_info.state = regState_DeregPending; // state expected by SendDereg() + SendServiceDeregistration(m, srs); + srs->uDNS_info.id = origid; + srs->uDNS_info.state = regState_Refresh; + srs->RR_SRV.LastAPTime = timenow; + srs->RR_SRV.ThisAPInterval = 300 * mDNSPlatformOneSecond; + } + srs = srs->next; + } + } + +mDNSlocal void WakeServiceRegistrations(mDNS *m) + { + mDNSs32 timenow = mDNSPlatformTimeNow(m); + ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations; + while(srs) + { + if (srs->uDNS_info.state == regState_Refresh) + { + // trigger slightly delayed refresh (we usually get this message before kernel is ready to send packets) + srs->RR_SRV.LastAPTime = timenow; + srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL; + } + srs = srs->next; } } mDNSexport void uDNS_Init(mDNS *const m) { mDNSPlatformMemZero(&m->uDNS_info, sizeof(uDNS_GlobalInfo)); - m->uDNS_info.nextevent = mDNSPlatformTimeNow() + 0x78000000; + m->uDNS_info.nextevent = m->timenow_last + 0x78000000; + } + +mDNSexport void uDNS_Sleep(mDNS *m) + { + SuspendLLQs(m, mDNStrue); + SleepServiceRegistrations(m); + SleepRecordRegistrations(m); + } + +mDNSexport void uDNS_Wake(mDNS *m) + { + RestartQueries(m); + WakeServiceRegistrations(m); + WakeRecordRegistrations(m); } diff --git a/mDNSCore/uDNS.h b/mDNSCore/uDNS.h index 52fc878..20edb2d 100755 --- a/mDNSCore/uDNS.h +++ b/mDNSCore/uDNS.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,58 @@ Change History (most recent first): $Log: uDNS.h,v $ +Revision 1.27 2004/11/23 04:06:50 cheshire +Get rid of floating point constant -- in a small embedded device, bringing in all +the floating point libraries just to halve an integer value is a bit too heavyweight. + +Revision 1.26 2004/11/22 17:49:15 ksekar +Changed INIT_REFRESH from fraction to decimal + +Revision 1.25 2004/11/22 17:16:20 ksekar + Unicast services don't disappear when you disable all networking + +Revision 1.24 2004/11/19 04:24:08 ksekar + Security: Enforce a "window" on one-shot wide-area queries + +Revision 1.23 2004/11/18 18:04:21 ksekar +Add INIT_REFRESH constant + +Revision 1.22 2004/11/15 20:09:24 ksekar + Wide Area support for Add/Remove record + +Revision 1.21 2004/11/11 20:14:55 ksekar + Wide-Area registrations not deregistered on sleep + +Revision 1.20 2004/10/16 00:16:59 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.19 2004/09/17 01:08:49 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.18 2004/09/03 19:23:05 ksekar +: Need retransmission mechanism for wide-area service registrations + +Revision 1.17 2004/09/01 03:59:29 ksekar +: Conditionally compile out uDNS code on Windows + +Revision 1.16 2004/08/25 00:37:27 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.15 2004/07/30 17:40:06 ksekar +: TXT Record updates not available for wide-area services + +Revision 1.14 2004/07/29 19:27:15 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.13 2004/07/29 02:03:35 ksekar +Delete unused #define and structure field + +Revision 1.12 2004/07/26 22:49:30 ksekar +: Feature #9516: Need support for NATPMP in client + Revision 1.11 2004/06/17 01:13:11 ksekar : polling interval too short @@ -65,49 +115,58 @@ Revision 1.1 2003/12/13 03:05:27 ksekar #ifndef __UDNS_H_ #define __UDNS_H_ -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "DNSCommon.h" #ifdef __cplusplus extern "C" { #endif -#define INIT_UCAST_POLL_INTERVAL (15 * mDNSPlatformOneSecond) +#define RESTART_GOODBYE_DELAY (2 * mDNSPlatformOneSecond) // delay after restarting LLQ before nuking previous known answers (avoids flutter if we restart before we have networking up) +#define MIN_UCAST_PERIODIC_EXEC (5 * mDNSPlatformOneSecond) +#define INIT_UCAST_POLL_INTERVAL mDNSPlatformOneSecond // this interval is used after send failures on network transitions + // which typically heal quickly, so we start agressively and exponentially back off #define MAX_UCAST_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) -#define NO_GOODBYE // will we receive goodbye packets from the server? +#define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond) // require server responses within one minute of request #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 mDNSBool uDNS_IsActiveQuery(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 void uDNS_Init(mDNS *m); +extern void uDNS_Sleep(mDNS *m); +extern void uDNS_Wake(mDNS *m); +#define uDNS_Close uDNS_Sleep + +// uDNS_UpdateRecord +// following fields must be set, and the update validated, upon entry. +// rr->NewRData +// rr->newrdlength +// rr->UpdateCallback + +extern mStatus uDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra); +extern mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr); 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); + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, + const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); +extern void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len); + // returns time of next scheduled event extern void uDNS_Execute(mDNS *const m); -extern void uDNS_Init(mDNS *const m); #ifdef __cplusplus } diff --git a/mDNSMacOS9/CarbonResource.r b/mDNSMacOS9/CarbonResource.r index 853a4ac..105a398 100644 --- a/mDNSMacOS9/CarbonResource.r +++ b/mDNSMacOS9/CarbonResource.r @@ -3,8 +3,6 @@ * * @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 9ff78dc..cf03862 100644 --- a/mDNSMacOS9/Mac OS Test Responder.c +++ b/mDNSMacOS9/Mac OS Test Responder.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,16 @@ Change History (most recent first): $Log: Mac\040OS\040Test\040Responder.c,v $ +Revision 1.23 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.22 2004/08/13 23:25:01 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + 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. @@ -53,7 +61,7 @@ Update to APSL 2.0 #include // For WaitNextEvent() #include // For SIOUXHandleOneEvent() -#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform @@ -164,7 +172,7 @@ mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) char buffer[MAX_ESCAPED_DOMAIN_NAME]; mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; - ConvertDomainNameToCString(&m->hostname, buffer); + ConvertDomainNameToCString(&m->MulticastHostname, buffer); printf("Name %s\n", buffer); printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); diff --git a/mDNSMacOS9/Mac OS Test Searcher.c b/mDNSMacOS9/Mac OS Test Searcher.c index 6b5d90d..f2fb9ce 100644 --- a/mDNSMacOS9/Mac OS Test Searcher.c +++ b/mDNSMacOS9/Mac OS Test Searcher.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,20 @@ Change History (most recent first): $Log: Mac\040OS\040Test\040Searcher.c,v $ +Revision 1.20 2004/10/19 21:33:18 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.19 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.18 2004/09/16 21:59:16 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + Revision 1.17 2004/06/10 04:37:27 cheshire Add new parameter in mDNS_GetDomains() @@ -51,7 +63,7 @@ Update to APSL 2.0 #include // For WaitNextEvent() #include // For SIOUXHandleOneEvent() -#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform typedef struct @@ -155,7 +167,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 = zerov4Addr; info->i.port = zeroIPPort; info->add = AddRecord; info->dom = mDNSfalse; @@ -188,7 +200,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 = zerov4Addr; info->i.port = zeroIPPort; info->add = AddRecord; info->dom = mDNStrue; @@ -253,7 +265,7 @@ int main() printf("\nSending mDNS service lookup queries and waiting for responses...\n\n"); MakeDomainNameFromDNSNameString(&srvtype, "_http._tcp."); MakeDomainNameFromDNSNameString(&srvdom, "local."); - err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, FoundInstance, &services); + err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, mDNSfalse, FoundInstance, &services); if (err) break; err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, NULL, mDNSInterface_Any, FoundDomain, &services); if (err) break; diff --git a/mDNSMacOS9/Responder.c b/mDNSMacOS9/Responder.c index d1612f8..2e7135e 100644 --- a/mDNSMacOS9/Responder.c +++ b/mDNSMacOS9/Responder.c @@ -3,8 +3,6 @@ * * @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/Searcher.c b/mDNSMacOS9/Searcher.c index 4d0d494..a0a562f 100644 --- a/mDNSMacOS9/Searcher.c +++ b/mDNSMacOS9/Searcher.c @@ -3,8 +3,6 @@ * * @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/SubTypeTester.c b/mDNSMacOS9/SubTypeTester.c new file mode 100644 index 0000000..7c153a3 --- /dev/null +++ b/mDNSMacOS9/SubTypeTester.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + + Change History (most recent first): + +$Log: SubTypeTester.c,v $ +Revision 1.5 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.4 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.3 2004/08/13 23:25:01 cheshire +Now that we do both uDNS and mDNS, global replace "m->hostname" with +"m->MulticastHostname" for clarity + +Revision 1.2 2004/08/04 22:11:30 cheshire + Current method of doing subtypes causes name collisions +Change to use "._sub." instead of ".s." to mark subtypes. + +Revision 1.1 2004/06/11 00:03:28 cheshire +Add code for testing avail/busy subtypes + + + */ + +#include // For printf() +#include // For strlen() etc. + +#include // For WaitNextEvent() +#include // For SIOUXHandleOneEvent() + +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above + +#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform + +// These don't have to be globals, but their memory does need to remain valid for as +// long as the search is going on. They are declared as globals here for simplicity. +static mDNS m; +static mDNS_PlatformSupport p; +static ServiceRecordSet p1, p2; +static AuthRecord availRec1, availRec2; +static Boolean availRec2Active; + +// This sample code just calls mDNS_RenameAndReregisterService to automatically pick a new +// unique name for the service. For a device such as a printer, this may be appropriate. +// For a device with a user interface, and a screen, and a keyboard, the appropriate +// response may be to prompt the user and ask them to choose a new name for the service. +mDNSlocal void Callback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) + { + switch (result) + { + case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name.c); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name.c); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name.c); break; + default: debugf("Callback: %##s Unknown Result %d", sr->RR_SRV.resrec.name.c, result); break; + } + + if (result == mStatus_NameConflict) mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + } + +// RegisterService() is a simple wrapper function which takes C string +// parameters, converts them to domainname parameters, and calls mDNS_RegisterService() +mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, + UInt16 PortAsNumber, const char txtinfo[], + const domainlabel *const n, const char type[], const char domain[]) + { + domainname t; + domainname d; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + UInt8 txtbuffer[512]; + + MakeDomainNameFromDNSNameString(&t, type); + MakeDomainNameFromDNSNameString(&d, domain); + + if (txtinfo) + { + strncpy((char*)txtbuffer+1, txtinfo, sizeof(txtbuffer)-1); + txtbuffer[0] = (UInt8)strlen(txtinfo); + } + else + txtbuffer[0] = 0; + + mDNS_RegisterService(m, recordset, + n, &t, &d, // Name, type, domain + mDNSNULL, mDNSOpaque16fromIntVal(PortAsNumber), + txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length + mDNSNULL, 0, // Subtypes (none) + mDNSInterface_Any, // Interface ID + Callback, mDNSNULL); // Callback and context + + ConvertDomainNameToCString(&recordset->RR_SRV.resrec.name, buffer); + printf("Made Service Records for %s\n", buffer); + } + +// RegisterFakeServiceForTesting() simulates the effect of services being registered on +// dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. +mDNSlocal void RegisterFakeServiceForTesting(mDNS *m, ServiceRecordSet *recordset, const char txtinfo[], + const char name[], const char type[], const char domain[]) + { + static UInt16 NextPort = 0xF000; + domainlabel n; + MakeDomainLabelFromLiteralString(&n, name); + RegisterService(m, recordset, NextPort++, txtinfo, &n, type, domain); + } + +// Done once on startup, and then again every time our address changes +mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) + { + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; + + ConvertDomainNameToCString(&m->MulticastHostname, buffer); + printf("Name %s\n", buffer); + printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); + + printf("\n"); + printf("Registering Service Records\n"); + // Create example printer discovery records + //static ServiceRecordSet p1, p2; + + RegisterFakeServiceForTesting(m, &p1, "", "One", "_raop._tcp.", "local."); + RegisterFakeServiceForTesting(m, &p2, "", "Two", "_raop._tcp.", "local."); + + return(kOTNoError); + } + +mDNSlocal void AvailCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + Boolean *b = (Boolean *)rr->RecordContext; + (void)m; // Unused + // Signal that our record is now free for re-use + if (result == mStatus_MemFree) *b = false; + } + +mDNSlocal OSStatus mDNSResponderSetAvail(mDNS *m, AuthRecord *rr, ServiceRecordSet *sr) + { + // 1. Initialize required fields of AuthRecord + // 2. Set name of subtype PTR record to our special subtype name denoting "available" instances + // 3. Set target of subtype PTR record to point to our SRV record (exactly the same as the main service PTR record) + // 4. And register it + mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 2*3600, kDNSRecordTypeShared, AvailCallback, &availRec2Active); + MakeDomainNameFromDNSNameString(&rr->resrec.name, "a._sub._raop._tcp.local."); + AssignDomainName(rr->resrec.rdata->u.name, sr->RR_SRV.resrec.name); + return(mDNS_Register(m, rr)); + } + +// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS +mDNSlocal Boolean YieldSomeTime(UInt32 milliseconds) + { + extern Boolean SIOUXQuitting; + EventRecord e; + WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); + SIOUXHandleOneEvent(&e); + return(SIOUXQuitting); + } + +int main() + { + mStatus err; + Boolean DoneSetup = false; + mDNSs32 nextAvail, nextBusy; + + 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"); + + err = InitOpenTransport(); + if (err) { debugf("InitOpenTransport failed %d", err); return(err); } + + err = mDNS_Init(&m, &p, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, + mDNS_Init_AdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); + if (err) return(err); + + while (!YieldSomeTime(35)) + { +#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; + printf("\nListening for mDNS queries...\n"); + mDNSResponderTestSetup(&m); + mDNSResponderSetAvail(&m, &availRec1, &p1); + availRec2Active = false; + nextAvail = mDNS_TimeNow(&m) + mDNSPlatformOneSecond * 10; + nextBusy = mDNS_TimeNow(&m) + mDNSPlatformOneSecond * 15; + } + + if (DoneSetup) + { + // We check availRec2.RecordType because we don't want to re-register this record + // if the previous mDNS_Deregister() has not yet completed + if (mDNS_TimeNow(&m) - nextAvail > 0 && !availRec2Active) + { + printf("Setting Two now available\n"); + availRec2Active = true; + mDNSResponderSetAvail(&m, &availRec2, &p2); + nextAvail = nextBusy + mDNSPlatformOneSecond * 10; + } + else if (mDNS_TimeNow(&m) - nextBusy > 0) + { + printf("Setting Two now busy\n"); + mDNS_Deregister(&m, &availRec2); + nextBusy = nextAvail + mDNSPlatformOneSecond * 5; + } + } + } + + if (p1.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &p1); + if (p2.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &p2); + if (availRec1.resrec.RecordType) mDNS_Deregister(&m, &availRec1); + if (availRec2Active) mDNS_Deregister(&m, &availRec2); + + mDNS_Close(&m); + + return(0); + } diff --git a/mDNSMacOS9/mDNS.mcp b/mDNSMacOS9/mDNS.mcp index fdf6c0ee07628c8364217bfda06a1fbd5bcedd03..24cdedd73790ef4fad01371fb5629adc20362d12 100644 GIT binary patch delta 6153 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 D#Sh6Y delta 1138 zcmZXUe`r-@7{{OY-1BSa$NjbG?(S8(?q1j2!aHX%>E65DwKAORc5y;dFoMMxwlSR( zC9+vCt|8eyzQfo2C!@qbV%8yF&`~NOVJ@RoWL6ZkmVr40iP-G+ocV@94}8z(JkNRF z^SSOUe-G_GyniT08$VBU8vm!fX*!#s`+@CpN#510iZ};0)kJx`$Z$4 z-6yOSehgm`2dGW~L@-2R(EDun>XDa5(2<~d*9oL&I28m0t;y>@>?oJNKJ7jDuZ8|~ z=*yus?p%I~xW|33`RpLZ;|5+(Nw-;Z40<4wam%575xT!2N55-GIh_`7`&3dT8t+cx z&>7-}QVX`?$58?H^Nf;XA1s(TdNBKih3j}6l}2UgwsTcs4WMQHhOVcOx%;I1S|{k$ z%sQ5sQ66#%57VaMCGN$x3oklPr`@lPUW%~9N7A=ASCkD^8@jvybi0zQ0El_Rkx{`K z!8$>M&V3#aNuH;RNo{~Gy6SARH{2F!je-e}7FJR)Ku6EM*@cPqBB4oel}FrbkKjfT z4+sVYLxNR;m2~sDrpo{CAK2?}X{|JFDl}WI(N`lTyY`LTNLKPM3uKSPCT4v)Plcp; z;+8Jx^)go3&$s*~Z;-X?mnQ*QkMZrlsPlxsUzT_3Iv)78a{1s55sVqi0gH{kp#}Lp zlhi%I_LNkOX4-0LZ(F=LkS8b!<>Lc+`S&r_lqV+ETIYiu{^6}W&}ei#=M{NTX!kVu3qAuJysX6fPmFr>_5wC&+^fdI)Bw$v%wxMsY%vT zQfrE5`ZSb|s^Dk(j6>^`q}R{1bUV&-cgYK0XQ}+{K8r~F@Syx|f@e?pma6Rb4W+g? zF=g|?bz ztxC4(j!Z|2c^en-|wz83R3`6nc>LeaFoSJ2DxX*GZy0NG88UV|Cv GPyYi6B`rt* diff --git a/mDNSMacOS9/mDNSLibrary.c b/mDNSMacOS9/mDNSLibrary.c index a8ac4a9..13dc10a 100644 --- a/mDNSMacOS9/mDNSLibrary.c +++ b/mDNSMacOS9/mDNSLibrary.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,12 @@ Change History (most recent first): $Log: mDNSLibrary.c,v $ +Revision 1.2 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + 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. @@ -33,7 +37,7 @@ 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 "mDNSEmbeddedAPI.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; diff --git a/mDNSMacOS9/mDNSLibraryLoader.c b/mDNSMacOS9/mDNSLibraryLoader.c index c708eac..d671d77 100644 --- a/mDNSMacOS9/mDNSLibraryLoader.c +++ b/mDNSMacOS9/mDNSLibraryLoader.c @@ -3,8 +3,6 @@ * * @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/mDNSLibraryResources.r b/mDNSMacOS9/mDNSLibraryResources.r index c77311c..b699ed5 100644 --- a/mDNSMacOS9/mDNSLibraryResources.r +++ b/mDNSMacOS9/mDNSLibraryResources.r @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,45 @@ Change History (most recent first): $Log: mDNSLibraryResources.r,v $ +Revision 1.17 2004/12/13 21:54:30 cheshire +mDNSResponder-87 + +Revision 1.16 2004/12/06 19:09:47 cheshire +mDNSResponder-86 + +Revision 1.15 2004/11/29 23:36:15 cheshire +mDNSResponder-85 + +Revision 1.14 2004/11/23 03:46:31 cheshire +mDNSResponder-84 + +Revision 1.13 2004/10/26 20:30:30 cheshire +mDNSResponder-82 + +Revision 1.12 2004/10/14 23:38:04 cheshire +mDNSResponder-80 + +Revision 1.11 2004/10/07 21:49:15 cheshire +mDNSResponder-79 + +Revision 1.10 2004/09/25 02:52:09 cheshire +mDNSResponder-78 + +Revision 1.9 2004/09/22 22:52:07 cheshire +mDNSResponder-77 + +Revision 1.8 2004/09/21 00:20:52 cheshire +mDNSResponder-76 + +Revision 1.7 2004/09/15 19:45:06 cheshire +mDNSResponder-75 + +Revision 1.6 2004/09/09 23:32:35 cheshire +mDNSResponder-74 + +Revision 1.5 2004/08/10 21:51:45 cheshire +mDNSResponder-69 + Revision 1.4 2004/06/10 20:28:16 cheshire Update version string to 1.0a66 @@ -55,15 +92,15 @@ like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code resource 'vers' (1, purgeable) { - 0x01, 0x00, alpha, 66, verUS, - "1.0a66", - "Multicast DNS & DNS Service Discovery 1.0a66" + 0x01, 0x00, alpha, 87, verUS, + "1.0a87", + "Multicast DNS & DNS Service Discovery 1.0a87" }; resource 'vers' (2, purgeable) { - 0x01, 0x00, alpha, 66, verUS, - "1.0a66", + 0x01, 0x00, alpha, 87, verUS, + "1.0a87", "developer.apple.com/darwin/projects/rendezvous/" }; diff --git a/mDNSMacOS9/mDNSMacOS9.c b/mDNSMacOS9/mDNSMacOS9.c index 6563112..d38facc 100644 --- a/mDNSMacOS9/mDNSMacOS9.c +++ b/mDNSMacOS9/mDNSMacOS9.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,49 @@ Change History (most recent first): $Log: mDNSMacOS9.c,v $ +Revision 1.41 2004/10/16 00:17:00 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.40 2004/09/27 23:56:27 cheshire +Fix infinite loop where mDNSPlatformUnlock() called mDNS_TimeNow(), +and then mDNS_TimeNow() called mDNSPlatformUnlock() + +Revision 1.39 2004/09/21 21:02:54 cheshire +Set up ifname before calling mDNS_RegisterInterface() + +Revision 1.38 2004/09/17 01:08:50 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.37 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.36 2004/09/16 21:59:16 cheshire +For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr + +Revision 1.35 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.34 2004/09/14 23:42:36 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.33 2004/09/14 23:16:31 cheshire +Fix compile error: mDNS_SetFQDNs has been renamed to mDNS_SetFQDN + +Revision 1.32 2004/09/14 21:03:16 cheshire +Fix spacing + +Revision 1.31 2004/08/14 03:22:42 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.30 2004/07/29 19:26:03 ksekar +Plaform-level changes for NATPMP support + Revision 1.29 2004/05/26 20:53:16 cheshire Remove unncecessary "return( -1 );" at the end of mDNSPlatformUTC() @@ -55,10 +96,10 @@ 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. +Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. Revision 1.19 2003/08/18 23:09:20 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() + mDNSResponder divide by zero in mDNSPlatformRawTime() Revision 1.18 2003/08/12 19:56:24 cheshire Update to APSL 2.0 @@ -72,7 +113,7 @@ Update to APSL 2.0 #include // For smSystemScript #include // For ConvertFromPStringToUnicode() -#include "mDNSClientAPI.h" // Defines the interface provided to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above #include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform @@ -165,7 +206,7 @@ mDNSexport void LogMsg(const char *format, ...) } #endif -mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, +mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, 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 @@ -223,11 +264,11 @@ mDNSlocal OSStatus readpacket(mDNS *m) senderport.NotAnInteger = sender.fPort; destaddr.type = mDNSAddrType_IPv4; - destaddr.ip.v4 = zeroIPAddr; + destaddr.ip.v4 = zerov4Addr; #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; + destaddr.ip.v4 = AllDNSLinkGroupv4; #endif if (recvdata.opt.len) @@ -246,8 +287,7 @@ mDNSlocal OSStatus readpacket(mDNS *m) if (flags & T_MORE) debugf("ERROR: OTRcvUData() buffer too small (T_MORE set)"); else if (recvdata.addr.len < sizeof(InetAddress)) debugf("ERROR: recvdata.addr.len (%d) too short", recvdata.addr.len); - else if (recvdata.udata.len < sizeof(DNSMessageHeader)) debugf("ERROR: recvdata.udata.len (%d) too short", recvdata.udata.len); - else mDNSCoreReceive(m, &packet, recvdata.udata.buf + recvdata.udata.len, &senderaddr, senderport, &destaddr, MulticastDNSPort, interface, 255); + else mDNSCoreReceive(m, &packet, recvdata.udata.buf + recvdata.udata.len, &senderaddr, senderport, &destaddr, MulticastDNSPort, interface); return(err); } @@ -324,11 +364,14 @@ mDNSlocal pascal void mDNSNotifier(void *contextPtr, OTEventCode code, OTResult 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.McastTxRx = mDNStrue; + 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.mask.type = mDNSAddrType_IPv4; + m->p->interface.mask.ip.v4.NotAnInteger = interfaceinfo.fMask; + m->p->interface.ifname[0] = 0; + m->p->interface.Advertise = m->AdvertiseLocalAddresses; + m->p->interface.McastTxRx = mDNStrue; } case T_OPTMGMTCOMPLETE: @@ -572,7 +615,7 @@ mDNSexport mStatus mDNSPlatformInit(mDNS *const m) ConvertUTF8PstringToRFC1034HostLabel(m->nicelabel.c, &m->hostlabel); if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh"); - mDNS_GenerateFQDN(m); + mDNS_SetFQDN(m); // When it's finished mDNSOpenEndpoint asynchronously calls mDNSinitComplete() and then mDNS_RegisterInterface() CallmDNSNotifierUPP = NewOTNotifyUPP(CallmDNSNotifier); @@ -655,7 +698,7 @@ mDNSlocal void ScheduleNextTimerCallback(const mDNS *const m) { if (m->mDNSPlatformStatus == mStatus_NoError) { - SInt32 interval = m->NextScheduledEvent - mDNSPlatformTimeNow(); + SInt32 interval = m->NextScheduledEvent - (mDNSPlatformRawTime() + m->timenow_adjust); if (interval < 1) interval = 1; else if (interval > 0x70000000 / 1000) interval = 0x70000000 / mDNSPlatformOneSecond; else interval = (interval * 1000 + mDNSPlatformOneSecond-1)/ mDNSPlatformOneSecond; @@ -680,9 +723,10 @@ mDNSexport void mDNSPlatformMemCopy(const void *src, void *dst, UInt32 mDNSexport mDNSBool mDNSPlatformMemSame(const void *src, const void *dst, UInt32 len) { return(OTMemcmp(dst, src, len)); } mDNSexport void mDNSPlatformMemZero( void *dst, UInt32 len) { OTMemzero(dst, len); } mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(OTAllocMem(len)); } -mDNSexport void mDNSPlatformMemFree (void *mem) { OTFreeMem(mem); } -mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) { *timenow = mDNSPlatformTimeNow(); return(mStatus_NoError); } -mDNSexport SInt32 mDNSPlatformTimeNow() { return((SInt32)TickCount()); } +mDNSexport void mDNSPlatformMemFree(void *mem) { OTFreeMem(mem); } +mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) { return(TickCount()); } +mDNSexport mStatus mDNSPlatformTimeInit(void) { return(mStatus_NoError); } +mDNSexport SInt32 mDNSPlatformRawTime() { return((SInt32)TickCount()); } mDNSexport SInt32 mDNSPlatformOneSecond = 60; mDNSexport mDNSs32 mDNSPlatformUTC(void) diff --git a/mDNSMacOS9/mDNSMacOS9.h b/mDNSMacOS9/mDNSMacOS9.h index 03c7bd5..c4fa310 100755 --- a/mDNSMacOS9/mDNSMacOS9.h +++ b/mDNSMacOS9/mDNSMacOS9.h @@ -3,8 +3,6 @@ * * @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/mDNSPrefix.h b/mDNSMacOS9/mDNSPrefix.h index 3d1b0aa..80adab4 100644 --- a/mDNSMacOS9/mDNSPrefix.h +++ b/mDNSMacOS9/mDNSPrefix.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: mDNSPrefix.h,v $ +Revision 1.3 2004/06/11 00:03:28 cheshire +Add code for testing avail/busy subtypes + Revision 1.2 2004/05/21 01:57:08 cheshire Add macros for malloc() and free() so that dnssd_clientlib.c can use them @@ -46,26 +47,32 @@ like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code // 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") +#if __ide_target("Standalone TestResponder") || __ide_target("Standalone TestSearcher") || __ide_target("Standalone SubTypeTester") #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 diff --git a/mDNSMacOSX/DNSServiceDiscoveryDefines.h b/mDNSMacOSX/DNSServiceDiscoveryDefines.h index bd5b11b..c2f50c1 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryDefines.h +++ b/mDNSMacOSX/DNSServiceDiscoveryDefines.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: DNSServiceDiscoveryDefines.h,v $ +Revision 1.6 2004/09/20 21:45:27 ksekar +Mach IPC cleanup + Revision 1.5 2003/08/12 19:56:25 cheshire Update to APSL 2.0 @@ -41,5 +42,6 @@ typedef char DNSCString[1024]; typedef char sockaddr_t[128]; typedef const char * record_data_t; +typedef struct { char bytes[4]; } IPPort; #endif /* __DNS_SERVICE_DISCOVERY_DEFINES_H */ diff --git a/mDNSMacOSX/DNSServiceDiscoveryReply.defs b/mDNSMacOSX/DNSServiceDiscoveryReply.defs index 6ae6004..942fb6b 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryReply.defs +++ b/mDNSMacOSX/DNSServiceDiscoveryReply.defs @@ -3,8 +3,6 @@ * * @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 a4596af..b6d9cf8 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryRequest.defs +++ b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs @@ -3,8 +3,6 @@ * * @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 +34,7 @@ import "DNSServiceDiscoveryDefines.h"; type DNSCString = c_string[*:1024]; type record_data = ^ array [] of MACH_MSG_TYPE_BYTE ctype: record_data_t; +type IPPort = struct[4] of char ctype:IPPort; simpleroutine DNSServiceBrowserCreate_rpc( server: mach_port_t; @@ -55,7 +54,7 @@ simpleroutine DNSServiceRegistrationCreate_rpc( in name: DNSCString; in regtype: DNSCString; in domain: DNSCString; - in port: int; + in port: IPPort; in txtRecord: DNSCString); diff --git a/mDNSMacOSX/LegacyNATTraversal.c b/mDNSMacOSX/LegacyNATTraversal.c new file mode 100644 index 0000000..d8f0480 --- /dev/null +++ b/mDNSMacOSX/LegacyNATTraversal.c @@ -0,0 +1,3010 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * 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@ + + Change History (most recent first): + +$Log: LegacyNATTraversal.c,v $ +Revision 1.11 2004/12/03 03:34:20 ksekar + LegacyNATTraversal.c leaks threads + +Revision 1.10 2004/12/01 02:43:49 cheshire +Update copyright message + +Revision 1.9 2004/10/27 02:25:05 cheshire + Random memory smashing bug + +Revision 1.8 2004/10/27 02:17:21 cheshire +Turn off "safe_close: ERROR" error messages -- there are too many of them + +Revision 1.7 2004/10/26 21:15:40 cheshire + Legacy NAT traversal code closes file descriptor 0 +Additional fixes: Code should set fds to -1 after closing sockets. + +Revision 1.6 2004/10/26 20:59:20 cheshire + Legacy NAT traversal code closes file descriptor 0 + +Revision 1.5 2004/10/26 01:01:35 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.4 2004/10/10 06:51:36 cheshire +Declared some strings "const" as appropriate + +Revision 1.3 2004/09/21 23:40:12 ksekar + mDNSResponder to return errors on NAT traversal failure + +Revision 1.2 2004/09/17 01:08:52 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.1 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + + +*/ + +#include "mDNSEmbeddedAPI.h" +#include "mDNSMacOSX.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "memory.h" +#include +#include + +//#include "IPAddr.h" +//#include "upnp.h" +//#include "debug.h" + +// use error codes +//#include "netaddr.h" + +// TODO: remove later and do variable length +#define MAX_SOAPMSGSIZE 65536 + +static int safe_close(int fd) + { + if (fd < 3) { /* LogMsg("safe_close: ERROR sd %d < 3", fd); */ return(-1); } + return(close(fd)); + } + +#define close safe_close + +//////////////////////////////////////////////////////////////////////// +// NetAddr Functions +//////////////////////////////////////////////////////////////////////// + +// Return codes +#define NA_E_SUCCESS (0) +#define NA_E_INTERNAL_ERROR (-1) /* somewhere something wrong */ +#define NA_E_INVALID_PARAMETER (-2) /* bad params */ +#define NA_E_OPERATION_FAILED (-3) /* can't fulfill request */ +#define NA_E_TIMEOUT (-4) /* operation timed out */ +#define NA_E_THREAD_ERROR (-5) /* some error related to threads */ +#define NA_E_PARSE_ERROR (-6) /* a parsing error occured */ +#define NA_E_NOT_READY (-7) /* this op can't proceed yet */ +#define NA_E_NOT_FOUND (-8) /* resource/prereq not found */ +#define NA_E_NOT_AVAILABLE (-9) /* service not available */ +#define NA_E_EXISTS (-10) /* can't modify existing item */ +#define NA_E_AGAIN (-11) /* something wrong - try again */ +#define NA_E_NOT_SUPPORTED (-12) /* wait until next version */ +#define NA_E_ABORT (-14) /* operation aborted */ +#define NA_E_NET (-15) /* network layer problem */ + +// Logging flags - log types (increasing degree of detail) +#define NALOG_ERROR (1UL) /* error messages */ +#define NALOG_ALERT (2UL) /* useful warning/alerts */ +#define NALOG_INFO0 (4UL) /* info - potential problem */ +#define NALOG_INFO1 (8UL) /* extra info */ +#define NALOG_DUMP (16UL) /* data dumps */ + +#define NALOG_RSRV1 (32UL) /* reserved */ +#define NALOG_RSRV2 (64UL) /* reserved */ +#define NALOG_RSRV3 (128UL) /* reserved */ + +// Logging flags - component (not used for now) +#define NALOG_UPNP (256) /* UPnP */ + +// Default Logging levels +#define NALOG_LEVEL0 (0) +#define NALOG_LEVEL1 (NALOG_UPNP | NALOG_ERROR) +#define NALOG_LEVEL2 (NALOG_LEVEL1 | NALOG_ALERT) +#define NALOG_LEVEL3 (NALOG_LEVEL2 | NALOG_INFO0) +#define NALOG_LEVEL4 (NALOG_LEVEL3 | NALOG_INFO1) +#define NALOG_LEVEL5 (NALOG_LEVEL4 | NALOG_DUMP) +#define NALOG_DEFAULT_LEVEL (NALOG_LEVEL2) + +// Default timeout values (in m-seconds (milli)) +// 50 milliseconds for function timeout +#define NA_DEFAULT_FUNCTION_TIMEOUT (50) + +//////////////////////////////////////////////////////////////////////// +// GLOBAL Defines +//////////////////////////////////////////////////////////////////////// +#define SSDP_IP "239.255.255.250" +#define SSDP_PORT 1900 +#define SSDP_TTL 4 + +#define CRLF "\r\n" +#define H_CRLF "\r\n" +// SOAP message's CRLF: +//#define S_CRLF "\r\n" +#define S_CRLF + +// standard 200 ok msg +#define HTTP200OK "HTTP/1.1 200 OK\r\n\r\n" +#define HTTP200OKLEN (sizeof(HTTP200OK) - 1) + +// maximum time to wait for an event (in microseconds) +#define MAX_EXPECTEVENTTIME (10000) + +//////////////////////////////////////////////////////////////////////// +// GLOBAL Data Types +//////////////////////////////////////////////////////////////////////// +typedef struct tagProperty { + char *pszName; + char *pszValue; + char *pszType; +} Property, *PProperty; + +typedef struct tagHTTPResponse { + char *pszStatus; + char *pszReason; + int iNumHeaders; + Property aHeaders[30]; // assume at most this many headers + char *pszBody; + + // for admin use + int fFree; + char *buf; +} HTTPResponse, *PHTTPResponse, **PPHTTPResponse; + +//////////////////////////////////////////////////////////////////////// +// GLOBAL Constants +//////////////////////////////////////////////////////////////////////// +static const char szSSDPMsgDiscoverRoot[] = + "M-SEARCH * HTTP/1.1\r\n" + "Host:239.255.255.250:1900\r\n" + "ST:upnp:rootdevice\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n"; + +static const char szSSDPMsgDiscoverIGD[] = + "M-SEARCH * HTTP/1.1\r\n" + "Host:239.255.255.250:1900\r\n" + "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n"; + +static const char szSSDPMsgDiscoverNAT[] = + "M-SEARCH * HTTP/1.1\r\n" + "Host:239.255.255.250:1900\r\n" + "ST:urn:schemas-upnp-org:service:WANIPConnection:1\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n"; + +//// Subscribe message +// 1$s: control URL +// 2$s: local's host/port ("host:port") +// 3$s: router's host/port ("host:port") +// 4$d: subscription timeout in seconds +static const char szEventMsgSubscribeFMT[] = + "SUBSCRIBE %1$s HTTP/1.1\r\n" + "NT: upnp:event\r\n" + "Callback: \r\n" + "Timeout: Second-%4$d\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n" + "Host: %3$s\r\n" + "Content-Length: 0\r\n" + "Pragma: no-cache\r\n" + "\r\n"; + +//// Unsubscribe message +// 1$s: control URL +// 2$s: SID (some uuid passed back during subscribe) +// 3$s: router's host ("host") +#if 0 +static const char szEventMsgUnsubscribeFMT[] = + "UNSUBSCRIBE %1$s HTTP/1.1\r\n" + "SID: %2$s\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n" + "Host: %3$s\r\n" + "Content-Length: 0\r\n" + "Pragma: no-cache\r\n" + "\r\n"; +#endif + +//// Generic SOAP Control:Action request messages +// 1$s: control URL +// 2$s: router's host/port ("host:port") +// 3$s: action (string) +// 4$d: content-length +static const char szSOAPMsgControlAHeaderFMT[] = + //"M-POST %1$s HTTP/1.1\r\n" + "POST %1$s HTTP/1.1\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + //"TEST: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n" + //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n" + //"01-SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n" + "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#%3$s\"\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n" + "Host: %2$s\r\n" + "Content-Length: %4$d\r\n" + "Connection: close\r\n" +// "Connection: Keep-Alive\r\n" + "Pragma: no-cache\r\n" + "\r\n"; + +// 1$: action (string) +// 2$: argument list +static const char szSOAPMsgControlABodyFMT[] = + "" CRLF + "" S_CRLF + "" S_CRLF + "" S_CRLF + "%2$s" + "" S_CRLF + "" S_CRLF + "" S_CRLF +// CRLF +// "0" +// CRLF + CRLF; + +// 1$: argument name +// 2$: argument value +static const char szSOAPMsgControlAArgumentFMT[] = + "<%1$s>%2$s" S_CRLF; + +// 1$: argument name +// 2$: argument value +// 3$: argument type +static const char szSOAPMsgControlAArgumentFMT_t[] = + "<%1$s" + " xmlns:dt=\"urn:schemas-microsoft-com:datatypes\"" + " dt:dt=\"%3$s\">%2$s" S_CRLF; + +#if 0 +//// Generic SOAP Control:Query request messages +// 1$s: control URL +// 2$s: router's host/port ("host:port") +// 3$d: content-length +static const char szSOAPMsgControlQHeaderFMT[] = + "M-POST %1$s HTTP/1.1\r\n" + //"POST %1$s HTTP/1.1\r\n" + "Host: %2$s\r\n" + "Content-Length: %3$d\r\n" + "Content-Type: text/xml; charset-\"utf-8\"\r\n" + //"Man: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n" + //"SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n" + "01-SOAPAction: \"urn:schemas-upnp-org:control-1-0#QueryStateVariable\"\r\n" + "\r\n"; + +// 1$: variable name +static const char szSOAPMsgControlQBodyFMT[] = + "" S_CRLF + "" S_CRLF + "%s" S_CRLF + "" S_CRLF + "" S_CRLF + "" S_CRLF + "" S_CRLF; +#endif +// 1$: device description URL +// 2$: host/port +static const char szSSDPMsgDescribeDeviceFMT[] = + "GET %s HTTP/1.1\r\n" + "Accept: text/xml, application/xml\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n" + "Host: %s\r\n" + "Connection: close\r\n" +// "Connection: Keep-Alive\r\n" + "\r\n"; + +//////////////////////////////////////////////////////////////////////// +// GLOBAL Variables +//////////////////////////////////////////////////////////////////////// + +static int g_fFirstInit = TRUE; +static int g_fQuit = FALSE; +static FILE *g_log; +static int g_fLogging; + +// Globally-accessible UDP socket +static int g_sUDP = -1; +static int g_sUDPCancel = -1; + +// Globally-accessible TCP socket +static int g_sTCP = -1; +static int g_sTCPCancel = -1; + +// Event Vars +static int g_fEventEnabled = FALSE; +static unsigned short g_wEventPort; +static struct sockaddr_in g_saddrRouterEvent; +static char g_szRouterHostPortEvent[1024]; +static char g_szEventURL[1024]; + +// UPnP Router info +static char g_szFriendlyName[1024]; +static char g_szManufacturer[1024]; +static char g_szModelName[1024]; +static char g_szModelDescription[1024]; + +// URL base +static struct sockaddr_in g_saddrRouterBase; +static char g_szRouterHostPortBase[1024]; + +// the threads +static pthread_t g_UDPthread = NULL; +static pthread_t g_TCPthread = NULL; + +// Local IP +static unsigned long g_dwLocalIP = 0; + +// Globally accessible info about the router/UPnP +static int g_fUPnPEnabled = FALSE; +static char g_szUSN[1024]; + +static struct sockaddr_in g_saddrRouterDesc; +static char g_szRouterHostPortDesc[1024]; +static char g_szNATDevDescURL[1024]; + +static struct sockaddr_in g_saddrRouterSOAP; +static char g_szRouterHostPortSOAP[1024]; +static char g_szControlURL[1024]; +static int g_fControlURLSet = FALSE; + +// Lock/condvar for synchronous upnp calls +static pthread_mutex_t g_xUPnP; +static pthread_mutex_t g_xUPnPMsg; +static pthread_cond_t g_condUPnP; +static pthread_cond_t g_condUPnPControlURL; +static struct timeval g_tvUPnPInitTime; +static struct timeval g_tvLastUpdateTime; + +// timeout values in seconds +static int g_iFunctionTimeout = NA_DEFAULT_FUNCTION_TIMEOUT; + +static void GetDeviceDescription(void); +static void SetLocalIP(void); + +//////////////////////////////////////////////////////////////////////// +// IPAddr Functions +//////////////////////////////////////////////////////////////////////// + + +#define ISIPV6 0x01 +#define ISPPP 0x02 +#define IFNAMELEN 16 /* Interface Name Length */ +#define IPLEN 16 /* 16 bytes(128 bits) for IPv6 */ + +typedef struct tagIPINFO +{ + int iFlags; + char szIfName[IFNAMELEN]; /* Interface name */ + unsigned char abIP[IPLEN]; /* IP in host byte order */ + unsigned short wPort; +} IPINFO, *PIPINFO, **PPIPINFO; + +typedef struct hostent HOSTENT, *PHOSTENT; + +static unsigned long GetNATIPNetmask(unsigned long dwIP) +{ + if ((dwIP & 0xFF000000) == 0x0A000000) return 0xFF000000; + if ((dwIP & 0xFFF00000) == 0xAC100000) return 0xFFF00000; + if ((dwIP & 0xFFFF0000) == 0xC0a80000) return 0xFFFF0000; + + return 0; /* No NAT IP */ +} + +static int GetIPInfo(PPIPINFO ppIPInfo) +{ + int fd; + int iLastLen, iLen, iNum = 0, iMax = 0; + unsigned long dwIP; + char *pcBuf, *pcTemp; + PIPINFO pIPInfo = NULL; + struct ifconf ifc; + struct ifreq *ifr, ifrcopy; + + if (ppIPInfo == NULL) return 0; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + + iLastLen = -1; + iLen = 100 * sizeof(struct ifreq); + + for (;;) + { + pcBuf = (char *)malloc(iLen); + ifc.ifc_len = iLen; + ifc.ifc_buf = pcBuf; + + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) + { + if (errno != EINVAL || iLastLen != -1) + { +// DbgPrint(ELL_ERROR, "ioctl failed(%d)\n", errno); + free(pcBuf); + close(fd); + return 0; + } + } + else + { + if (ifc.ifc_len == iLastLen) break; + iLastLen = ifc.ifc_len; + } + + iLen += 10 * sizeof(struct ifreq); + free(pcBuf); + } + + for (pcTemp = pcBuf; pcTemp < pcBuf + ifc.ifc_len; ) + { + if (iNum >= iMax) + { + PIPINFO pIPInfoNew; + + iMax += 10; + pIPInfoNew = (PIPINFO)realloc(pIPInfo, sizeof(IPINFO) * iMax); + if (pIPInfoNew == NULL) + { + free(pIPInfo); + free(pcBuf); + close(fd); + return 0; + } + else pIPInfo = pIPInfoNew; + + memset(pIPInfo + (iMax - 10), 0, sizeof(IPINFO) * 10); + } + + ifr = (struct ifreq *)pcTemp; + + pcTemp += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; + + /* discard invalid address families & loopback */ + if ((ifr->ifr_addr.sa_family != AF_INET && + ifr->ifr_addr.sa_family != AF_INET6) || + strncmp(ifr->ifr_name, "lo", 2) == 0) continue; + + ifrcopy = *ifr; + ioctl(fd, SIOCGIFFLAGS, &ifrcopy); + if ((ifrcopy.ifr_flags & IFF_UP) == 0) continue; + + switch (ifr->ifr_addr.sa_family) + { + case AF_INET: + memcpy(pIPInfo[iNum].szIfName, ifr->ifr_name, IFNAMELEN); + dwIP = + ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr); + memcpy(pIPInfo[iNum].abIP, &dwIP, sizeof(unsigned long)); + if (ifrcopy.ifr_flags & IFF_POINTOPOINT) + pIPInfo[iNum].iFlags |= ISPPP; + iNum++; + break; + + case AF_INET6: + memcpy(pIPInfo[iNum].szIfName, ifr->ifr_name, IFNAMELEN); + memcpy(pIPInfo[iNum].abIP, + ((struct sockaddr_in6 *)&(ifr->ifr_addr))-> sin6_addr.s6_addr, + 16); + pIPInfo[iNum].iFlags |= ISIPV6; + if (ifrcopy.ifr_flags & IFF_POINTOPOINT) + pIPInfo[iNum].iFlags |= ISPPP; + iNum++; + break; + + default: + break; + } + } + + free(pcBuf); + close(fd); + + *ppIPInfo = pIPInfo; + + return iNum; +} + +static void FreeIPInfo(PIPINFO pIPInfo) +{ + if (pIPInfo != NULL) free(pIPInfo); +} + + +//////////////////////////////////////////////////////////////////////// +// Function Definitions +//////////////////////////////////////////////////////////////////////// + +static void SendDiscoveryMsg(); + +// SSDPListen +// Creates a UDP multicast socket and listens to the SSDP IP/PORT +// Returns +// -1 on error, or the socket descriptor if success +static int SSDPListen() +{ + char fLoop; + int iTTL; + struct ip_mreq mreq; + struct sockaddr_in saddr; + int sd; + + // IPPROTO_IP == 0; IPPROTO_TCP == 6; IPPROTO_UDP == 17; etc. + sd = socket(AF_INET, SOCK_DGRAM, 0); + if (sd == -1) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "Can't create socket! SSDPListen exiting\n"); + return NA_E_NET; + } + + // sock options values + fLoop = 0; // false - don't send copy to self + iTTL = SSDP_TTL; + + // bind to listen to ssdp multicast address + bzero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + //saddr.sin_addr.s_addr = inet_addr(SSDP_IP); + //saddr.sin_port = htons(SSDP_PORT); + saddr.sin_addr.s_addr = htonl(g_dwLocalIP); + saddr.sin_port = 0; + + // and set the multicast add_member structure + // (TODO: need to find interfaces later - ioctl, with: + // SIOCFIFCONF to find if's, SIOCGIFADDR to get addr, and SIOCFIFFLAGS + // to check for IFF_MULTICAST flag for multicast support on an if) + bzero(&mreq, sizeof(mreq)); + mreq.imr_interface.s_addr = g_dwLocalIP; + mreq.imr_multiaddr.s_addr = inet_addr(SSDP_IP); + + if ( + bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)) //|| + //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &fLoop, sizeof(fLoop)) || + //setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &iTTL, sizeof(iTTL)) || + //setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) + ) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, + "bind/setsockopt for multicast failed... errno = %d\n", errno); + close(sd); + return NA_E_NET; + } + + return sd; +} + +static int EventListen() +{ + struct sockaddr_in saddr; + int sd; + + // try 5 ports before failing completely + for (g_wEventPort = 5000; g_wEventPort < 5005; g_wEventPort++) + { + sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sd == -1) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "Can't create socket! EventListen exiting\n"); + return NA_E_NET; + } + + bzero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(g_dwLocalIP); + saddr.sin_port = htons(g_wEventPort); + + // return if okay + if (bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)) == 0) + { + listen(sd, 128); + ////TracePrint(ELL_TRACE, "UPnP: EventListen @%u\n", g_wEventPort); + return sd; + } + + // unsuccessful - close sd and try again + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, + "bind TCP port %u failed: errno = %d\n", g_wEventPort, errno); + close(sd); + } + + return NA_E_NET; +} + +static void *TCPProc(void *in); + +static int EventInit() +{ + int iRet; + pthread_attr_t attr; + + if (g_fEventEnabled == FALSE) + { + // initialize TCP socket for Eventing + g_sTCP = EventListen(); + if (g_sTCP < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "EventInit - Failed to init tcp socket.\n"); + return NA_E_INTERNAL_ERROR; + } + + // make TCP thread + pthread_attr_init(&attr); + iRet = pthread_create(&g_TCPthread, &attr, TCPProc, 0); + if (iRet != 0) { + close(g_sTCP); + g_sTCP = -1; + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "EventInit: TCPProc create failed(%d)\n", iRet); + return NA_E_THREAD_ERROR; + } + } + + g_fEventEnabled = TRUE; + + return NA_E_SUCCESS; +} + +static void DumpHex(char *buf, int len) +{ + int i; + int nexti; + int j; + int endj; + + if (g_fLogging & NALOG_DUMP) { + if (buf == NULL) return; + if (len <= 0) return; + + for (i = 0; i < len; i = nexti) { + fprintf(g_log, "%04x: ", i); + nexti = i + 16; + endj = (nexti > len) ? len : nexti; + for (j = i; j < endj; j++) + fprintf(g_log, "%02x ", buf[j] & 0xff); + if (j == len) { + if ((j % 16) != 0) { + char pad[3 * 16 + 1]; // don't need the last 3 bytes anyway + j = (16 - (j % 16)) * 3; + memset(pad, ' ', j); + pad[j] = '\0'; + fputs(pad, g_log); + } + } + for (j = i; j < endj; j++) + isprint(buf[j]) ? fputc(buf[j], g_log) : fputc('.', g_log); + fputc('\n', g_log); + } + + } +} + +// FindHTTPHeaderNewLine +// Returns a pointer to the beginning of a CRLF, that is not a +// part of LWS. (LWS is CRLF followed by a space or tab, and in +// HTTP, considered as equivalent to a single space) (LWS stands +// for "linear white space") +// Returns a pointer the beginning of CRLF, and sets the EOH flag to +// whether this is the last header in the HTTP header section. +// Also, if pbuf is NULL, or if there isn't any CRLF found in the +// string, or if the HTTP syntax is wrong, NULL is returned, and +// the EOH flag is not touched. +static char *FindHTTPHeaderNewLine(char *pbuf, int iBufSize, int *pfEOH) +{ + char *result; + int i = 0; + + if (pbuf == NULL) return NULL; + + for (;;) { + result = memchr(pbuf, '\r', iBufSize); + if (result == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "FindHTTPHeaderNewLine: er @(%d)\n", i); + fflush(g_log); + } + return NULL; + } + i++; // count chars + + // decrement iBufSize, and move pbuf forward + iBufSize -= (result - pbuf); + pbuf = result; + + ++pbuf; // now pointing right after "\r" + --iBufSize; + if (*pbuf == '\0') break; + if (*pbuf != '\n') continue; + + ++pbuf; // now pointing after "\r\n" + --iBufSize; + if (*pbuf == '\0') break; + if ((*pbuf == ' ') || (*pbuf == '\t')) continue; + + // at this point we know we're at the end of a header field, + // and there's more stuff coming... + + // just need to check if this is the last header + if ((pbuf[0] == '\r') && (pbuf[1] == '\n')) + *pfEOH = TRUE; + else + *pfEOH = FALSE; + + return result; + } + + return NULL; +} + +// NewHTTPResponse_sz +// Creates an HTTPResponse structure from a string (sz). Set +// fDestroyOriginal to TRUE if the buffer passed in can be overwritten. +// Otherwise, NewHTTPResponse_sz will duplicate the buffer. +// Returns the created HTTPResponse structure if successful, or if an +// error occured (out of memory, or bad http syntax), returns NULL. +// NOTE: ALWAYS call DeleteHTTPResponse after using the HTTPResponse structure. +// NOTE: The input is assumed to be correct. If there're HTTP syntax errors, +// and the pszHTTPResponse is not null-terminated, result may be undefined. +// (to be fixed next version) +static PHTTPResponse NewHTTPResponse_sz( + char *pszHTTPResponse, + int iBufferSize, + int fDestroyOriginal) +{ + PHTTPResponse pResponse; + int fEOH; + char *pszEOL; + int iNumHeaders; + char *pBuf; + + if ((pResponse = (PHTTPResponse)malloc(sizeof(HTTPResponse))) == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 1\n"); + fflush(g_log); + } + return NULL; + } + + // make copy of buffer now + if (fDestroyOriginal) { + pResponse->buf = NULL; + pBuf = pszHTTPResponse; + } + else { + int len = strlen(pszHTTPResponse); + if ((len+1) > iBufferSize) { + if (g_fLogging & NALOG_INFO0) + fprintf(g_log, "Length: %d > %d\n", len+1, iBufferSize); + iBufferSize = len+1; + } + if ((pResponse->buf = (char *)malloc(iBufferSize)) == NULL) { + free(pResponse); + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 2\n"); + fflush(g_log); + } + return NULL; + } + memcpy(pResponse->buf, pszHTTPResponse, iBufferSize); + pBuf = pResponse->buf; + } + + // get the first line + pszEOL = FindHTTPHeaderNewLine(pBuf, iBufferSize, &fEOH); + if (pszEOL == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 3\n"); + fflush(g_log); + } + goto cleanup; + } + + *pszEOL = '\0'; // terminate the status line + pszEOL += 2; // point to the rest of the buffer + + // set the status string first + pResponse->pszStatus = strchr(pBuf, ' '); + if (pResponse->pszStatus == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 4\n"); + fflush(g_log); + } + goto cleanup; // syntax error + } + + pResponse->pszStatus++; // point to the actual status + + pResponse->pszReason = strchr(pResponse->pszStatus, ' '); + if (pResponse->pszReason == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 5\n"); + fflush(g_log); + } + goto cleanup; // syntax error + } + + pResponse->pszReason[0] = '\0'; // terminate status string + pResponse->pszReason++; // point to the reason string + + iNumHeaders = 0; // initialize to 0 headers + + // parse header fields line by line (while not end of headers) + while (!fEOH) { + PProperty pHeader = &(pResponse->aHeaders[iNumHeaders]); + // point header field name to the first char of the line + pHeader->pszName = pszEOL; + + // search for the end of line + pszEOL = FindHTTPHeaderNewLine(pszEOL, + iBufferSize - (pszEOL - pBuf), // remainder size + &fEOH); + if (pszEOL == NULL) goto cleanup; // syntax error + + *pszEOL = '\0'; // terminate this string + pszEOL += 2; // point to beginning of next line + + pHeader->pszValue = strchr(pHeader->pszName, ':'); + if (pHeader->pszValue == NULL) { + if (g_fLogging & NALOG_INFO0) { + fprintf(g_log, "NewHTTPResponse_sz: er 6\n"); + fflush(g_log); + } + goto cleanup; // syntax error (header field has no ":") + } + + pHeader->pszValue[0] = '\0'; // terminate the header name string + pHeader->pszValue++; // point after the ":" + // get rid of leading spaces for the value part + while ( + (pHeader->pszValue[0] == ' ') || + (pHeader->pszValue[0] == '\t') || + (pHeader->pszValue[0] == '\r') || + (pHeader->pszValue[0] == '\n') + ) { + pHeader->pszValue++; // skip the space + } + + iNumHeaders++; // added one more header + pHeader++; // point to the next header in pResponse->aHeaders + } + + pResponse->iNumHeaders = iNumHeaders; // remember to set it in pResponse + + pResponse->pszBody = pszEOL + 2; // point after the empty line + + return pResponse; + +cleanup: + if (pResponse->buf != NULL) free(pResponse->buf); + free(pResponse); + return NULL; +} + +// DeleteHTTPResponse +// Deallocates stuff in the HTTPResponse structure, effectively returning +// memory to the system and destroying the structure. +// NOTE: The pointer pResponse WILL BE FREED, and will be unusable after +// the call to DeleteHTTPResponse. +static void DeleteHTTPResponse(PHTTPResponse pResponse) +{ +// int i; + + if (pResponse == NULL) return; + + // Current impl is just simple array - no need to free() + //for (i = 0; i < pResponse->iNumHeaders; i++) { + // free(pResponse->aHeaders[i]); + //} + + if (pResponse->buf != NULL) + free(pResponse->buf); + free(pResponse); +} + +//typedef struct tagHTTPResponse { +// char *pszStatus; +// char *pszReason; +// int iNumHeaders; +// Property aHeaders[30]; // assume at most this many headers +// char *pszBody; +// +// // for admin use +// int fFree; +// char *buf; +//} HTTPResponse, *PHTTPResponse, **PPHTTPResponse; + +static void PrintHTTPResponse(PHTTPResponse pResponse) +{ + int i; + + if (g_fLogging & (NALOG_INFO1)) { + if (pResponse == NULL) return; + fprintf(g_log, " *** HTTP response begin *** \n"); + fprintf(g_log, " * status = [%s], reason = [%s] *\n", + pResponse->pszStatus, pResponse->pszReason); + for (i = 0; i < pResponse->iNumHeaders; i++) { + fprintf(g_log, " * Header \"%s\" = [%s]\n", + pResponse->aHeaders[i].pszName, + pResponse->aHeaders[i].pszValue); + } + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, " * body = [%s] *\n", pResponse->pszBody); + fprintf(g_log, " *** HTTP response end *** \n"); + } +} + +static int DiscoverRouter(PHTTPResponse pResponse) +{ + int i; + int fLocation = FALSE; + int fUSN = FALSE; + int fIsNATDevice = FALSE; + +#if 0 + if (strcmp(pResponse->pszStatus, "200") != 0) + return -1; +#endif + + if (pResponse == NULL) { + if (g_fLogging & NALOG_INFO0) + fprintf(g_log, "DiscoverRouter: pResponse == NULL\n"); + return -1; + } + + // check to see if this is a relevant packet + for (i = 0; i < pResponse->iNumHeaders; i++) { + PProperty pHeader = &(pResponse->aHeaders[i]); + + if ((strcasecmp(pHeader->pszName, "ST") == 0) || + (strcasecmp(pHeader->pszName, "NT") == 0)) { + if ((strcmp(pHeader->pszValue, + "urn:schemas-upnp-org:service:WANIPConnection:1") == 0) || + (strcmp(pHeader->pszValue, + "urn:schemas-upnp-org:device:InternetGatewayDevice:1") == 0)) { + fIsNATDevice = TRUE; + } + } + } + + // leave the message alone if we don't need it + if (!fIsNATDevice) + return -1; + + // Now that we know we're looking at the message about the NAT device: + pthread_mutex_lock(&g_xUPnP); + + // set upnp to be unconfigured for now + g_fUPnPEnabled = FALSE; + + // loop through the headers + for (i = 0; i < pResponse->iNumHeaders; i++) { + PProperty pHeader = &(pResponse->aHeaders[i]); + + if (strcasecmp(pHeader->pszName, "Location") == 0) { + char *p; + char *q; + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Checking Location...\n"); + p = pHeader->pszValue; + if (strncmp(p, "http://", 7) != 0) + continue; // hope for another Location header to correct it + p += 7; // skip over "http://" + q = strchr(p, '/'); + + // set the control URL first + if (q == NULL) { + g_szNATDevDescURL[0] = '/'; + g_szNATDevDescURL[1] = '\0'; + } + else { + strncpy(g_szNATDevDescURL, q, sizeof(g_szNATDevDescURL) - 1); + g_szNATDevDescURL[sizeof(g_szNATDevDescURL) - 1] = '\0'; + // terminate the host/port string + *q = '\0'; + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, " Device Description URL set to[%s]...\n", + g_szNATDevDescURL); + + // see if port is specified + q = strchr(p, ':'); + if (q == NULL) { + sprintf(g_szRouterHostPortDesc, "%s", p); + + g_saddrRouterDesc.sin_addr.s_addr = inet_addr(p); + g_saddrRouterDesc.sin_port = htons(80); + } + else { + // don't include the ":80" - HTTP is by default port 80 + if (atoi(q+1) == 80) *q = '\0'; + + strcpy(g_szRouterHostPortDesc, p); + + // terminate the host part and point to it + *q = '\0'; + q++; + + g_saddrRouterDesc.sin_addr.s_addr = inet_addr(p); + g_saddrRouterDesc.sin_port = htons(atoi(q)); + } + + g_saddrRouterDesc.sin_family = AF_INET; + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, " Router Address set to[%s]...\n", + g_szRouterHostPortDesc); + fLocation = TRUE; + } + else if (strcasecmp(pHeader->pszName, "USN") == 0) { + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Checking USN...\n"); + strncpy(g_szUSN, pHeader->pszValue, sizeof(g_szUSN) - 1); + g_szUSN[sizeof(g_szUSN) - 1] = '\0'; + fUSN = TRUE; + } + else { + ; // do nothing for other headers for now + } + } + + // now check flags and set enabled if all set + if (fLocation && fUSN) { + if (g_fLogging & NALOG_INFO1) { + fprintf(g_log, + "Description Host/port string: [%s]\n" + "NATDevDescURL: [%s], USN: [%s]\n", + g_szRouterHostPortDesc, + g_szNATDevDescURL, g_szUSN); + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Got router information\n"); + } + + g_fUPnPEnabled = TRUE; + pthread_cond_broadcast(&g_condUPnP); + } + + // remember to unlock before return + pthread_mutex_unlock(&g_xUPnP); + + return 0; +} + +// granularity is specified as: granularity = 1/nth seconds +#define UPNP_TIMEOUT_GRANULARITY (1000) +#define U_TOGRAN UPNP_TIMEOUT_GRANULARITY + +// result = a - b +static void TimevalSubtract( + struct timeval *result, + const struct timeval *a, + const struct timeval *b) +{ + result->tv_sec = a->tv_sec - b->tv_sec; + + if (b->tv_usec > a->tv_usec) { + result->tv_sec--; + result->tv_usec = 1000000 + a->tv_usec - b->tv_usec; + } + else + result->tv_usec = a->tv_usec - b->tv_usec; +} + +// elapsed = end - start +static void GetTimeElapsed( + const struct timeval *tv_start, + const struct timeval *tv_end, + struct timeval *tv_elapsed) +{ + TimevalSubtract(tv_elapsed, tv_end, tv_start); +#if 0 + tv_elapsed->tv_sec = tv_end->tv_sec - tv_start->tv_sec; + + if (tv_start->tv_usec > tv_end->tv_usec) { + tv_elapsed->tv_sec--; + tv_elapsed->tv_usec = 1000000 + tv_end->tv_usec - tv_start->tv_usec; + } + else + tv_elapsed->tv_usec = tv_end->tv_usec - tv_start->tv_usec; +#endif +} + +// returns +1, 0, or -1, if a>b, a==b, atv_sec == b->tv_sec) && + (a->tv_usec == b->tv_usec)) return 0; + + if (a->tv_sec > b->tv_sec) return 1; + else if (a->tv_sec < b->tv_sec) return -1; + + // if seconds are equal... + if (a->tv_usec > b->tv_usec) return 1; + else return -1; +} + +static int WaitControlURLSet(double timeout) +{ + struct timespec ts; + struct timeval tv; + struct timeval tv_start; + int iRet; + long to_sec = (int) (timeout / U_TOGRAN); + long to_usec = + (int) (((timeout / U_TOGRAN) - to_sec) * 1000000.0); + //long to_sec = (int) timeout; + //long to_usec = (int) ((timeout - to_sec) * 1000000.0); + struct timeval elapsed; + + // get function start time + gettimeofday(&tv_start, NULL); + + pthread_mutex_lock(&g_xUPnP); + +#if 0 + // if last update is too long ago then wait for it + GetTimeElapsed(&g_tvLastUpdateTime, &tv_start, &elapsed); + if ((elapsed.tv_sec + (elapsed.tv_usec / 1000000.0)) > + (((double) g_iUPnPTimeout) / U_TOGRAN)) + g_fControlURLSet = 0; +#endif + + while (!g_fControlURLSet) { + // get current time + gettimeofday(&tv, NULL); + +#if 0 +for now ignore device timeout + // see if we've past the device's timeout first + GetTimeElapsed(&g_tvUPnPInitTime, &tv, &elapsed); + if ((elapsed.tv_sec > g_timeout_sec) || + ( (elapsed.tv_sec == g_timeout_sec) && + (elapsed.tv_usec > g_timeout_usec) + )) + { + pthread_mutex_unlock(&g_xUPnP); + return FALSE; + } +#endif + + // calculate ts to sleep till + ts.tv_sec = tv.tv_sec + to_sec; + ts.tv_nsec = (tv.tv_usec + to_usec) * 1000; + if (ts.tv_nsec > 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec += 1; + } + + // now get how long we've been in this function already and deduct + GetTimeElapsed(&tv_start, &tv, &elapsed); + ts.tv_sec -= elapsed.tv_sec; + if (ts.tv_nsec < (elapsed.tv_usec * 1000)) { + ts.tv_sec--; + ts.tv_nsec = 1000000000 + ts.tv_nsec - (elapsed.tv_usec * 1000); + } + else { + ts.tv_nsec -= (elapsed.tv_usec * 1000); + } + + iRet = pthread_cond_timedwait(&g_condUPnPControlURL, &g_xUPnP, &ts); + + // if timeout then return false + if (iRet != 0) + { + pthread_mutex_unlock(&g_xUPnP); + return FALSE; + } + } + pthread_mutex_unlock(&g_xUPnP); + + return TRUE; +} + +static int WaitUPnPFunction() +{ + struct timeval start; +// struct timeval end; + double wait2; +// struct timeval elapsed; + + gettimeofday(&start, NULL); + + wait2 = (double)g_iFunctionTimeout; + + WaitControlURLSet(wait2); + +//gettimeofday(&end, NULL); +//GetTimeElapsed(&start, &end, &elapsed); +//fprintf(stderr, "== wait2: (%f) %d.%06d\n", +// wait2/U_TOGRAN, elapsed.tv_sec, elapsed.tv_usec); + + return g_fControlURLSet; +} + +static void SetLocalIP(); + +static int SendTCPMsg_saddr_parse( + char *msg, int iLen, + char *result, int resultSize, + struct sockaddr_in *saHost); + +static void *TCPProc(void *in) +{ + int iRet; + unsigned char buf[MAX_SOAPMSGSIZE]; + int iBufLen; + + (void)in; // unused + WaitUPnPFunction(); + //TracePrint(ELL_TRACE, "UPnP: Begin TCPProc\n"); + + // do the subscription + { + char callback[100]; + char response[2000]; + PHTTPResponse resp; + int n; + sprintf(callback, "%lu.%lu.%lu.%lu:%u", + (g_dwLocalIP >> 24) & 0xFF, + (g_dwLocalIP >> 16) & 0xFF, + (g_dwLocalIP >> 8) & 0xFF, + (g_dwLocalIP >> 0) & 0xFF, + g_wEventPort); + + n = sprintf(buf, + szEventMsgSubscribeFMT, + g_szEventURL, + callback, g_szRouterHostPortEvent, 1800); + + memset(response, 0, 2000); + n = SendTCPMsg_saddr_parse( + buf, n, + response, 2000, + &g_saddrRouterEvent); + if (n > 0) + { + response[n] = '\0'; + resp = NewHTTPResponse_sz(buf, n, TRUE); + if (NULL != resp) + { +////TracePrint(ELL_TRACE, "UPnP Subscribe returns %s/%d\n", resp->pszStatus, n); + } + else + { +////TracePrint(ELL_TRACE, "UPnP Subscribe not enough response (%d) \n[%s]\n", +// n, response); + } + DeleteHTTPResponse(resp); + } + else + { +////TracePrint(ELL_TRACE, "UPnP Subscribe failed (%d)\n", n); + return NULL; + } + } + + //TracePrint(ELL_TRACE, "UPnP: TCPProc begin loop\n"); + + g_sTCPCancel = -1; + + for (;;) + { +// ssize_t n; + struct sockaddr_in recvaddr; + int recvaddrlen; + fd_set readfds; + struct timeval timeout; + int sEvent; + int fFirstRecv; + int sMax; + + // for after responding to long(?) TCP event + if (g_fQuit) + goto cleanup; + + if (g_sTCPCancel != -1) close(g_sTCPCancel); + sMax = g_sTCPCancel = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sMax < g_sTCP) sMax = g_sTCP; + + FD_ZERO(&readfds); + FD_SET(g_sTCP, &readfds); + FD_SET(g_sTCPCancel, &readfds); + iRet = select(sMax+1, &readfds, NULL, NULL, NULL); + if (iRet <= 0) { + if (EBADF == errno) + continue; + //TracePrint(ELL_TRACE, "UPnP Event select failed (%d)\n", errno); + continue; + } + + recvaddrlen = sizeof(recvaddr); + sEvent = accept(g_sTCP, (struct sockaddr *)&recvaddr, &recvaddrlen); + // not likely - (system's descriptor/file table full) + if (sEvent <= 0) continue; + + ////TracePrint(ELL_TRACE, "UPnP receiving event..\n"); + + // read all we could from this event + fFirstRecv = 1; + iBufLen = 0; + for (;;) + { + FD_ZERO(&readfds); + FD_SET(sEvent, &readfds); + timeout.tv_sec = 0; + timeout.tv_usec = 400000; // long cause we're dealing with input + iRet = select(sEvent+1, &readfds, NULL, NULL, &timeout); + if (iRet <= 0) { + if (g_fQuit) + { + close(sEvent); + goto cleanup; + } + break; + } + + // recv + iRet = recv(sEvent, buf + iBufLen, MAX_SOAPMSGSIZE - iBufLen, 0); + if (iRet < 0) + { + // something is wrong + break; + } + else if (iRet == 0) + { + break; + } + + iBufLen += iRet; + + if (fFirstRecv) + { + int iTemp; + iTemp = send(sEvent, HTTP200OK, HTTP200OKLEN, 0); + shutdown(sEvent, 1); + fFirstRecv = 0; + } + } + + // now send 200 OK and be done + close(sEvent); + + ////TracePrint(ELL_TRACE, "UPnP event (%d) received (%d)\n", g_fExpectEvent, iBufLen); + + // and parse the XML here. + if (iBufLen < MAX_SOAPMSGSIZE) + { + buf[iBufLen] = '\0'; + // for now do nothing + } + else + { + buf[MAX_SOAPMSGSIZE - 1] = '\0'; + } + } + +cleanup: + //TracePrint(ELL_TRACE, "UPnP: TCPProc end\n"); + close(g_sTCP); + g_sTCP = -1; + g_fEventEnabled = FALSE; + if (g_sTCPCancel != -1) close(g_sTCPCancel); + g_sTCPCancel = -1; + return NULL; +} + +static void *UDPProc(void *in) +{ +// char fLoop = 0; // false - don't send copy to self +// int iTTL = SSDP_TTL; + int iRet; +// struct ip_mreq mreq; +// struct sockaddr_in saddr; + unsigned char buf[65536]; +// FILE *log = g_log; + static time_t last_getdevicedesc_t = 0; + + (void)in; // unused + pthread_mutex_lock(&g_xUPnP); + gettimeofday(&g_tvUPnPInitTime, NULL); + pthread_mutex_unlock(&g_xUPnP); + + for (;;) { + ssize_t n; + struct sockaddr_in recvaddr; + int recvaddrlen; + fd_set readfds; + //struct timeval timeout; + //int i; + int sMax; + + if (g_sUDPCancel < g_sUDP) sMax = g_sUDP; + else sMax = g_sUDPCancel; + + FD_ZERO(&readfds); + FD_SET(g_sUDP, &readfds); + FD_SET(g_sUDPCancel, &readfds); + iRet = select(sMax+1, &readfds, NULL, NULL, NULL); + + if (iRet <= 0) { + if (g_fQuit) + { + close(g_sUDP); + close(g_sUDPCancel); + g_sUDP = -1; + g_sUDPCancel = -1; + return NULL; + } + continue; + } + + if (!FD_ISSET(g_sUDP, &readfds)) continue; + recvaddrlen = sizeof(recvaddr); + n = recvfrom(g_sUDP, buf, sizeof(buf), 0, + (struct sockaddr *)&recvaddr, &recvaddrlen); + if (n < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "recv failed (%d)\n", errno); + close(g_sUDP); + close(g_sUDPCancel); + g_sUDP = -1; + g_sUDPCancel = -1; + return NULL; + } + buf[n] = '\0'; + if (strncmp(buf, "HTTP/1.1", 8) == 0) { + PHTTPResponse pResponse = NewHTTPResponse_sz(buf, n, TRUE); + PrintHTTPResponse(pResponse); + if (DiscoverRouter(pResponse) == 0) + { + time_t now = time(NULL); + if (!g_fControlURLSet || + ((now - last_getdevicedesc_t) > 5)) + { + GetDeviceDescription(); + SetLocalIP(); + last_getdevicedesc_t = now; + } + } + DeleteHTTPResponse(pResponse); + } + else if (strncmp(buf, "NOTIFY * HTTP/1.1", 7) == 0) { + // temporarily use this to fudge - will have the exact same + // parsing, only status/reason set to "*" and "HTTP/1.1". + // TODO: add support for HTTP requests + PHTTPResponse pResponse = NewHTTPResponse_sz(buf, n, TRUE); + if (DiscoverRouter(pResponse) == 0) + { + time_t now = time(NULL); + if (!g_fControlURLSet || + ((now - last_getdevicedesc_t) > 5)) + { + GetDeviceDescription(); + SetLocalIP(); + last_getdevicedesc_t = now; + } + } + DeleteHTTPResponse(pResponse); + } + else { + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "(%ld) Buffer: \n[%s]\n", time(NULL), buf); + fflush(g_log); + } + } + + close(g_sUDP); + g_sUDP = -1; +} + +static void SendUDPMsg(const char *msg) { + struct sockaddr_in saSendTo; + int iRet; + int iLen; + + bzero(&saSendTo, sizeof(saSendTo)); + saSendTo.sin_family = AF_INET; + saSendTo.sin_addr.s_addr = inet_addr(SSDP_IP); + saSendTo.sin_port = htons(SSDP_PORT); + + iLen = strlen(msg); + + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "SendUDP: [%s]\n", msg); + + iRet = sendto(g_sUDP, msg, iLen, 0, + (struct sockaddr *)&saSendTo, sizeof(saSendTo)); + + // sanity check + if (iRet != iLen) + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, + "SendUDPMsg: iRet(%d) != strlen(msg)(%d)! (errno %d)\n", + iRet, iLen, errno); +} + +// strstr, case insensitive, and is limited by len +static char *strcasestr_n(const char *big, const char *little, int len) +{ + int bigLen; + int littleLen; + int i; + int end; + + if (little == NULL) return (char *)big; + if (big == NULL) return NULL; + + //bigLen = strlen(big); + bigLen = len; + littleLen = strlen(little); + + if (bigLen < littleLen) return NULL; + + end = bigLen - littleLen; + for (i = 0; i <= end; (i++), (big++)) { + if (strncasecmp(big, little, littleLen) == 0) + return (char *)big; + } + + return NULL; +} + +// this is strnstr, only portable +static char *strstr_n(const char *big, const char *little, int len) +{ + int iBigLen; + int iLittleLen; + + (void)len; // unused + + if ((big == NULL) || (little == NULL)) return NULL; + + iBigLen = strlen(big); + iLittleLen = strlen(little); + + // this part is basically strnstr, except this is portable + for (;;) { + if (iBigLen < iLittleLen) + return NULL; + if (strncmp(big, little, iLittleLen) == 0) + return (char *)big; + ++big; + --iBigLen; + } +} + +// returns -1 for "not found" +static int FindContentLength(char *pbuf, int iLen) +{ + // non reusable HTTP header parsing code: + // ---------------------------------------------- + char *p; + int iResult; + + // find content length header + p = strcasestr_n(pbuf, "\r\nContent-Length:", iLen); + if (p == NULL) return -1; + + p += sizeof("\r\nContent-Length:") - 1; // minus '\0' + + iResult = atoi(p); + + return iResult; + // ---------------------------------------------- +} + +// returns -1 for "not found" +static int FindBody(char *pbuf, int iLen) +{ + // non reusable HTTP header parsing code: + // ---------------------------------------------- + char *p; +// int iResult; + + // find the empty line + p = strstr_n(pbuf, "\r\n\r\n", iLen); + if (p == NULL) return -1; + + p += sizeof("\r\n\r\n") - 1; // minus '\0' + + return (p - pbuf); + // ---------------------------------------------- +} + +static int SendTCPMsg_saddr_2part( + char *msg, int iLen, + char *msg2, int iLen2, + char *result, int resultSize, + struct sockaddr_in *saHost) +{ + int s; + struct sockaddr_in saSendTo; + int iRet; + int iBufLen; + int fND; + int fcntl_flags; + int iRetcode; + struct timeval tv; + fd_set writefds; + + struct timeval tv_start; + struct timeval tv_end; + struct timeval tv_elapsed; + + int iContentLength = -1; + int iBodyOffset = -1; + + gettimeofday(&tv_start, NULL); + + if (g_fUPnPEnabled != TRUE) { +//TracePrint(ELL_TRACE, "UPnP not enabled\n"); + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "UPnP not enabled (no UPnP device found yet)\n"); + return NA_E_NOT_AVAILABLE; + } + + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "Can't get TCP socket (%d)\n", errno); + return NA_E_NET; + } + + fND = 1; + if (setsockopt(s, IPPROTO_IP, TCP_NODELAY, &fND, sizeof(fND)) != 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: Can't set TCP_NODELAY option!\n"); + iRetcode = NA_E_NET; + goto cleanup; + } + + fcntl_flags = 0; + fcntl_flags = fcntl(s, F_GETFL, 0); + fcntl_flags |= O_NONBLOCK; + if (fcntl(s, F_SETFL, fcntl_flags) != 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: Can't set O_NONBLOCK option!\n"); + iRetcode = NA_E_NET; + goto cleanup; + } + + if (saHost == NULL) + memcpy(&saSendTo, &g_saddrRouterDesc, sizeof(saSendTo)); + else + memcpy(&saSendTo, saHost, sizeof(saSendTo)); + + iRet = connect(s, (struct sockaddr *) &saSendTo, sizeof(saSendTo)); + if ((iRet < 0) && (errno != EINPROGRESS)) { +//TracePrint(ELL_TRACE, "UPnP connect failed\n"); + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: connect failed (%d)\n", errno); + iRetcode = NA_E_NET; + goto cleanup; + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, + "- Before Sending TCP Msg1: %d == %lu?\n", iLen, strlen(msg)); + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "Sending TCP msg part 1:\n[%s]\n", msg); + + tv.tv_sec = g_iFunctionTimeout / UPNP_TIMEOUT_GRANULARITY; + tv.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + FD_ZERO(&writefds); + FD_SET(s, &writefds); + iRet = select(s+1, 0, &writefds, 0, &tv); + if (iRet < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: select failed (%d)\n", errno); + iRetcode = NA_E_NET; + goto cleanup; + } + if (iRet == 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: select timed out\n"); + iRetcode = NA_E_TIMEOUT; +gettimeofday(&tv_end, NULL); +GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); +//TracePrint(ELL_TRACE, "UPnP 2part: timeout @1st after %lu.%06lu secs\n", +// tv_elapsed.tv_sec, tv_elapsed.tv_usec); + goto cleanup; + } + + iRet = send(s, msg, iLen, 0); + // sanity check + if (iRet != iLen) + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "SendTCPMsg/2part: iRet(%d) != strlen(msg)(%d)!\n", + iRet, iLen); + +//TracePrint(ELL_TRACE, "UPnP 2part: 1st %d == %d (%d) (%d)?\n", iRet, iLen, strlen(msg), errno); + + tv.tv_sec = g_iFunctionTimeout / UPNP_TIMEOUT_GRANULARITY; + tv.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + FD_ZERO(&writefds); + FD_SET(s, &writefds); + // calculate how much time elapsed + gettimeofday(&tv_end, NULL); + GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); + if (CompareTime(&tv_elapsed, &tv) > 0) { + close(s); + return NA_E_TIMEOUT; + //tv.tv_sec = 0; + //tv.tv_usec = 0; + } + else { + // subtract that from timeout accordingly + tv.tv_sec -= tv_elapsed.tv_sec; + if (tv.tv_usec < tv_elapsed.tv_usec) { + tv.tv_sec--; + tv.tv_usec = 1000000 + tv.tv_usec - tv_elapsed.tv_usec; + } + else + tv.tv_usec = tv.tv_usec - tv_elapsed.tv_usec; + } + iRet = select(s+1, 0, &writefds, 0, &tv); + if (iRet < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: select2 failed (%d)\n", errno); + iRetcode = NA_E_NET; + goto cleanup; + } + if (iRet == 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/2part: select2 timed out\n"); + iRetcode = NA_E_TIMEOUT; +gettimeofday(&tv_end, NULL); +GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); +//TracePrint(ELL_TRACE, "UPnP 2part: timeout @2nd after %lu.%06lu secs\n", +// tv_elapsed.tv_sec, tv_elapsed.tv_usec); + goto cleanup; + } + + iRet = send(s, msg2, iLen2, 0); + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, + "SendTCPMsg/parse: Before Sending TCP Msg2: %d == %lu?\n", + iLen2, strlen(msg2)); + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "Sending TCP msg part 2:\n[%s]\n", msg2); + +//TracePrint(ELL_TRACE, "UPnP 2part: 2nd %d == %d (%d) (%d)?\n", iRet, iLen2, strlen(msg2), errno); + + // sanity check + if (iRet != iLen2) + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "SendTCPMsg/2part: iRet(%d) != strlen(msg2)(%d)!\n", + iRet, iLen2); + + if (result == NULL) { // if caller just want to send/display msgs + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "TCP Buffer: ["); + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "start recv @%lu\n", time(NULL)); + + iBufLen = 0; + iContentLength = -1; + iBodyOffset = -1; + for (;;) { + fd_set readfds; + struct timeval timeout; + int i; + + FD_ZERO(&readfds); + FD_SET(s, &readfds); + //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN; + //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + // just do flat 2 sec now, since connection already established + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + + iRet = select(s+1, &readfds, NULL, NULL, &timeout); + if (iRet <= 0) + { +//TracePrint(ELL_TRACE, "UPnP 2part: select timeout? (%d, %d)\n", +// iRet, errno); + break; + } + +//gettimeofday(&tv_end, NULL); +//GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); +//fprintf(stderr, "2 == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec); + + // if only sending messages + if (result == NULL) { + char t[1000]; + i = recv(s, t, 1000-1, 0); // leave room for '\0' for dump + if (i== 0) break; + if (g_fLogging & NALOG_DUMP) { + t[i] = '\0'; + fprintf(g_log, "%s", t); + } + continue; + } + + // EO result buf: discard extra bytes + if (resultSize <= iBufLen) { + char t[1000]; + i = recv(s, &t, 1000, 0); + if (i== 0) break; + // Note that there's no dump here - prevents DoS attack from + // flooding the logs/diskspace + continue; + } + + i = recv(s, result + iBufLen, resultSize - iBufLen, 0); + if (i <= 0) { +//TracePrint(ELL_TRACE, "UPnP 2part: recv done %d (%d, %d)\n", +// iBufLen, i, errno); + break; + } + + iBufLen += i; + + // parse and see if we can find content-length to quit early + iContentLength = FindContentLength(result, iBufLen); + + // now if we're still in header, see if we can find body + iBodyOffset = FindBody(result, iBufLen); + + // now check if we can leave early. conditions are: + // past headers, and we've already recv'ed content-length of body + if ((iBodyOffset >= 0) && + (iContentLength >= 0) && + ((iBufLen - iBodyOffset) >= iContentLength)) + { +//TracePrint(ELL_TRACE, "UPnP 2part: read all specified %d (%d, %d) (%d, %d)\n", +// iBufLen, i, errno, iBodyOffset, iContentLength); + break; + } + } + +//fprintf(stderr, "2 -- \n"); + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "done recv @%lu\n", time(NULL)); + + if (result == NULL) { // if caller just want to send/display msgs + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "]\n"); + } + + close(s); + return iBufLen; + +cleanup: + close(s); + return iRetcode; +} + +static int SendTCPMsg_saddr_parse( + char *msg, int iLen, + char *result, int resultSize, + struct sockaddr_in *saHost) +{ + int s; + struct sockaddr_in saSendTo; + int iRet; + int iBufLen; + int fcntl_flags; + fd_set writefds; + struct timeval tv; + + struct timeval tv_start; +// struct timeval tv_end; +// struct timeval tv_elapsed; + + // HTTP parsing vars + char *pszCurHdr; + int iContentLength; + int iBodyOffset; +// char prevChar; + + tv.tv_sec = 0; + tv.tv_usec = 25000; + select(0, NULL, NULL, NULL, &tv); + + pthread_mutex_lock(&g_xUPnPMsg); + + gettimeofday(&tv_start, NULL); + + if (g_fUPnPEnabled != TRUE) { +//TracePrint(ELL_TRACE, "UPnP not enabled\n"); + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "UPnP not enabled (no UPnP device found yet)\n"); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NOT_AVAILABLE; + } + + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "Can't get TCP socket (%d)\n", errno); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NET; + } + + fcntl_flags = 0; + fcntl_flags = fcntl(s, F_GETFL, 0); + fcntl_flags |= O_NONBLOCK; + if (fcntl(s, F_SETFL, fcntl_flags) != 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/parse: Can't set O_NONBLOCK option!\n"); + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NET; + } + + if (saHost == NULL) + memcpy(&saSendTo, &g_saddrRouterDesc, sizeof(saSendTo)); + else + memcpy(&saSendTo, saHost, sizeof(saSendTo)); + + iRet = connect(s, (struct sockaddr *) &saSendTo, sizeof(saSendTo)); + if ((iRet < 0) && (errno != EINPROGRESS)) { +//TracePrint(ELL_TRACE, "UPnP connect failed\n"); + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/parse: connect failed (%d)\n", errno); + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NET; + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "SendTCPMsg/parse: Before Sending TCP Msg: %d == %lu?\n", + iLen, strlen(msg)); + if (g_fLogging & NALOG_DUMP) + fprintf(g_log,"Sending TCP msg:\n[%s]\n", msg); + + tv.tv_sec = g_iFunctionTimeout / UPNP_TIMEOUT_GRANULARITY; + tv.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + FD_ZERO(&writefds); + FD_SET(s, &writefds); + iRet = select(s+1, 0, &writefds, 0, &tv); + if (iRet < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/parse: select failed (%d)\n", errno); + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_NET; + } + if (iRet == 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "SendTCPMsg/parse: select timed out\n"); + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return NA_E_TIMEOUT; + } + + iRet = send(s, msg, iLen, 0); + + // sanity check + if (iRet != iLen) + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "SendTCPMsg: iRet (%d) != strlen(msg) (%d)!\n", + iRet, iLen); + + if (result == NULL) { // if caller just want to send/display msgs + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "TCP Buffer: ["); + } + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "start recv @%lu\n", time(NULL)); + + iBufLen = 0; + pszCurHdr = result; + iContentLength = -1; + iBodyOffset = -1; + for (;;) { + fd_set readfds; + struct timeval timeout; + int i; + + FD_ZERO(&readfds); + FD_SET(s, &readfds); + //timeout.tv_sec = g_iFunctionTimeout / U_TOGRAN; + //timeout.tv_usec = (g_iFunctionTimeout % U_TOGRAN) * 1000000 / U_TOGRAN; + // just do flat 2 sec now, since connection already established + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + iRet = select(s+1, &readfds, NULL, NULL, &timeout); + if (iRet <= 0) { +//fprintf(stderr, "**********: select failed (%d/%d)\n", iRet, errno); + break; + } + +//gettimeofday(&tv_end, NULL); +//GetTimeElapsed(&tv_start, &tv_end, &tv_elapsed); +//fprintf(stderr, "p == loop: %d.%06d\n", tv_elapsed.tv_sec, tv_elapsed.tv_usec); + + // if only sending messages + if (result == NULL) { + char t[1000]; + i = recv(s, t, 1000-1, 0); // leave room for '\0' for dump + if (i== 0) break; + if (g_fLogging & NALOG_DUMP) { + t[i] = '\0'; + fprintf(g_log, "%s", t); + } + continue; + } + + // EO result buf: discard extra bytes + if (resultSize <= iBufLen) { + char t[1000]; + i = recv(s, &t, 1000, 0); + if (i== 0) break; + // Note that there's no dump here - prevents DoS attack from + // flooding the logs/diskspace + continue; + } + + i = recv(s, result + iBufLen, resultSize - iBufLen, 0); + if (0 == i) { + + break; + } + else if (i < 0) { + if (EAGAIN == errno) continue; + break; + } + + iBufLen += i; + + // parse and see if we can find content-length to quit early + iContentLength = FindContentLength(result, iBufLen); + + // now if we're still in header, see if we can find body + iBodyOffset = FindBody(result, iBufLen); + + } + +//fprintf(stderr, "p -- \n"); + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "done recv @%lu\n", time(NULL)); + + if (result == NULL) { // if caller just want to send/display msgs + if (g_fLogging & NALOG_DUMP) + fprintf(g_log, "]\n"); + } + + close(s); + pthread_mutex_unlock(&g_xUPnPMsg); + return iBufLen; +} + + + +// szSOAPMsgControlAHeaderFMT - 4 args (ctrl_url, host/port, action, length) +// szSOAPMsgControlABodyFMT - 2 args (action, args string) +// szSOAPMsgControlAArgumentFMT - 2 args (name/value) +static PHTTPResponse SendSOAPMsgControlAction( + char *action, + int argc, + PProperty args, + int f2Part) +{ + //char outBuffer[65536]; + //char outBufferBody[65536]; + //char outBufferArgs[65536]; + char *outBuffer = NULL; + char *outBufferBody = NULL; + char *outBufferArgs = NULL; + char *inBuffer = NULL; + int iLen; + int iHeaderLen; + int iBodyLen; + int iArgsLen; + int iResultLen; + int i; + int n; + PHTTPResponse pResponse = NULL; + + + if (!WaitUPnPFunction()) + return NULL; + + if ((outBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for outBuffer\n"); + goto cleanup; + } + if ((outBufferBody = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for outBufferBody\n"); + goto cleanup; + } + if ((outBufferArgs = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for outBufferArgs\n"); + goto cleanup; + } + if ((inBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for inBuffer\n"); + goto cleanup; + } + + iArgsLen = 0; + if (args != NULL) + for (i=0; i 0) { + if (iResultLen > MAX_SOAPMSGSIZE) { + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "result truncated..\n"); + iResultLen = MAX_SOAPMSGSIZE; + } + pResponse = NewHTTPResponse_sz(inBuffer, iResultLen, FALSE); + if (pResponse != NULL) { + PrintHTTPResponse(pResponse); + //DeleteHTTPResponse(pResponse); + // - return response to caller + } + } + else { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "No TCP Response\n"); + //TracePrint(ELL_TRACE, "UPnP SendSOAPMsg got no TCP response (%d)\n", +// iResultLen); + } + +cleanup: + if (outBuffer != NULL) free(outBuffer); + if (outBufferBody != NULL) free(outBufferBody); + if (outBufferArgs != NULL) free(outBufferArgs); + if (inBuffer != NULL) free(inBuffer); + + return pResponse; +} + +static int FindURLBase(char *pbuf, int iLen, char *szURLBase) +{ + // non reusable XML parsing code: + // ---------------------------------------------- + char *p; + int i = 0; + + // now skip after end of this tag, then skip until controlURL tag + p = strstr_n(pbuf, "", iLen); + if (p == NULL) return -1; + + // skip to the actual stuff + p += sizeof("") - 1; // minus '\0' + + // skip white spaces (just in case) + while (isspace(*p)) + p++; + + // copy into szURLBase + while ((*p != '\0') && (*p != '<') && !isspace(*p)) { + if (i++ > 1000) break; + *szURLBase = *p; + szURLBase++; + p++; + } + *szURLBase = '\0'; + + return 0; + // ---------------------------------------------- +} + + +static int FindDescInfo( + char *pbuf, + int iLen, + const char *szParentName, + const char *szName, + char *szValue) +{ + char *p; + char szSearch[100]; + int iSearchLen; + int i = 0; + + // find the device within pbuf + p = strstr_n( + pbuf, + szParentName, + iLen); + if (p == NULL) + return -1; + + // adjust strlen + iLen -= (p - pbuf); + pbuf = p; + + // now skip after end of this tag, then skip until manufacturer tag + iSearchLen = sprintf(szSearch, "<%s>", szName); + p = strstr_n(pbuf, szSearch, iLen); + if (p == NULL) return -1; + p += iSearchLen; + + // skip white spaces (just in case) + while (isspace(*p)) + p++; + + // copy into szValue + while ((*p != '\0') && (*p != '<')) { + if (i++ > 1000) break; + *szValue = *p; + szValue++; + p++; + } + *szValue = '\0'; + + return 0; +} + +static int FindIGDInfo(char *pbuf, int iLen, const char *szName, char *szValue) +{ + return FindDescInfo( + pbuf, iLen, + "urn:schemas-upnp-org:device:InternetGatewayDevice:1", + szName, szValue); +} + +static int FindManufacturer(char *pbuf, int iLen, char *szManuf) +{ + return FindIGDInfo(pbuf, iLen, "manufacturer", szManuf); +} + +static int FindFriendlyName(char *pbuf, int iLen, char *szValue) +{ + return FindIGDInfo(pbuf, iLen, "friendlyName", szValue); +} + +static int FindModelName(char *pbuf, int iLen, char *szValue) +{ + return FindIGDInfo(pbuf, iLen, "modelName", szValue); +} + +static int FindModelDescription(char *pbuf, int iLen, char *szValue) +{ + return FindIGDInfo(pbuf, iLen, "modelDescription", szValue); +} + +static int FindWANIPInfo(char *pbuf, int iLen, const char *szName, char *szValue) +{ + return FindDescInfo( + pbuf, iLen, + "urn:schemas-upnp-org:service:WANIPConnection:1", + szName, szValue); +} + +static int FindControlURL(char *pbuf, int iLen, char *szControlURL) +{ + return FindWANIPInfo(pbuf, iLen, "controlURL", szControlURL); +} + +static int FindEventURL(char *pbuf, int iLen, char *szEventURL) +{ + return FindWANIPInfo(pbuf, iLen, "eventSubURL", szEventURL); +} + +static int FindRouterInfo(char *inBuffer, int iLen) +{ + if (FindManufacturer(inBuffer, iLen, g_szManufacturer) != 0) + g_szManufacturer[0] = '\0'; + + if (FindFriendlyName(inBuffer, iLen, g_szFriendlyName) != 0) + g_szFriendlyName[0] = '\0'; + + if (FindModelName(inBuffer, iLen, g_szModelName) != 0) + g_szModelName[0] = '\0'; + + if (FindModelDescription(inBuffer, iLen, g_szModelDescription) != 0) + g_szModelDescription[0] = '\0'; + +//TracePrint(ELL_TRACE, +// "UPnP Router Info:\n" +// " - manufacturer [%s]\n" +// " - friendly name [%s]\n" +// " - model name [%s]\n" +// " - model desc [%s]\n", +// g_szManufacturer, g_szFriendlyName, g_szModelName, g_szModelDescription); + + return 0; +} + +static void ParseURL( + const char *szBuf, char *pszHostPort, + struct sockaddr_in *psaddr, char *pszPath) +{ + char buf[1024]; + char *p; + char *q; + unsigned short port; + + strcpy(buf, szBuf); + + p = buf; + if (0 == strncmp(p, "http://", 7)) + p += 7; + + q = strchr(p, '/'); + + if (pszPath) { + if (NULL == q) { + pszPath[0] = '/'; + pszPath[1] = '\0'; + } + else { + strcpy(pszPath, q); + *q = '\0'; + } + } + + // find the port separetor + q = strchr(p, ':'); + if (NULL == q) + port = 80; + else { + port = atoi(q + 1); + // HTTP's by default port 80, so don't have it in the "Host:" header + if (80 == port) *q = '\0'; + } + + if (pszHostPort) strcpy(pszHostPort, p); + + if (NULL != q) *q = '\0'; + + if (NULL != psaddr) { + psaddr->sin_family = AF_INET; + psaddr->sin_addr.s_addr = inet_addr(p); + psaddr->sin_port = htons(port); + } +#if 0 +//TracePrint(ELL_TRACE, "ParseURL [%s] -> [%s][%s] %lu.%lu.%lu.%lu:%u\n", + szBuf, + pszHostPort?pszHostPort:"", + pszPath?pszPath:"", + (psaddr->sin_addr.s_addr >> 24) & 0xff, + (psaddr->sin_addr.s_addr >> 16) & 0xff, + (psaddr->sin_addr.s_addr >> 8) & 0xff, + (psaddr->sin_addr.s_addr >> 0) & 0xff, + psaddr->sin_port); +#endif +} + +static void GetDeviceDescription(void) +{ + char *outBuffer = NULL; + char *inBuffer = NULL; + int iBufLen; + int iLen; + char szURLBase[1024]; + char szControlURL[1024]; + char szEventURL[1024]; + + if (!g_fUPnPEnabled) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "GetDeviceDescription: upnp not enabled\n"); + return; + } + + if ((outBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for outBuffer\n"); + goto cleanup; + } + if ((inBuffer = (char *) malloc(MAX_SOAPMSGSIZE)) == NULL) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "can't malloc for inBuffer\n"); + goto cleanup; + } + + iBufLen = sprintf(outBuffer, szSSDPMsgDescribeDeviceFMT, g_szNATDevDescURL, + g_szRouterHostPortDesc); + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Describe Device: [%s]\n", outBuffer); + iLen = SendTCPMsg_saddr_parse(outBuffer, iBufLen, inBuffer, MAX_SOAPMSGSIZE, + &g_saddrRouterDesc); + + g_fControlURLSet = FALSE; + + if (FindControlURL(inBuffer, iLen, szControlURL) != 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(g_log, "GetDeviceDesc: can't find control URL\n"); + goto cleanup; + } + + // start modifying global + pthread_mutex_lock(&g_xUPnP); + + { + // now see if there's the URLBase + if (FindURLBase(inBuffer, iLen, szURLBase) != 0) { + // not there? try default numbers from device description + memcpy(&g_saddrRouterBase, &g_saddrRouterDesc, + sizeof(g_saddrRouterBase)); + strcpy(g_szRouterHostPortBase, g_szRouterHostPortDesc); + } + else { + ParseURL(szURLBase, + g_szRouterHostPortBase, &g_saddrRouterBase, NULL); + + if ((strlen(g_szRouterHostPortBase) == 0) || + (g_saddrRouterBase.sin_addr.s_addr == INADDR_NONE)) { + memcpy(&g_saddrRouterBase, &g_saddrRouterDesc, + sizeof(g_saddrRouterBase)); + strcpy(g_szRouterHostPortBase, g_szRouterHostPortDesc); + } + } + } + + ParseURL(szControlURL, + g_szRouterHostPortSOAP, &g_saddrRouterSOAP, g_szControlURL); + if ((strlen(g_szRouterHostPortSOAP) == 0) || + (g_saddrRouterSOAP.sin_addr.s_addr == INADDR_NONE)) { + memcpy(&g_saddrRouterSOAP, &g_saddrRouterBase, + sizeof(g_saddrRouterSOAP)); + strcpy(g_szRouterHostPortSOAP, g_szRouterHostPortBase); + } + + +////TracePrint(ELL_TRACE, "UPnP Control URL set to[%s][%s]...\n", +// g_szRouterHostPortSOAP, g_szControlURL); + + g_fControlURLSet = TRUE; + gettimeofday(&g_tvLastUpdateTime, NULL); + pthread_cond_broadcast(&g_condUPnPControlURL); + + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Got Device Description\n"); + + // find router info + FindRouterInfo(inBuffer, iLen); + + if (FindEventURL(inBuffer, iLen, szEventURL) != 0) { + szEventURL[0] = '\0'; + } + else { + ParseURL(szEventURL, + g_szRouterHostPortEvent, &g_saddrRouterEvent, g_szEventURL); + if ((strlen(g_szRouterHostPortEvent) == 0) || + (g_saddrRouterEvent.sin_addr.s_addr == INADDR_NONE)) { + memcpy(&g_saddrRouterEvent, &g_saddrRouterBase, + sizeof(g_saddrRouterEvent)); + strcpy(g_szRouterHostPortEvent, g_szRouterHostPortBase); + } + + EventInit(); + } + +cleanup: + if (outBuffer != NULL) free(outBuffer); + if (inBuffer != NULL) free(inBuffer); + + pthread_mutex_unlock(&g_xUPnP); +} + + +static void GetIPByName(char *hostname, unsigned long *ip_ret) +{ + unsigned long ip; + + ip = inet_addr(hostname); + if (ip == INADDR_NONE) { + struct hostent *pHEnt; + pHEnt = gethostbyname(hostname); + if (pHEnt == NULL) { + if (g_fLogging & NALOG_ALERT) + fprintf(g_log, "Can't translate [%s] to IP...\n", hostname); + g_dwLocalIP = htonl(INADDR_ANY); + return; + } + ip = ntohl(*(unsigned long *)(pHEnt->h_addr)); + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "hostname [%s] to ip: %ld.%ld.%ld.%ld\n", + hostname, + (ip >> 24) & 0xff, + (ip >> 16) & 0xff, + (ip >> 8) & 0xff, + (ip >> 0) & 0xff); + } + *ip_ret = ip; +} + +static void SetLocalIP() +{ + PIPINFO pIPInfo = NULL; + int count = GetIPInfo(&pIPInfo); + if (NULL != pIPInfo) + { + // choose first non IPV6 address + // iterate through array and set port information + int i; + unsigned long dwFirst = 0; + for(i = 0; i < count; i++) + { + if (!(pIPInfo[i].iFlags & ISIPV6) && + (strncmp(pIPInfo[i].szIfName, "ppp", 3) != 0)) + { + unsigned long dwTemp; + + memcpy(&dwTemp, pIPInfo[i].abIP, sizeof(unsigned long)); + + if (0 != GetNATIPNetmask(dwTemp)) { + g_dwLocalIP = dwTemp; + break; + } + + if (0 == dwFirst) + dwFirst = dwTemp; + } + } + if (i == count) + g_dwLocalIP = dwFirst; + FreeIPInfo(pIPInfo); + } + +} + +static int FindTagContent(const char *text, const char *tagname, char *buf) +{ + char *p; + // parse the xml + p = strstr(text, tagname); + if (p == NULL) { + if (g_fLogging & NALOG_INFO0) + fprintf(g_log, "FindTagContent: can't find %s\n", tagname); + return NA_E_PARSE_ERROR; + } + + if (sscanf(p, "%*[^>]> %[^ <] <", buf) < 1) { + if (g_fLogging & NALOG_INFO0) + fprintf(g_log, "FindTagContent: Can't parse tag %s\n", tagname); + return NA_E_PARSE_ERROR; + } + + return NA_E_SUCCESS; +} + +mStatus LNT_UnmapPort(mDNSIPPort PubPort, mDNSBool tcp) +{ + //int iLen; + char szEPort[10]; + //char szRemoteHost[1024]; + //unsigned long dwIP; + Property propArgs[3]; + PHTTPResponse resp; + unsigned short port = PubPort.NotAnInteger; + int protocol = tcp ? IPPROTO_TCP : IPPROTO_UDP; + sprintf(szEPort, "%u", port); + + bzero(propArgs, sizeof(propArgs)); + propArgs[0].pszName = "NewRemoteHost"; + propArgs[0].pszValue = ""; + propArgs[0].pszType = "string"; + propArgs[1].pszName = "NewExternalPort"; + propArgs[1].pszValue = szEPort; + propArgs[1].pszType = "ui2"; + propArgs[2].pszName = "NewProtocol"; + if (protocol == IPPROTO_TCP) { + propArgs[2].pszValue = "TCP"; + } + else if (protocol == IPPROTO_UDP) { + propArgs[2].pszValue = "UDP"; + } + else { + return -1; + } + propArgs[2].pszType = "string"; + + resp = SendSOAPMsgControlAction( + "DeletePortMapping", 3, propArgs, FALSE); + if (resp == NULL) { + return mStatus_NATTraversal; + } + + if (strcmp(resp->pszStatus, "200") != 0) { + DeleteHTTPResponse(resp); + return mStatus_NATTraversal; + } + + DeleteHTTPResponse(resp); + return mStatus_NoError; +} + + +static int GetMappingUnused(unsigned short eport, int protocol); + +extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp) +{ + char szEPort[6]; + char szIPort[6]; + unsigned long dwIP; + char szLocalIP[30]; + char descr[40]; + Property propArgs[8]; + PHTTPResponse resp; + unsigned short iport = priv.NotAnInteger; + unsigned short eport = pub.NotAnInteger; + int protocol = tcp ? IPPROTO_TCP : IPPROTO_UDP; + + + if (NA_E_EXISTS == GetMappingUnused(eport, protocol)) + return mStatus_AlreadyRegistered; + + //DeletePortMapping(eport, protocol); + + sprintf(szEPort, "%u", eport); + + sprintf(szIPort, "%u", iport); + + dwIP = g_dwLocalIP; + sprintf(szLocalIP, "%u.%u.%u.%u", + (unsigned int)((dwIP >> 24) & 0xff), + (unsigned int)((dwIP >> 16) & 0xff), + (unsigned int)((dwIP >> 8) & 0xff), + (unsigned int)((dwIP >> 0) & 0xff)); + + bzero(propArgs, sizeof(propArgs)); + propArgs[0].pszName = "NewRemoteHost"; + propArgs[0].pszValue = ""; + propArgs[0].pszType = "string"; + propArgs[1].pszName = "NewExternalPort"; + propArgs[1].pszValue = szEPort; + propArgs[1].pszType = "ui2"; + propArgs[2].pszName = "NewProtocol"; + if (protocol == IPPROTO_TCP) { + propArgs[2].pszValue = "TCP"; + } + else if (protocol == IPPROTO_UDP) { + propArgs[2].pszValue = "UDP"; + } + else { + return mStatus_BadParamErr; + } + propArgs[2].pszType = "string"; + propArgs[3].pszName = "NewInternalPort"; + propArgs[3].pszValue = szIPort; + propArgs[3].pszType = "ui2"; + propArgs[4].pszName = "NewInternalClient"; + propArgs[4].pszValue = szLocalIP; + propArgs[4].pszType = "string"; + propArgs[5].pszName = "NewEnabled"; + propArgs[5].pszValue = "1"; + propArgs[5].pszType = "boolean"; + propArgs[6].pszName = "NewPortMappingDescription"; + sprintf(descr, "iC%u", eport); + //propArgs[6].pszValue = "V"; + propArgs[6].pszValue = descr; + propArgs[6].pszType = "string"; + propArgs[7].pszName = "NewLeaseDuration"; + propArgs[7].pszValue = "0"; + propArgs[7].pszType = "ui4"; + + resp = SendSOAPMsgControlAction( + "AddPortMapping", 8, propArgs, FALSE); + + if (resp == NULL) { + return mStatus_NATTraversal; + } + + if (strcmp(resp->pszStatus, "200") != 0) { + DeleteHTTPResponse(resp); + return mStatus_NATTraversal; + } + + DeleteHTTPResponse(resp); + return mStatus_NoError; +} + +static int GetMappingUnused(unsigned short eport, int protocol) +{ + char buf[1024]; + char szPort[10]; + Property propArgs[3]; + PHTTPResponse resp; + unsigned long ip; + + sprintf( szPort, "%u", eport); + + bzero(&propArgs, sizeof(propArgs)); + propArgs[0].pszName = "NewRemoteHost"; + propArgs[0].pszValue = ""; + propArgs[0].pszType = "string"; + propArgs[1].pszName = "NewExternalPort"; + propArgs[1].pszValue = szPort; + propArgs[1].pszType = "ui2"; + propArgs[2].pszName = "NewProtocol"; + if (protocol == IPPROTO_TCP) { + propArgs[2].pszValue = "TCP"; + } + else if (protocol == IPPROTO_UDP) { + propArgs[2].pszValue = "UDP"; + } + else { + return NA_E_INVALID_PARAMETER; + } + propArgs[2].pszType = "string"; + + resp = SendSOAPMsgControlAction( + "GetSpecificPortMappingEntry", 3, propArgs, FALSE); + if (resp != NULL) { + if ((strcmp(resp->pszStatus, "200") == 0) && + (FindTagContent(resp->pszBody, "NewInternalClient", buf) == 0)) + { + GetIPByName(buf, &ip); + if (ip == g_dwLocalIP) { + // (perhaps we let it go?) + DeleteHTTPResponse(resp); + return NA_E_SUCCESS; + } + else { + DeleteHTTPResponse(resp); + return NA_E_EXISTS; + } + } + DeleteHTTPResponse(resp); + } + + return NA_E_SUCCESS; +} + +mStatus LNT_GetPublicIP(mDNSOpaque32 *IpPtr) +{ + char buf[1024]; + PHTTPResponse resp; + static struct timeval tvLastGoodIP = {0,0}; + static unsigned long dwLastGoodIP; + struct timeval tv; + unsigned long *ip = (unsigned long *)IpPtr; + if (ip == NULL) return mStatus_BadParamErr; + + gettimeofday(&tv, NULL); + GetTimeElapsed(&tvLastGoodIP, &tv, &tv); + if (tv.tv_sec < 4) + { + return dwLastGoodIP; + } + + resp = SendSOAPMsgControlAction( + "GetExternalIPAddress", 0, NULL, FALSE); + + if (resp == NULL) + return mStatus_NATTraversal; + + if (FindTagContent(resp->pszBody, "NewExternalIPAddress", buf) == 0) { + if (g_fLogging & NALOG_INFO1) + fprintf(g_log, "Mapped remote host = %s\n", buf); + *ip = inet_addr(buf); + DeleteHTTPResponse(resp); + + gettimeofday(&tvLastGoodIP, NULL); + dwLastGoodIP = *ip; + + return mStatus_NoError; + } + + DeleteHTTPResponse(resp); + return mStatus_NATTraversal; +} + +static void SendDiscoveryMsg() +{ + // do it twice to avoid lost packet + //SendUDPMsg(szSSDPMsgDiscoverNAT); + SendUDPMsg(szSSDPMsgDiscoverRoot); + SendUDPMsg(szSSDPMsgDiscoverIGD); + SendUDPMsg(szSSDPMsgDiscoverNAT); +} + +// Set up threads for upnp responses, etc. +int LegacyNATInit(void) +{ + //pthread_t UDPthread; + pthread_attr_t attr; + int iRet; + //struct timeval tv; + + static int fFirstInitLocks = TRUE; + FILE *log = NULL; + + g_fLogging = 0; + g_log = stderr; + + SetLocalIP(); + + g_fQuit = FALSE; + + if (fFirstInitLocks) + { + // init locks + if (pthread_mutex_init(&g_xUPnP, NULL)) { + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - mutex init failed\n"); + return NA_E_INTERNAL_ERROR; + } + if (pthread_cond_init(&g_condUPnP, NULL)) { + pthread_mutex_destroy(&g_xUPnP); + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - cond init failed\n"); + return NA_E_INTERNAL_ERROR; + } + if (pthread_cond_init(&g_condUPnPControlURL, NULL)) { + pthread_mutex_destroy(&g_xUPnP); + pthread_cond_destroy(&g_condUPnP); + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - cond init failed\n"); + return NA_E_INTERNAL_ERROR; + } + if (pthread_mutex_init(&g_xUPnPMsg, NULL)) { + pthread_mutex_destroy(&g_xUPnP); + pthread_cond_destroy(&g_condUPnP); + pthread_cond_destroy(&g_condUPnPControlURL); + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - mutex init failed\n"); + return NA_E_INTERNAL_ERROR; + } + + fFirstInitLocks = FALSE; + } + + if (g_fFirstInit) + { + // initialize UDP socket for SSDP + g_sUDP = SSDPListen(); + g_sUDPCancel = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // sock to signal canccelation to UDP thread + if (g_sUDP < 0 || g_sUDPCancel < 0) { + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - Failed to init multicast socket.\n"); + return NA_E_INTERNAL_ERROR; + } + + // make UDP thread + pthread_attr_init(&attr); + iRet = pthread_create(&g_UDPthread, &attr, UDPProc, log); + if (iRet != 0) { + g_fFirstInit = TRUE; // so we'll redo this part next time + close(g_sUDP); + g_sUDP = -1; + if (g_fLogging & NALOG_ERROR) + fprintf(log, "UpnpInit - pthread create failed (%d)\n", iRet); + return NA_E_THREAD_ERROR; + } + + // set this to FALSE only if first call succeeded + g_fFirstInit = FALSE; + + //TracePrint(ELL_TRACE, "UPnP init passed\n"); + + //tv.tv_sec = 0; + //tv.tv_usec = 20000; // wait 20ms for thread/udp/multicast init + //select(0, 0, 0, 0, &tv); + } + + // send discovery message + SendDiscoveryMsg(); + + return NA_E_SUCCESS; +} + +int LegacyNATDestroy() +{ + void *UDPThreadRetVal; + g_fQuit = TRUE; + if (g_sTCPCancel >= 0) close(g_sTCPCancel); + if (g_sUDPCancel >= 0) close(g_sUDPCancel); + pthread_join(g_UDPthread, &UDPThreadRetVal); + g_sTCPCancel = -1; + g_sUDPCancel = -1; + g_fFirstInit = TRUE; + g_fUPnPEnabled = FALSE; + g_fControlURLSet = FALSE; + return NA_E_SUCCESS; +} diff --git a/mDNSMacOSX/SamplemDNSClient.c b/mDNSMacOSX/SamplemDNSClient.c index 430d0e6..a387269 100644 --- a/mDNSMacOSX/SamplemDNSClient.c +++ b/mDNSMacOSX/SamplemDNSClient.c @@ -3,8 +3,6 @@ * * @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 @@ -38,6 +36,12 @@ Change History (most recent first): $Log: SamplemDNSClient.c,v $ +Revision 1.46 2004/11/02 01:32:34 cheshire + Update code so it still compiles when DNSServiceDiscovery.h is deprecated + +Revision 1.45 2004/06/15 02:39:47 cheshire +When displaying error message, only show command name, not entire path + 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" @@ -80,6 +84,12 @@ Add checkin history header #include #include #include + +// We already know this tool is using the old deprecated API (that's its purpose) +// Since we compile with all warnings treated as errors, we have to turn off the warnings here or the project won't compile +#include +#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED +#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED #include //************************************************************************************************************* @@ -322,6 +332,7 @@ static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *cont int main(int argc, char **argv) { + const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; char *dom; setlinebuf(stdout); // Want to see lines as they appear, not block buffered @@ -443,16 +454,16 @@ Exit: return 0; Fail: - fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", argv[0]); - fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", argv[0]); - fprintf(stderr, "%s -B (Browse for services instances)\n", argv[0]); - fprintf(stderr, "%s -L (Look up a service instance)\n", argv[0]); - fprintf(stderr, "%s -R [...] (Register a service)\n", argv[0]); - fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", argv[0]); - fprintf(stderr, "%s -U (Test updating a TXT record)\n", argv[0]); - fprintf(stderr, "%s -N (Test adding a large NULL record)\n", argv[0]); - fprintf(stderr, "%s -T (Test creating a large TXT record)\n", argv[0]); - fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", argv[0]); - fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", argv[0]); + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", progname); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", progname); + fprintf(stderr, "%s -B (Browse for services instances)\n", progname); + fprintf(stderr, "%s -L (Look up a service instance)\n", progname); + fprintf(stderr, "%s -R [...] (Register a service)\n", progname); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", progname); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", progname); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", progname); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", progname); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", progname); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", progname); return 0; } diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 9cb6a47..e82657e 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -3,8 +3,6 @@ * * @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 @@ -38,6 +36,177 @@ Change History (most recent first): $Log: daemon.c,v $ +Revision 1.227 2004/12/10 13:52:57 cheshire + Turn off SIGPIPE signals + +Revision 1.226 2004/12/10 05:27:26 cheshire + Guard against multiple autoname services of the same type on the same machine + +Revision 1.225 2004/12/10 04:28:29 cheshire + User not notified of name changes for services using new UDS API + +Revision 1.224 2004/12/10 00:41:05 cheshire +Adjust alignment of log messages + +Revision 1.223 2004/12/07 20:42:34 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.222 2004/12/06 21:15:23 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.221 2004/11/30 03:24:04 cheshire + Defer processing network configuration changes until configuration has stabilized + +Revision 1.220 2004/11/29 23:34:31 cheshire +On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero +is crude, and effectively halves the time resolution. The more selective NonZeroTime() function +only nudges the time value to 1 if the interval calculation happens to result in the value zero. + +Revision 1.219 2004/11/25 01:00:56 cheshire +Checkin 1.217 not necessary + +Revision 1.218 2004/11/24 20:27:19 cheshire +Add missing "err" parameter in LogMsg() call + +Revision 1.217 2004/11/24 17:55:01 ksekar +Added log message clarifying For unicast operations, verify that service types are legal + +Revision 1.216 2004/11/24 00:10:44 cheshire + For unicast operations, verify that service types are legal + +Revision 1.215 2004/11/23 22:33:01 cheshire + Remove temporary workaround code for iChat + +Revision 1.214 2004/11/23 22:13:59 cheshire + Subtype advertising broken for Mach API + +Revision 1.213 2004/11/23 06:12:55 cheshire + Update wording for name conflict dialogs + +Revision 1.212 2004/11/23 05:15:37 cheshire + Computer Name in use message garbled + +Revision 1.211 2004/11/23 05:00:41 cheshire + Name conflict log message should not have ".local" appended + +Revision 1.210 2004/11/03 03:45:17 cheshire + mDNSResponder does not inform user of Computer Name collisions + +Revision 1.209 2004/11/03 02:25:50 cheshire + Conflict for Computer Name should update *all* empty string services, not just the one with the conflict + +Revision 1.208 2004/11/03 01:54:14 cheshire +Update debugging messages + +Revision 1.207 2004/11/02 23:58:19 cheshire + mDNSResponder does not inform user of name collisions + +Revision 1.206 2004/10/28 02:40:47 cheshire +Add log message to confirm receipt of SIGUSR1 (simulate network configuration change event) + +Revision 1.205 2004/10/28 02:21:01 cheshire + Improve mDNSResponder signal handling +Added SIGHUP as a way to do a forced restart of the daemon (better than kill -9) +Added SIGUSR1 to simulate a network change notification from System Configuration Framework + +Revision 1.204 2004/10/27 01:57:21 cheshire +Add check of m->p->InterfaceList + +Revision 1.203 2004/10/26 04:31:44 cheshire +Rename CountSubTypes() as ChopSubTypes() + +Revision 1.202 2004/10/26 01:29:18 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.201 2004/10/25 21:41:39 ksekar + wide-area name conflicts can cause crash + +Revision 1.200 2004/10/22 01:03:55 cheshire + select() says data is waiting; recvfrom() says there is no data +Log error message if attempt to remap stdin/stdout/stderr to /dev/null fails + +Revision 1.199 2004/10/19 21:33:19 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.198 2004/10/15 23:00:18 ksekar + Need to update LLQs on location changes + +Revision 1.197 2004/10/12 23:38:59 ksekar + remove unnecessary log message + +Revision 1.196 2004/10/04 05:56:04 cheshire + mDNSResponder doesn't respond to certain AirPort changes + +Revision 1.195 2004/09/30 00:24:59 ksekar + Dynamically update default registration domains on config change + +Revision 1.194 2004/09/26 23:20:35 ksekar + Allow default registrations in multiple wide-area domains + +Revision 1.193 2004/09/23 23:35:27 cheshire +Update error message + +Revision 1.192 2004/09/21 23:40:12 ksekar + mDNSResponder to return errors on NAT traversal failure + +Revision 1.191 2004/09/21 21:05:12 cheshire +Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c, +into mDNSShared/uds_daemon.c + +Revision 1.190 2004/09/21 19:51:15 cheshire +Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c + +Revision 1.189 2004/09/21 18:17:23 cheshire + Add version info to mDNSResponder + +Revision 1.188 2004/09/20 21:45:27 ksekar +Mach IPC cleanup + +Revision 1.187 2004/09/17 01:08:52 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.186 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.185 2004/08/25 02:01:45 cheshire + Need to be able to get status of Dynamic DNS Host Name Update + +Revision 1.184 2004/08/19 19:04:12 ksekar +: mDNSResponder crashes when adding a record to a service + +Revision 1.183 2004/08/14 03:22:42 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.182 2004/08/13 23:57:59 cheshire +Get rid of non-portable "_UNUSED" + +Revision 1.181 2004/08/11 02:02:26 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init(); +Move CheckForDuplicateRegistrations to uds_daemon.c + +Revision 1.180 2004/07/13 21:24:25 rpantos +Fix for . + +Revision 1.179 2004/06/19 00:02:54 cheshire +Restore fix for Should not allow empty string for resolve domain + +Revision 1.178 2004/06/18 19:10:00 cheshire + Current method of doing subtypes causes name collisions + +Revision 1.177 2004/06/16 23:14:46 ksekar + Remove fix for Should not allow empty string for resolve domain + +Revision 1.176 2004/06/11 20:27:42 cheshire +Rename "SocketRef" as "cfs" to avoid conflict with other plaforms + Revision 1.175 2004/06/10 20:23:21 cheshire Also list interfaces in SIGINFO output @@ -77,7 +246,7 @@ Unified list copy/free code. Added symetric list for 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" +from mDNSMacOSX.h to mDNSEmbeddedAPI.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. @@ -251,10 +420,10 @@ standard error file descriptors to /dev/null just like any other well behaved daemon Revision 1.112 2003/06/25 23:42:19 ksekar -: Feature: New Rendezvous APIs (#7875) +: Feature: New DNS-SD 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. +DNS-SD APIs, and integrated with existing Mach-port based daemon. Revision 1.111 2003/06/11 01:02:43 cheshire mDNSResponder binary compatibility @@ -336,11 +505,12 @@ Add $Log header #include #include #include +#include #include "DNSServiceDiscoveryRequestServer.h" #include "DNSServiceDiscoveryReply.h" -#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h @@ -355,26 +525,21 @@ Add $Log header // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" // To expand "version" to its value before making the string, use STRINGIFY(version) instead -#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s +#define STRINGIFY_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 kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart"; static mach_port_t client_death_port = MACH_PORT_NULL; -static mach_port_t exit_m_port = MACH_PORT_NULL; -static mach_port_t info_m_port = MACH_PORT_NULL; +static mach_port_t signal_port = MACH_PORT_NULL; static mach_port_t server_priv_port = MACH_PORT_NULL; // mDNS Mach Message Timeout, in milliseconds. @@ -411,17 +576,20 @@ typedef struct DNSServiceBrowser_struct DNSServiceBrowser; typedef struct DNSServiceBrowserQuestion { - struct DNSServiceBrowserQuestion *next; - DNSQuestion q; + struct DNSServiceBrowserQuestion *next; + DNSQuestion q; + domainname domain; } DNSServiceBrowserQuestion; struct DNSServiceBrowser_struct { DNSServiceBrowser *next; mach_port_t ClientMachPort; - DNSServiceBrowserQuestion *qlist; + DNSServiceBrowserQuestion *qlist; DNSServiceBrowserResult *results; mDNSs32 lastsuccess; + mDNSBool DefaultDomain; // was the browse started on an explicit domain? + domainname type; // registration type }; typedef struct DNSServiceResolver_struct DNSServiceResolver; @@ -434,33 +602,41 @@ struct DNSServiceResolver_struct mDNSs32 ReportTime; }; - -typedef struct ExtraRecordRef +// A single registered service: ServiceRecordSet + bookkeeping +// Note that we duplicate some fields from parent DNSServiceRegistration object +// to facilitate cleanup, when instances and parent may be deallocated at different times. +typedef struct ServiceInstance { - 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; + struct ServiceInstance *next; mach_port_t ClientMachPort; - mDNSBool autoname; - 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; - ExtraRecordRef *ExtraRefList; - ServiceRecordSet *gs; // default "global" (wide area) service (may be NULL) - ServiceRecordSet ls; // .local service (also used if client passes an explicit domain) + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool autorename; // Set if we just got a name conflict and now need to automatically pick a new name + domainlabel name; + domainname domain; + ServiceRecordSet srs; // Don't add any fields after ServiceRecordSet. // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object - }; + } ServiceInstance; + +// A client-created service. May reference several ServiceInstance objects if default +// settings cause registration in multiple domains. +typedef struct DNSServiceRegistration + { + struct DNSServiceRegistration *next; + mach_port_t ClientMachPort; + mDNSBool DefaultDomain; + mDNSBool autoname; + size_t rdsize; + int NumSubTypes; + char regtype[MAX_ESCAPED_DOMAIN_NAME]; // for use in AllocateSubtypes + domainlabel name; // used only if autoname is false + domainname type; + mDNSIPPort port; + unsigned char txtinfo[1024]; + size_t txt_len; + uint32_t NextRef; + ServiceInstance *regs; + } DNSServiceRegistration; static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL; static DNSServiceBrowser *DNSServiceBrowserList = NULL; @@ -484,7 +660,8 @@ static void validatelists(mDNS *const m) CacheRecord *cr; DNSQuestion *q; mDNSu32 slot; - + NetworkInterfaceInfoOSX *i; + for (e = DNSServiceDomainEnumerationList; e; e=e->next) if (e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t)~0) LogMsg("!!!! DNSServiceDomainEnumerationList: %p is garbage (%X) !!!!", e, e->ClientMachPort); @@ -514,9 +691,13 @@ static void validatelists(mDNS *const m) LogMsg("!!!! Questions list: %p is garbage (%lX) !!!!", q, q->ThisQInterval); for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) - for (cr = mDNSStorage.rrcache_hash[slot]; cr; cr=cr->next) + for (cr = m->rrcache_hash[slot]; cr; cr=cr->next) if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF) LogMsg("!!!! Cache slot %lu: %p is garbage (%X) !!!!", slot, rr, rr->resrec.RecordType); + + for (i = m->p->InterfaceList; i; i = i->next) + if (!i->ifa_name) + LogMsg("!!!! InterfaceList: %p is garbage !!!!", i); } void *mallocL(char *msg, unsigned int size) @@ -525,7 +706,7 @@ void *mallocL(char *msg, unsigned int size) if (!mem) { LogMsg("malloc( %s : %d ) failed", msg, size); - return(NULL); + return(NULL); } else { @@ -563,46 +744,26 @@ void freeL(char *msg, void *x) //************************************************************************************************************* // Client Death Detection -mDNSlocal void FreeSRS(ServiceRecordSet *s) +mDNSlocal void FreeServiceInstance(ServiceInstance *x) { - while (s->Extras) + ServiceRecordSet *s = &x->srs; + ExtraResourceRecord *e = x->srs.Extras, *tmp; + + while(e) { - 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); + e->r.RecordContext = e; + tmp = e; + e = e->next; + FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree); } - + if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage) freeL("TXT RData", s->RR_TXT.resrec.rdata); - if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes); + if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes); + freeL("ServiceInstance", x); } -mDNSlocal void FreeDNSServiceRegistration(ServiceRecordSet *srs) - { - DNSServiceRegistration *x = srs->ServiceContext; - ExtraRecordRef *ref, *fptr; - - 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 @@ -631,7 +792,7 @@ 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; while (qptr) @@ -641,7 +802,7 @@ mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) else LogOperation("%5d: DNSServiceBrowser(%##s) STOP", ClientMachPort, qptr->q.qname.c); mDNS_StopBrowse(&mDNSStorage, &qptr->q); freePtr = qptr; - qptr = qptr->next; + qptr = qptr->next; freeL("DNSServiceBrowserQuestion", freePtr); } while (x->results) @@ -670,32 +831,27 @@ mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next; if (*r) { + ServiceInstance *si = NULL; DNSServiceRegistration *x = *r; *r = (*r)->next; - x->autorenameLS = mDNSfalse; - x->autorenameGS = mDNSfalse; - if (m && m != x) - { - 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 (x->gs && mDNS_DeregisterService(&mDNSStorage, x->gs)) + + si = x->regs; + while (si) { - // Deregister returned an error, so we free immediately - FreeSRS(x->gs); - x->gs = NULL; + ServiceInstance *instance = si; + si = si->next; + instance->autorename = mDNSfalse; + if (m && m != x) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, instance->srs.RR_SRV.resrec.name.c, SRS_PORT(&instance->srs), m, x); + else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, instance->srs.RR_SRV.resrec.name.c, SRS_PORT(&instance->srs)); + + // 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, &instance->srs)) FreeServiceInstance(instance); // FreeServiceInstance invalidates pointer } - if (mDNS_DeregisterService(&mDNSStorage, &x->ls)) - FreeDNSServiceRegistration(&x->ls); + x->regs = NULL; + freeL("DNSServiceRegistration", x); return; } @@ -725,8 +881,8 @@ mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *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); + ServiceInstance *si; + for (si = r->regs; si; si = si->next) LogMsg("%5d: Registration(%##s) %s%s", c, si->srs.RR_SRV.resrec.name.c, reason, msg); } else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg); @@ -740,7 +896,7 @@ mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c) 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; @@ -752,7 +908,7 @@ mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c) 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->ls.RR_SRV.resrec.name.c); + if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->regs ? r->regs->srs.RR_SRV.resrec.name.c : NULL); return(e || b || l || r); } @@ -841,7 +997,7 @@ mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port x->ClientMachPort = client; x->next = DNSServiceDomainEnumerationList; DNSServiceDomainEnumerationList = x; - + // Generate initial response verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing"); // We always give local. as the initial default browse domain, and then look for more @@ -853,7 +1009,7 @@ mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port 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 LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c); EnableDeathNotificationForClient(client, x); @@ -870,10 +1026,10 @@ fail: mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { (void)m; // Unused - + if (answer->rrtype != kDNSType_PTR) { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; } - + domainlabel name; domainname type, domain; if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain)) @@ -885,7 +1041,7 @@ mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const Resourc DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x)); if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; } - + verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c); AssignDomainName(x->result, answer->rdata->u.name); if (AddRecord) @@ -899,6 +1055,72 @@ mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const Resourc *p = x; } +mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainname *d) + { + mStatus err = mStatus_NoError; + DNSServiceBrowserQuestion *ptr, *question = NULL; + + for (ptr = browser->qlist; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->q.qname, d)) + { debugf("Domain %##s already contained in browser", d->c); return mStatus_AlreadyRegistered; } + } + + question = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion)); + if (!question) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr; } + AssignDomainName(question->domain, *d); + question->next = browser->qlist; + browser->qlist = question; + LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c); + err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, mDNSfalse, FoundInstance, browser); + if (err) LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err); + return err; + } + +mDNSexport void DefaultBrowseDomainChanged(const domainname *d, mDNSBool add) + { + DNSServiceBrowser *ptr; + + debugf("%s default browse domain %##s", add ? "Adding" : "Removing", d->c); + for (ptr = DNSServiceBrowserList; ptr; ptr = ptr->next) + { + if (ptr->DefaultDomain) + { + if (add) + { + mStatus err = AddDomainToBrowser(ptr, d); + if (err && err != mStatus_AlreadyRegistered) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d, ptr->ClientMachPort); + } + else + { +#if 0 + /* + * By cancelling the browse immediately, we may lose remove events. + * Instead, we allow the browse to run. If our previous results are no longer valid (e.g. because we + * moved out from behind a firewall) we will get remove events for those names. + */ + // find the question for this domain + DNSServiceBrowserQuestion *q = ptr->qlist, *prev = NULL; + while (q) + { + if (SameDomainName(&q->domain, d)) + { + if (prev) prev->next = q->next; + else ptr->qlist = q->next; + mDNS_StopBrowse(&mDNSStorage, &q->q); + freeL("DNSServiceBrowserQuestion", q); + break; + } + prev = q; + q = q->next; + } + if (!q) LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort); +#endif + } + } + } + } + mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client, DNSCString regtype, DNSCString domain) { @@ -907,76 +1129,79 @@ mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unuseds 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; - if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; } + t.c[0] = 0; + mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes + 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; } + domainname temp; + if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { errormsg = "Illegal regtype"; goto badparam; } + if (temp.c[0] > 15 && (!domain || domain[0] == 0)) domain = "local."; // For over-long service types, we only allow domain "local" // 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 + AssignDomainName(x->type, t); x->ClientMachPort = client; x->results = NULL; x->lastsuccess = 0; - x->qlist = NULL; + x->qlist = NULL; x->next = DNSServiceBrowserList; DNSServiceBrowserList = x; - //!!!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; } + x->DefaultDomain = mDNSfalse; + if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; } + err = AddDomainToBrowser(x, &d); + if (err) { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; } } else { // Start browser on all domains + x->DefaultDomain = mDNStrue; 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; } - } + err = AddDomainToBrowser(x, &sdPtr->name); + if (err) + { + // only terminally bail if .local fails + if (!SameDomainName(&localdomain, &sdPtr->name)) + LogMsg("Default browse in domain %##s failed. Continuing", sdPtr->name.c); + else { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; } + } + } } + // Succeeded: Wrap up and return EnableDeathNotificationForClient(client, x); mDNS_FreeDNameList(SearchDomains); return(mStatus_NoError); - + 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); - } + } //************************************************************************************************************* // Resolve Service Info - -mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) + + mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) { kern_return_t status; DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext; @@ -1013,7 +1238,7 @@ mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6; sin6->sin6_scope_id = ifx->scope_id; } - + if (query->info->ip.type == mDNSAddrType_IPv4) { struct sockaddr_in *sin = (struct sockaddr_in*)&address; @@ -1049,7 +1274,7 @@ mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) } } cstring[i-1] = 0; // Put the terminating NULL on the end - + LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort, x->i.name.c, &query->info->ip, mDNSVal16(query->info->port)); status = DNSServiceResolverReply_rpc(x->ClientMachPort, @@ -1084,11 +1309,7 @@ mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unuse x->ClientMachPort = client; x->i.InterfaceID = mDNSInterface_Any; x->i.name = srv; - x->ReportTime = (mDNSPlatformTimeNow() + 130 * mDNSPlatformOneSecond) | 1; - // Don't report errors for old iChat ("_ichat._tcp") service. - // New iChat ("_presence._tcp") uses DNSServiceQueryRecord() (from /usr/include/dns_sd.h) instead, - // and so should other applications that have valid reasons to be doing ongoing record monitoring. - if (SameDomainLabel(t.c, (mDNSu8*)"\x6_ichat")) x->ReportTime = 0; + x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond); x->next = DNSServiceResolverList; DNSServiceResolverList = x; @@ -1101,7 +1322,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); @@ -1111,113 +1332,186 @@ fail: //************************************************************************************************************* // Registration -mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) +mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) + { + m->p->NotifyUser = NonZeroTime(m->timenow + delay); + } + +mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) { - DNSServiceRegistration *x = (DNSServiceRegistration*)sr->ServiceContext; + ServiceInstance *si = (ServiceInstance*)srs->ServiceContext; if (result == mStatus_NoError) { kern_return_t status; - 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); + LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si->ClientMachPort, srs->RR_SRV.resrec.name.c, SRS_PORT(srs)); + status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT); if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "registration success", x); + AbortBlockedClient(si->ClientMachPort, "registration success", si); + if (si->autoname && CountPeerRegistrations(m, srs) == 0) + RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately } else if (result == mStatus_NameConflict) { - LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); + LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si->ClientMachPort, srs->RR_SRV.resrec.name.c, SRS_PORT(srs)); // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well. - if (x->autoname) - mDNS_RenameAndReregisterService(m, sr, mDNSNULL); + if (si->autoname && CountPeerRegistrations(m, srs) == 0) + { + // On conflict for an autoname service, rename and reregister *all* autoname services + IncrementLabelSuffix(&m->nicelabel, mDNStrue); + m->MainCallback(m, mStatus_ConfigChanged); + } + else if (si->autoname) + { + mDNS_RenameAndReregisterService(m, srs, mDNSNULL); + return; + } else { // If we get a name conflict, we tell the client about it, and then they are expected to dispose // of their registration in the usual way (which we will catch via client death notification). // If the Mach queue is full, we forcibly abort the client immediately. - kern_return_t status = DNSServiceRegistrationReply_rpc(x->ClientMachPort, result, MDNS_MM_TIMEOUT); + kern_return_t status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT); if (status == MACH_SEND_TIMED_OUT) - AbortBlockedClient(x->ClientMachPort, "registration conflict", x); + AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL); } } - + else if (result == mStatus_MemFree) { - mDNSBool *autorename = (sr == &x->ls ? &x->autorenameLS : &x->autorenameGS); - if (*autorename) + if (si->autorename) { - debugf("RegCallback renaming %#s to %#s", x->name.c, mDNSStorage.nicelabel.c); - *autorename = mDNSfalse; - x->name = mDNSStorage.nicelabel; - mDNS_RenameAndReregisterService(m, sr, &x->name); + debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c); + si->autorename = mDNSfalse; + si->name = m->nicelabel; + mDNS_RenameAndReregisterService(m, srs, &si->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) + // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list + DNSServiceRegistration *r; + for (r = DNSServiceRegistrationList; r; r = r->next) { - LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", sr->RR_SRV.resrec.name.c); - *r = (*r)->next; - } + ServiceInstance *sp = r->regs, *prev = NULL; + while (sp) + { + if (sp == si) + { + LogMsg("RegCallback: %##s Still in DNSServiceRegistration list; removing now", srs->RR_SRV.resrec.name.c); + if (prev) prev->next = sp->next; + else r->regs = sp->next; + break; + } + prev = sp; + sp = sp->next; + } + } // END SANITY CHECK - LogOperation("%5d: DNSServiceRegistration(%##s, %u) Memory Free", x->ClientMachPort, sr->RR_SRV.resrec.name.c, SRS_PORT(sr)); - FreeDNSServiceRegistration(sr); + FreeServiceInstance(si); } } + + else if (result != mStatus_NATTraversal) + LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %ld", si->ClientMachPort, srs->RR_SRV.resrec.name.c, SRS_PORT(srs), result); + } + +mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname *domain) + { + mStatus err = 0; + ServiceInstance *si = NULL; + AuthRecord *SubTypes = NULL; + + for (si = x->regs; si; si = si->next) + { + if (SameDomainName(&si->domain, domain)) + { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; } + } + SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype); + if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr; + + si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize); + if (!si) return mStatus_NoMemoryErr; + + si->ClientMachPort = x->ClientMachPort; + si->autorename = mDNSfalse; + si->autoname = x->autoname; + si->name = x->autoname ? mDNSStorage.nicelabel : x->name; + si->domain = *domain; + + err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL, x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si); + if (!err) + { + si->next = x->regs; + x->regs = si; + } else { - 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; } + LogMsg("Error %d for registration of service in domain %##s", err, domain->c); + freeL("ServiceInstance", si); } + return err; } -mDNSlocal void CheckForDuplicateRegistrations(DNSServiceRegistration *x, domainname *srv, mDNSIPPort port) +mDNSexport void DefaultRegDomainChanged(const domainname *d, mDNSBool add) { - int count = 1; // Start with the one we're planning to register, then see if there are any more - AuthRecord *rr; - for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) - if (rr->resrec.rrtype == kDNSType_SRV && - rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger && - SameDomainName(&rr->resrec.name, srv)) - count++; - - if (count > 1) - LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.", - x->ClientMachPort, count, srv->c, mDNSVal16(port)); + DNSServiceRegistration *reg; + + LogMsg("%s default registration domain %##s", add ? "Adding" : "Removing", d->c); + for (reg = DNSServiceRegistrationList; reg; reg = reg->next) + { + if (reg->DefaultDomain) + { + if (add) + { + AddServiceInstance(reg, d); + } + else + { + ServiceInstance *si = reg->regs, *prev = NULL; + while (si) + { + if (SameDomainName(&si->domain, d)) + { + if (prev) prev->next = si->next; + else reg->regs = si->next; + if (mDNS_DeregisterService(&mDNSStorage, &si->srs)) + FreeServiceInstance(si); // only free memory synchronously on error + break; + } + prev = si; + si = si->next; + } + if (!si) LogMsg("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); + } + } + } } -// 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) +mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client, + DNSCString name, DNSCString regtype, DNSCString domain, IPPort IpPort, DNSCString txtRecord) { - ServiceRecordSet *srs = NULL; // record set to use in registration operation + (void)unusedserver; // Unused mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; - + + // older versions of this code passed the port via mach IPC as an int. + // we continue to pass it as 4 bytes to maintain binary compatibility, + // but now ensure that the network byte order is preserved by using a struct + mDNSIPPort port; + port.b[0] = IpPort.bytes[2]; + port.b[1] = IpPort.bytes[3]; + + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + // Check for sub-types after the service type - AuthRecord *SubTypes = mDNSNULL; - mDNSu32 i, NumSubTypes = 0; - char *comma = regtype; - while (*comma && *comma != ',') comma++; - if (*comma) // If we found a comma... - { - *comma = 0; // Overwrite the first comma with a nul - char *p = comma + 1; // Start scanning from the next character - while (*p) - { - if ( !(*p && *p != ',')) { errormsg = "Bad Service SubType"; goto badparam; } - while (*p && *p != ',') p++; - if (*p) *p++ = 0; - NumSubTypes++; - } - } + size_t reglen = strlen(regtype) + 1; + if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; } + mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; } // Check other parameters domainlabel n; @@ -1225,14 +1519,10 @@ mDNSlocal DNSServiceRegistration *RegisterService(mach_port_t client, DNSCString 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)) { errormsg = "Bad Domain"; goto badparam; } + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; } if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } - mDNSIPPort port; - port.NotAnInteger = notAnIntPort; - unsigned char txtinfo[1024] = ""; unsigned int data_len = 0; unsigned int size = sizeof(RDataBody); @@ -1264,116 +1554,148 @@ mDNSlocal DNSServiceRegistration *RegisterService(mach_port_t client, DNSCString if (size < data_len) size = data_len; - // Allocate memory, and handle failure - 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 + // 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) { - 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); + int count = CountExistingRegistrations(&srv, port); + if (count) + LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.", + client, count+1, srv.c, mDNSVal16(port)); } - 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 - } - } + // Allocate memory, and handle failure + DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + bzero(x, sizeof(*x)); + + // Set up object, and link into list + x->ClientMachPort = client; + x->DefaultDomain = !domain[0]; + x->autoname = (!name[0]); + x->rdsize = size; + x->NumSubTypes = NumSubTypes; + memcpy(x->regtype, regtype, reglen); + x->name = n; + x->type = t; + x->port = port; + memcpy(x->txtinfo, txtinfo, 1024); + x->txt_len = data_len; + x->NextRef = 0; + x->regs = NULL; + + x->next = DNSServiceRegistrationList; + DNSServiceRegistrationList = x; - // Do the operation 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, 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 + err = AddServiceInstance(x, &d); + if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } // bail if .local (or explicit domain) fails - if (err) + if (x->DefaultDomain) { - 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; - + DNameListElem *ptr, *regdomains = mDNSPlatformGetRegDomainList(); + for (ptr = regdomains; ptr; ptr = ptr->next) + AddServiceInstance(x, &ptr->name); + mDNS_FreeDNameList(regdomains); + } + + // 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 NULL; + client, name, regtype, domain, mDNSVal16(port), errormsg, err); + return(err); } -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) +// This updates either the text of the field currently labelled "Local Hostname", +// or the text of the field currently labelled "Computer Name" +// in the Sharing Prefs Control Panel +mDNSlocal void RecordUpdatedName(const mDNS *const m, domainlabel *n1, domainlabel *n2, char *msg, char *suffix, CFStringRef subtext) { - // 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); - -fail: - LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%ld)", - client, name, regtype, domain, notAnIntPort, errormsg, err); - return mStatus_UnknownErr; + char oldname[MAX_DOMAIN_LABEL+1]; + char newname[MAX_DOMAIN_LABEL+1]; + ConvertDomainLabelToCString_unescaped(n1, oldname); + ConvertDomainLabelToCString_unescaped(n2, newname); + const CFStringRef cfoldname = CFStringCreateWithCString(NULL, oldname, kCFStringEncodingUTF8); + const CFStringRef cfnewname = CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8); + const CFStringRef f1 = CFStringCreateWithCString(NULL, "“%@%s”", kCFStringEncodingUTF8); + const CFStringRef f2 = CFStringCreateWithCString(NULL, "“%@%s”", kCFStringEncodingUTF8); + const SCPreferencesRef session = SCPreferencesCreate(NULL, CFSTR("mDNSResponder"), NULL); + *n1 = *n2; + if (!cfoldname || !cfnewname || !f1 || !f2 || !session || !SCPreferencesLock(session, 0)) // If we can't get the lock don't wait + LogMsg("RecordUpdatedName: ERROR: Couldn't create SCPreferences session"); + else + { + const CFStringRef s0 = CFStringCreateWithCString(NULL, msg, kCFStringEncodingUTF8); + const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, f1, cfoldname, suffix); + const CFStringRef s2 = CFStringCreateWithFormat(NULL, NULL, f2, cfnewname, suffix); +// const CFMutableArrayRef alertMessage = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + const CFMutableStringRef alertMessage = CFStringCreateMutable(NULL, 0); + Boolean result; + if (n2 == &m->hostlabel) result = SCPreferencesSetLocalHostName(session, cfnewname); + else result = SCPreferencesSetComputerName(session, cfnewname, kCFStringEncodingUTF8); + if (!result || !SCPreferencesCommitChanges(session) || !SCPreferencesApplyChanges(session) || !s0 || !s1 || !s2 || !alertMessage) + LogMsg("RecordUpdatedName: ERROR: Couldn't update SCPreferences"); + else if (m->p->NotifyUser) + { +// CFArrayAppendValue(alertMessage, s0); + CFStringAppend(alertMessage, s0); + CFStringAppend(alertMessage, s1); + CFStringAppend(alertMessage, CFSTR(" is already in use on this network. The name has been changed to ")); + CFStringAppend(alertMessage, s2); + CFStringAppend(alertMessage, CFSTR(" automatically.")); + CFUserNotificationDisplayNotice(60.0, // Auto-dismiss after 60 seconds + kCFUserNotificationCautionAlertLevel, + NULL, NULL, NULL, // iconURL, soundURL, localizationURL + (CFStringRef)alertMessage, subtext, NULL); // alertHeader, alertMessage, defaultButtonTitle + } + if (s0) CFRelease(s0); + if (s1) CFRelease(s1); + if (s2) CFRelease(s2); + if (alertMessage) CFRelease(alertMessage); + SCPreferencesUnlock(session); + } + if (cfoldname) CFRelease(cfoldname); + if (cfnewname) CFRelease(cfnewname); + if (f1) CFRelease(f1); + if (f2) CFRelease(f2); + if (session) CFRelease(session); } - + mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) { (void)m; // Unused - if (result == mStatus_ConfigChanged) + if (result == mStatus_NoError) + { + // Allow three seconds in case we get a Computer Name update too -- don't want to alert the user twice + RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond*3); + } + else if (result == mStatus_ConfigChanged) { DNSServiceRegistration *r; for (r = DNSServiceRegistrationList; r; r=r->next) - if (r->autoname && !SameDomainLabel(r->name.c, mDNSStorage.nicelabel.c)) + if (r->autoname) { - debugf("NetworkChanged renaming %#s to %#s", r->name.c, mDNSStorage.nicelabel.c); - r->autorenameLS = mDNStrue; - mDNS_DeregisterService(&mDNSStorage, &r->ls); - if (r->gs) { mDNS_DeregisterService(&mDNSStorage, r->gs); r->autorenameGS = mDNStrue; } + ServiceInstance *si; + for (si = r->regs; si; si = si->next) + { + if (!SameDomainLabel(si->name.c, m->nicelabel.c)) + { + debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name.c, m->nicelabel.c); + si->autorename = mDNStrue; + if (mDNS_DeregisterService(m, &si->srs)) // If service deregistered already, we can re-register immediately + RegCallback(m, &si->srs, mStatus_MemFree); + } + } } udsserver_handle_configchange(); } @@ -1382,86 +1704,62 @@ mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) // If we've run out of cache space, then double the total cache size and give the memory to mDNSCore mDNSu32 numrecords = m->rrcache_size; CacheRecord *storage = mallocL("mStatus_GrowCache", sizeof(CacheRecord) * numrecords); - if (storage) mDNS_GrowCache(&mDNSStorage, storage, numrecords); + if (storage) mDNS_GrowCache(m, storage, numrecords); } } //************************************************************************************************************* // Add / Update / Remove records from existing Registration - -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) - { - mStatus err = mStatus_NoError; - const char *errormsg = "Unknown"; - domainname *name = (domainname *)""; - name = &srs->RR_SRV.resrec.name; - - (void)x; // unused - - unsigned int size = sizeof(RDataBody); - if (size < data_len) - size = data_len; - - // Allocate memory, and handle failure - ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); - if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } - - // Fill in type, length, and data of new record - extra->r.resrec.rrtype = type; - extra->r.rdatastorage.MaxRDLength = size; - extra->r.resrec.rdlength = data_len; - memcpy(&extra->r.rdatastorage.u.data, data, data_len); - - // Do the operation - LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p", - client, 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; - } - - return extra; - -fail: - LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%ld)", client, name->c, type, data_len, errormsg, err); - return NULL; - } - - 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 + uint32_t id; mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } DNSServiceRegistration *x = DNSServiceRegistrationList; + ServiceInstance *si; + size_t size; + (void)unusedserver; // Unused 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; } + if (data_len > sizeof(RDataBody)) size = data_len; + else size = sizeof(RDataBody); + + id = x->NextRef++; + *reference = (natural_t)id; + for (si = x->regs; si; si = si->next) + { + // Allocate memory, and handle failure + ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); + if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Fill in type, length, and data of new record + extra->r.resrec.rrtype = type; + extra->r.rdatastorage.MaxRDLength = size; + extra->r.resrec.rdlength = data_len; + memcpy(&extra->r.rdatastorage.u.data, data, data_len); + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p", + client, si->srs.RR_SRV.resrec.name.c, type, data_len, extra); + err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl); + + if (err) + { + freeL("Extra Resource Record", extra); + errormsg = "mDNS_AddRecordToService"; + 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; } + extra->ClientID = id; + } - 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: @@ -1496,7 +1794,7 @@ mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRe // Fill in new length, and data newrdata->MaxRDLength = size; memcpy(&newrdata->u, data, data_len); - + // Do the operation LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)", client, srs->RR_SRV.resrec.name.c, data_len); @@ -1507,14 +1805,13 @@ mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRe 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) @@ -1523,9 +1820,9 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_por mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; domainname *name = (domainname *)""; - AuthRecord *gRR, *lRR; + ServiceInstance *si; - (void)unusedserver; // unused + (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; @@ -1534,28 +1831,28 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_por // 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 + for (si = x->regs; si; si = si->next) { - 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; + AuthRecord *r = NULL; - if (gRR) UpdateRecord(x->gs, client, gRR, data, data_len, ttl); // don't return error if global fails + // Find the record we're updating. NULL reference means update the primary TXT record + if (!reference) r = &si->srs.RR_TXT; + else + { + ExtraResourceRecord *ptr; + for (ptr = si->srs.Extras; ptr; ptr = ptr->next) + { + if ((natural_t)ptr->ClientID == reference) + { r = &ptr->r; break; } + } + if (!r) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; } + } + err = UpdateRecord(&si->srs, client, r, data, data_len, ttl); + if (err) goto fail; //!!!KRS this will cause failures for non-local defaults! + } + return mStatus_NoError; - + fail: LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%ld)", client, name->c, reference, data_len, errormsg, err); return(err); @@ -1565,23 +1862,14 @@ mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra { 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: DNSServiceRegistrationRemoveRecord(%##s, %X) failed: %s", client, name->c, errormsg, err); - return(err); + err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra); + if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err); + + return err; } mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client, @@ -1591,30 +1879,28 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_por (void)unusedserver; // Unused mStatus err = mStatus_NoError; const char *errormsg = "Unknown"; - ExtraRecordRef *ref, *prev = NULL; if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } DNSServiceRegistration *x = DNSServiceRegistrationList; + ServiceInstance *si; + while (x && x->ClientMachPort != client) x = x->next; if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } - ref = x->ExtraRefList; - while (ref) + for (si = x->regs; si; si = si->next) { - if (ref == (ExtraRecordRef *)ref) break; - prev = ref; - ref = ref->next; + ExtraResourceRecord *e; + for (e = si->srs.Extras; e; e = e->next) + { + if ((natural_t)e->ClientID == reference) + { + err = RemoveRecord(&si->srs, e, client); + break; + } + } + if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; } } - - 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 - - // delete the ref struct - if (prev) prev->next = ref->next; - else x->ExtraRefList = ref->next; - ref->next = NULL; - freeL("ExtraRecordRef", ref); - return err; + + return mStatus_NoError; fail: LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%ld)", client, reference, errormsg, err); @@ -1705,7 +1991,7 @@ mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, vo break; default : - /* Includes success case. */ + /* Includes success case. */ break; } @@ -1772,23 +2058,19 @@ mDNSlocal kern_return_t destroyBootstrapService() return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL); } -mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) +mDNSlocal void ExitCallback(int signal) { - (void)port; // Unused - (void)msg; // Unused - (void)size; // Unused - (void)info; // Unused -/* +#if 0 CacheRecord *rr; int rrcache_active = 0; for (rr = mDNSStorage.rrcache; rr; rr=rr->next) if (CacheRRActive(&mDNSStorage, rr)) rrcache_active++; debugf("ExitCallback: RR Cache now using %d records, %d active", mDNSStorage.rrcache_used, rrcache_active); -*/ +#endif LogMsgIdent(mDNSResponderVersionString, "stopping"); debugf("ExitCallback: destroyBootstrapService"); - if (!mDNS_DebugMode) + if (!mDNS_DebugMode && signal != SIGHUP) destroyBootstrapService(); debugf("ExitCallback: Aborting MIG clients"); @@ -1808,89 +2090,62 @@ mDNSlocal void ExitCallback(CFMachPortRef port, void *msg, CFIndex size, void *i } // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit -mDNSlocal void HandleSIGTERM(int signal) +mDNSlocal void HandleSIG(int signal) { - (void)signal; // Unused debugf(" "); - debugf("SIGINT/SIGTERM"); + debugf("HandleSIG %d", signal); mach_msg_header_t header; header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - header.msgh_remote_port = exit_m_port; + header.msgh_remote_port = signal_port; header.msgh_local_port = MACH_PORT_NULL; header.msgh_size = sizeof(header); - header.msgh_id = 0; + header.msgh_id = signal; if (mach_msg_send(&header) != MACH_MSG_SUCCESS) - { LogMsg("HandleSIGTERM: mach_msg_send failed; Exiting immediately."); exit(-1); } + { + LogMsg("HandleSIG %d: mach_msg_send failed", signal); + if (signal == SIGHUP || signal == SIGTERM || signal == SIGINT) exit(-1); + } } -mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) +mDNSlocal void INFOCallback(void) { - (void)port; // Unused - (void)msg; // Unused - (void)size; // Unused - (void)info; // Unused DNSServiceDomainEnumeration *e; DNSServiceBrowser *b; DNSServiceResolver *l; DNSServiceRegistration *r; NetworkInterfaceInfoOSX *i; - 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 = mDNSStorage.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), - ((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) - LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", mDNSStorage.rrcache_totalused, CacheUsed); - if (mDNSStorage.rrcache_active != 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); + udsserver_info(&mDNSStorage); for (e = DNSServiceDomainEnumerationList; e; e=e->next) - LogMsgNoIdent("%5d: DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c); + LogMsgNoIdent("%5d: Mach DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c); for (b = DNSServiceBrowserList; b; b=b->next) { DNSServiceBrowserQuestion *qptr; for (qptr = b->qlist; qptr; qptr = qptr->next) - LogMsgNoIdent("%5d: ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c); + LogMsgNoIdent("%5d: Mach ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c); } for (l = DNSServiceResolverList; l; l=l->next) - LogMsgNoIdent("%5d: ServiceResolve %##s", l->ClientMachPort, l->i.name.c); + LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l->ClientMachPort, l->i.name.c); for (r = DNSServiceRegistrationList; r; r=r->next) { - 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)); + ServiceInstance *si; + for (si = r->regs; si; si = si->next) + LogMsgNoIdent("%5d: Mach ServiceInstance %##s %u", si->ClientMachPort, si->srs.RR_SRV.resrec.name.c, mDNSVal16(si->srs.RR_SRV.resrec.rdata->u.srv.port)); } - udsserver_info(); - 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); + LogMsgNoIdent("Interface: %s %5s(%lu) %.6a DORMANT", + i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifa_name, i->scope_id, &i->BSSID); 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, + LogMsgNoIdent("Interface: %s %5s(%lu) %.6a %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->BSSID, i->ifinfo.InterfaceActive ? "Active" : " ", i->ifinfo.IPv4Available ? "v4" : " ", i->ss.sktv4, i->ifinfo.IPv6Available ? "v6" : " ", i->ss.sktv6, @@ -1903,17 +2158,22 @@ mDNSlocal void INFOCallback(CFMachPortRef port, void *msg, CFIndex size, void *i LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----"); } -mDNSlocal void HandleSIGINFO(int signal) +mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) { - (void)signal; // Unused - mach_msg_header_t header; - header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - header.msgh_remote_port = info_m_port; - header.msgh_local_port = MACH_PORT_NULL; - header.msgh_size = sizeof(header); - header.msgh_id = 0; - if (mach_msg_send(&header) != MACH_MSG_SUCCESS) - LogMsg("HandleSIGINFO: mach_msg_send failed; No state log will be generated."); + (void)port; // Unused + (void)size; // Unused + (void)info; // Unused + mach_msg_header_t *m = (mach_msg_header_t *)msg; + switch(m->msgh_id) + { + case SIGHUP: + case SIGINT: + case SIGTERM: ExitCallback(m->msgh_id); break; + case SIGINFO: INFOCallback(); break; + case SIGUSR1: LogMsg("SIGUSR1: Simulate Network Configuration Change Event"); + mDNSMacOSXNetworkChanged(&mDNSStorage); break; + default: LogMsg("SignalCallback: Unknown signal %d", m->msgh_id); break; + } } mDNSlocal kern_return_t mDNSDaemonInitialize(void) @@ -1921,16 +2181,14 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) mStatus err; CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); CFMachPortRef s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); - CFMachPortRef e_port = CFMachPortCreate(NULL, ExitCallback, NULL, NULL); - CFMachPortRef i_port = CFMachPortCreate(NULL, INFOCallback, NULL, NULL); + CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL); mach_port_t m_port = CFMachPortGetPort(s_port); char *MachServerName = mDNSMacOSXSystemBuildNumber(NULL) < 7 ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder"; kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port); CFRunLoopSourceRef d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0); CFRunLoopSourceRef s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); - CFRunLoopSourceRef e_rls = CFMachPortCreateRunLoopSource(NULL, e_port, 0); CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0); - + if (status) { if (status == 1103) @@ -1944,37 +2202,34 @@ 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); - exit_m_port = CFMachPortGetPort(e_port); - info_m_port = CFMachPortGetPort(i_port); + signal_port = CFMachPortGetPort(i_port); CFRunLoopAddSource(CFRunLoopGetCurrent(), d_rls, kCFRunLoopDefaultMode); CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode); - CFRunLoopAddSource(CFRunLoopGetCurrent(), e_rls, kCFRunLoopDefaultMode); CFRunLoopAddSource(CFRunLoopGetCurrent(), i_rls, kCFRunLoopDefaultMode); CFRelease(d_rls); CFRelease(s_rls); - CFRelease(e_rls); CFRelease(i_rls); if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); - err = udsserver_init(&mDNSStorage); + err = udsserver_init(); if (err) { LogMsg("Daemon start: udsserver_init failed"); return err; } return(err); } -mDNSlocal mDNSs32 mDNSDaemonIdle(void) +mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m) { // 1. Call mDNS_Execute() to let mDNSCore do what it needs to do - mDNSs32 nextevent = mDNS_Execute(&mDNSStorage); + mDNSs32 nextevent = mDNS_Execute(m); + + mDNSs32 now = mDNS_TimeNow(m); - 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 @@ -2023,10 +2278,46 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(void) if (l->ReportTime && now - l->ReportTime >= 0) { l->ReportTime = 0; - LogMsg("%5d: DNSServiceResolver(%##s) active for over two minutes. " - "This places considerable burden on the network.", l->ClientMachPort, l->i.name.c); + LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. " + "This places considerable burden on the network.", l->i.name.c); } + if (m->p->NotifyUser) + { + if (m->p->NotifyUser - now < 0) + { + if (!SameDomainLabel(m->p->usernicelabel.c, m->nicelabel.c)) + { + LogMsg("Updating Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c); + RecordUpdatedName(m, &m->p->usernicelabel, &m->nicelabel, "The name of your computer ", "", + CFSTR("To change the name of your computer, open System Preferences and click Sharing. Then type the name in the Computer Name field.")); + m->p->NotifyUser = 0; // Clear m->p->NotifyUser here -- even if the hostlabel has changed too, we don't want to bug the user with *two* alerts + } + if (!SameDomainLabel(m->p->userhostlabel.c, m->hostlabel.c)) + { + LogMsg("Updating Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c); + RecordUpdatedName(m, &m->p->userhostlabel, &m->hostlabel, "This computer's local hostname ", ".local", + CFSTR("To change the local hostname, open System Preferences and click Sharing. Then click Edit and type the name in the Local Hostname field.")); + } + m->p->NotifyUser = 0; + } + else + if (nextevent - m->p->NotifyUser > 0) + nextevent = m->p->NotifyUser; + } + + if (m->p->NetworkChanged) + { + if (m->p->NetworkChanged - now < 0) + { + m->p->NetworkChanged = 0; + mDNSMacOSXNetworkChanged(m); + } + else + if (nextevent - m->p->NetworkChanged > 0) + nextevent = m->p->NetworkChanged; + } + return(nextevent); } @@ -2034,15 +2325,18 @@ mDNSexport int main(int argc, char **argv) { int i; kern_return_t status; - + for (i=1; ipw_uid); else setuid(-2); // User "nobody" is -2; use that value if "nobody" does not appear in the password database +#endif if (status == 0) { + LogMsg("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last); int numevents = 0; int RunLoopStatus = kCFRunLoopRunTimedOut; - + // This is the main work loop: // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time // (2) Then we make sure we've delivered all waiting browse messages to our clients @@ -2090,11 +2387,11 @@ 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(); + mDNSs32 nextevent = mDNSDaemonIdle(&mDNSStorage); nextevent = udsserver_idle(nextevent); // 2. Work out how long we expect to sleep before the next scheduled task - mDNSs32 ticks = nextevent - mDNSPlatformTimeNow(); + mDNSs32 ticks = nextevent - mDNS_TimeNow(&mDNSStorage); static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins if (ticks > 1) RepeatedBusy = 0; @@ -2105,7 +2402,7 @@ mDNSexport int main(int argc, char **argv) { 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 // (a) our next wakeup time, or (b) an event occurs. // The 'true' parameter makes it return after handling any event that occurs @@ -2133,23 +2430,27 @@ mDNSexport int main(int argc, char **argv) // uds_daemon.c support routines ///////////////////////////////////////////// -// We keep a list of client-supplied event sources in PosixEventSource records +// 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; + CFSocketRef cfs; 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) +static void cf_callback(CFSocketRef s, CFSocketCallBackType t, CFDataRef dr, const void *c, void *i) // Called by CFSocket when data appears on socket { + (void)s; // Unused + (void)t; // Unused + (void)dr; // Unused + (void)c; // Unused CFSocketEventSource *source = (CFSocketEventSource*) i; source->Callback(source->Context); } @@ -2159,7 +2460,7 @@ mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *cont { CFSocketEventSource *newSource; CFSocketContext cfContext = { 0, NULL, NULL, NULL, NULL }; - + if (gEventSources.LinkOffset == 0) InitLinkedList(&gEventSources, offsetof(CFSocketEventSource, Next)); @@ -2177,19 +2478,19 @@ mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *cont newSource->fd = fd; cfContext.info = newSource; - if ( NULL != (newSource->SocketRef = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, + if ( NULL != (newSource->cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, cf_callback, &cfContext)) && - NULL != (newSource->RLS = CFSocketCreateRunLoopSource(kCFAllocatorDefault, newSource->SocketRef, 0))) + NULL != (newSource->RLS = CFSocketCreateRunLoopSource(kCFAllocatorDefault, newSource->cfs, 0))) { CFRunLoopAddSource(CFRunLoopGetCurrent(), newSource->RLS, kCFRunLoopDefaultMode); AddToTail(&gEventSources, newSource); } else { - if (newSource->SocketRef) + if (newSource->cfs) { - CFSocketInvalidate(newSource->SocketRef); // automatically closes socket - CFRelease(newSource->SocketRef); + CFSocketInvalidate(newSource->cfs); // automatically closes socket + CFRelease(newSource->cfs); } return mStatus_NoMemoryErr; } @@ -2210,8 +2511,8 @@ mStatus udsSupportRemoveFDFromEventLoop(int fd) CFRunLoopRemoveSource(CFRunLoopGetCurrent(), iSource->RLS, kCFRunLoopDefaultMode); CFRunLoopSourceInvalidate(iSource->RLS); CFRelease(iSource->RLS); - CFSocketInvalidate(iSource->SocketRef); - CFRelease(iSource->SocketRef); + CFSocketInvalidate(iSource->cfs); + CFRelease(iSource->cfs); free(iSource); return mStatus_NoError; } @@ -2219,5 +2520,9 @@ mStatus udsSupportRemoveFDFromEventLoop(int fd) return mStatus_NoSuchNameErr; } +// If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = mDNSResponderVersionString; +asm(".desc ___crashreporter_info__, 0x10"); + // 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/CFSocket.c b/mDNSMacOSX/mDNSMacOSX.c similarity index 53% rename from mDNSMacOSX/CFSocket.c rename to mDNSMacOSX/mDNSMacOSX.c index 388cdf2..eebba79 100644 --- a/mDNSMacOSX/CFSocket.c +++ b/mDNSMacOSX/mDNSMacOSX.c @@ -1,17 +1,16 @@ -/* - * Copright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * 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, @@ -19,12 +18,339 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): -$Log: CFSocket.c,v $ +$Log: mDNSMacOSX.c,v $ +Revision 1.258 2004/12/14 00:18:05 cheshire +Don't log dns_configuration_copy() failures in the first three minutes after boot + +Revision 1.257 2004/12/10 19:45:46 cheshire + Reduce egregious stack space usage +Reduced myCFSocketCallBack() stack frame from 9K to 512 bytes + +Revision 1.256 2004/12/10 04:35:43 cheshire + Show "Note: Compiled without Apple-specific split DNS support" only once + +Revision 1.255 2004/12/10 04:12:54 ksekar + Need new DefaultBrowseDomain key + +Revision 1.254 2004/12/10 01:55:31 ksekar + Keychain lookups should be in lower case. + +Revision 1.253 2004/12/09 03:15:41 ksekar + use _legacy instead of _default to find "empty string" browse domains + +Revision 1.252 2004/12/07 01:32:42 cheshire +Don't log dns_configuration_copy() failure when running on 10.3 + +Revision 1.251 2004/12/06 22:30:31 cheshire +Added debugging log message + +Revision 1.250 2004/12/06 06:59:08 ksekar +RegisterSplitDNS should return Unsupported error when compiled on Panther + +Revision 1.249 2004/12/04 00:29:46 cheshire +Add "#ifdef MAC_OS_X_VERSION_10_4" around split-DNS code that can't be compiled on 10.3 systems +(When compiled on 10.3, code will not include split-DNS support.) + +Revision 1.248 2004/12/01 20:57:20 ksekar + Wide Area Rendezvous must be split-DNS aware + +Revision 1.247 2004/12/01 03:26:58 cheshire +Remove unused variables + +Revision 1.246 2004/12/01 01:51:34 cheshire +Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c + +Revision 1.245 2004/11/30 03:24:04 cheshire + Defer processing network configuration changes until configuration has stabilized + +Revision 1.244 2004/11/30 02:59:35 cheshire +For debugging diagnostics, added identifying strings in SCDynamicStoreCreate() calls + +Revision 1.243 2004/11/29 19:17:29 ksekar + Unnecessary GetUserSpecifiedDDNSConfig log messages + +Revision 1.242 2004/11/29 18:37:38 ksekar + Buffer overflow in GetConfigOption + +Revision 1.241 2004/11/25 01:37:04 ksekar + Config file and SCPreferences don't play well together + +Revision 1.240 2004/11/25 01:29:42 ksekar +Remove unnecessary log messages + +Revision 1.239 2004/11/25 01:27:19 ksekar + Don't try to advertise link-local IP addresses via dynamic update + +Revision 1.238 2004/11/24 22:00:59 cheshire +Move definition of mDNSAddressIsAllDNSLinkGroup() from mDNSMacOSX.c to mDNSEmbeddedAPI.h + +Revision 1.237 2004/11/24 21:54:44 cheshire + mDNSCore not receiving unicast responses properly + +Revision 1.236 2004/11/23 03:39:46 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.235 2004/11/17 01:45:35 cheshire + Rendezvous buddy list frequently becomes empty if you let the machine sleep +Refresh our interface list on receiving kIOMessageSystemHasPoweredOn, +in case we get no System Configuration Framework "network changed" event. + +Revision 1.234 2004/11/17 00:32:56 ksekar + mDNSResponder will not discover zones contained both in Search Domains and DHCP Domain option + +Revision 1.233 2004/11/12 03:16:45 rpantos +rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName + +Revision 1.232 2004/11/10 20:40:54 ksekar + LLQ mobility fragile on non-primary interface + +Revision 1.231 2004/11/06 00:59:33 ksekar +Don't log ENETDOWN errors for unicast destinations (pollutes log on +wake from sleep) + +Revision 1.230 2004/11/05 01:04:10 ksekar + LegacyNATDestroy() called too enthusiastically + +Revision 1.229 2004/11/03 03:45:16 cheshire + mDNSResponder does not inform user of Computer Name collisions + +Revision 1.228 2004/11/02 23:47:32 cheshire + Default hostname and Computer Name should be unique + +Revision 1.227 2004/11/02 04:23:03 cheshire +Change to more informative name "GetUserSpecifiedLocalHostName()" + +Revision 1.226 2004/11/01 20:36:19 ksekar + mDNSResponder should not receive Keychain Notifications + +Revision 1.225 2004/10/28 19:03:04 cheshire +Remove \n from LogMsg() calls + +Revision 1.224 2004/10/28 17:47:34 cheshire +Oops. Forgot the %d in the log message. + +Revision 1.223 2004/10/28 17:24:28 cheshire +Updated "bad ifa_netmask" log message to give more information + +Revision 1.222 2004/10/28 03:36:34 cheshire + Share the same port for both multicast and unicast receiving + +Revision 1.221 2004/10/28 03:24:41 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.220 2004/10/28 00:53:57 cheshire +Export mDNSMacOSXNetworkChanged() so it's callable from outside this mDNSMacOSX.c; +Add LogOperation() call to record when we get network change events + +Revision 1.219 2004/10/27 20:42:20 cheshire +Clean up debugging messages + +Revision 1.218 2004/10/27 02:03:59 cheshire +Update debugging messages + +Revision 1.217 2004/10/26 20:48:21 cheshire +Improve logging messages + +Revision 1.216 2004/10/26 01:02:37 cheshire +Update comment + +Revision 1.215 2004/10/25 20:09:00 ksekar +Cleaned up config file parsing. + +Revision 1.214 2004/10/25 19:30:53 ksekar + Simplify dynamic host name structures + +Revision 1.213 2004/10/23 01:16:01 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.212 2004/10/22 20:52:08 ksekar + Create NAT port mappings for Long Lived Queries + +Revision 1.211 2004/10/22 01:07:11 cheshire + select() says data is waiting; recvfrom() says there is no data +Log error message if socket() ever returns file descriptors 0, 1 or 2 (stdin/stdout/stderr). +These are all supposed to be remapped to /dev/null + +Revision 1.210 2004/10/20 02:19:54 cheshire +Eliminate "SetupAddr invalid sa_family" warning from RegisterSearchDomains() + +Revision 1.209 2004/10/16 00:17:00 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.208 2004/10/15 23:00:18 ksekar + Need to update LLQs on location changes + +Revision 1.207 2004/10/13 22:45:23 cheshire + Ten-second delay before kIOMessageSystemHasPoweredOn message + +Revision 1.206 2004/10/13 22:11:46 cheshire +Update debugging messages + +Revision 1.205 2004/10/12 21:10:11 cheshire + mach_absolute_time() not monotonically increasing +Do a NotifyOfElusiveBug() if we see mach_absolute_time() go backwards + +Revision 1.204 2004/10/12 03:20:52 ksekar + Incorrect LogMsg produces garbage on errors + +Revision 1.203 2004/10/08 04:29:25 ksekar + Allow default search domains to be set via hint from DHCP + +Revision 1.202 2004/10/04 05:56:04 cheshire + mDNSResponder doesn't respond to certain AirPort changes + +Revision 1.201 2004/09/30 00:24:59 ksekar + Dynamically update default registration domains on config change + +Revision 1.200 2004/09/26 23:20:35 ksekar + Allow default registrations in multiple wide-area domains + +Revision 1.199 2004/09/24 23:54:55 cheshire + Don't use kCFSocketCloseOnInvalidate + +Revision 1.198 2004/09/24 23:47:49 cheshire +Correct comment and error message + +Revision 1.197 2004/09/24 23:39:27 cheshire + Only IPv6 loopback address advertised on laptop w/no networking + +Revision 1.196 2004/09/24 20:53:04 cheshire +Revise error message to say "Setsockopt SO_REUSEPORT failed" instead of "Flaw in Kernel" + +Revision 1.195 2004/09/24 19:21:45 cheshire + Report "Address already in use" errors + +Revision 1.194 2004/09/24 19:16:54 cheshire +Remove "mDNS *const m" parameter from NotifyOfElusiveBug(); +Refine error message to say "Flaw in Kernel (select/recvfrom mismatch)" + +Revision 1.193 2004/09/22 00:41:59 cheshire +Move tcp connection status codes into the legal range allocated for mDNS use + +Revision 1.192 2004/09/21 21:02:55 cheshire +Set up ifname before calling mDNS_RegisterInterface() + +Revision 1.191 2004/09/21 19:19:36 cheshire + Combine WatchForDynDNSChanges() into WatchForNetworkChanges() + +Revision 1.190 2004/09/21 19:04:45 cheshire +Strip trailing white space from the ends of lines + +Revision 1.189 2004/09/21 00:13:28 cheshire +Fix build failure (io_connect_t and io_object_t are integers, not pointers) + +Revision 1.188 2004/09/20 23:52:02 cheshire +CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c + +Revision 1.187 2004/09/18 01:37:01 cheshire +Update comment + +Revision 1.186 2004/09/18 01:11:57 ksekar + Add a user's default domain to empty-string browse list + +Revision 1.185 2004/09/17 01:08:52 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.184 2004/09/17 00:19:10 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.183 2004/09/17 00:15:56 cheshire +Rename mDNSPlatformInit_ReceiveUnicast to mDNSPlatformInit_CanReceiveUnicast + +Revision 1.182 2004/09/16 21:36:36 cheshire + Fix unsafe use of mDNSPlatformTimeNow() +Changes to add necessary locking calls around unicast DNS operations + +Revision 1.181 2004/09/16 02:03:42 cheshire + Change address to notify user of kernel flaw + +Revision 1.180 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.179 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.178 2004/09/15 21:51:34 cheshire + mDNSResponder should notify user of kernel flaw +Calling CFUserNotificationDisplayNotice too early in the boot process seems to kill +the machine, so make sure we don't do this until at least three minutes after boot. + +Revision 1.177 2004/09/15 01:16:29 cheshire + mDNSResponder should notify user of kernel flaw + +Revision 1.176 2004/09/14 23:42:36 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.175 2004/09/14 21:35:46 cheshire +Minor code tidying, and added comments about CFRetainCounts + +Revision 1.174 2004/09/14 19:14:57 ksekar + DynDNS: Discovery of DynDNS Zones via Reverse-Map PTR + +Revision 1.173 2004/08/25 23:35:22 ksekar +: Error converting shared secret from base-64 to binary + +Revision 1.172 2004/08/25 02:01:45 cheshire + Need to be able to get status of Dynamic DNS Host Name Update + +Revision 1.171 2004/08/25 01:04:42 cheshire +Don't need to CFRelease name and array + +Revision 1.170 2004/08/25 00:37:28 ksekar +: Cleanup DynDNS hostname registration code + +Revision 1.169 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.168 2004/08/17 03:16:24 ksekar +Fixed checkin 1.166 - enumeration type changed for wrong invocation of mDNS_GetDomains + +Revision 1.167 2004/08/17 00:52:43 ksekar +Fix config file parse error, make semantics match SCPreferences +configuration input. + +Revision 1.166 2004/08/16 19:55:07 ksekar +Change enumeration type to BrowseDefault to construct empty-string +browse list as result of checking 1.161. + +Revision 1.165 2004/08/16 19:52:40 ksekar +Pass computer name + zone for FQDN after keychain notification, +setting global default service registration domain to the zone. + +Revision 1.164 2004/08/16 16:52:37 ksekar +Pass in zone read from keychain to mDNS_SetFQDNs. + +Revision 1.163 2004/08/14 03:22:42 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.162 2004/08/12 22:34:00 cheshire +All strings should be read as kCFStringEncodingUTF8, not kCFStringEncodingASCII + +Revision 1.161 2004/08/11 00:17:46 ksekar +: 8A227: Need Lighthouse configred machines to +set default bit for their domains + +Revision 1.160 2004/07/29 19:27:16 ksekar +NATPMP Support - minor fixes and cleanup + +Revision 1.159 2004/07/26 22:49:31 ksekar +: Feature #9516: Need support for NATPMP in client + +Revision 1.158 2004/07/13 21:24:24 rpantos +Fix for . + Revision 1.157 2004/06/08 18:54:48 ksekar : mDNSResponder leaks after exploring in Printer Setup Utility @@ -56,7 +382,7 @@ 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" +from mDNSMacOSX.h to mDNSEmbeddedAPI.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. @@ -155,7 +481,7 @@ 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. +Best solution is just to combine mDNSEmbeddedAPI.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 @@ -177,7 +503,7 @@ 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() + mDNSResponder divide by zero in mDNSPlatformRawTime() Revision 1.110 2003/08/16 03:39:00 cheshire InterfaceID -1 indicates "local only" @@ -256,7 +582,7 @@ Revision 1.90 2003/06/24 01:51:47 cheshire 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 + mDNSResponder 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 @@ -322,7 +648,7 @@ Revision 1.73 2003/05/21 17:56:29 ksekar 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 +mDNSMacOSX.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 @@ -428,7 +754,7 @@ 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 +Move Puma support to mDNSMacOSXPuma.c Revision 1.43 2002/09/17 01:05:28 cheshire Change mDNS_AdvertiseLocalAddresses to be an Init parameter instead of a global @@ -439,7 +765,7 @@ Minor code tidying */ // *************************************************************************** -// mDNS-CFSocket.c: +// mDNSMacOSX.c: // Supporting routines to run mDNS on a CFRunLoop platform // *************************************************************************** @@ -452,13 +778,16 @@ Minor code tidying // 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 "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "DNSCommon.h" +#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform +#include "../mDNSShared/uds_daemon.h" // Defines communication interface from platform layer up to UDS daemon +#include "PlatformCommon.h" #include -#include // For select() and close() #include // For va_list support #include +#include // For IFT_ETHER #include #include #include @@ -480,12 +809,17 @@ Minor code tidying #include +#include +#ifdef MAC_OS_X_VERSION_10_4 +#include +#endif + // 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" +#include "mDNSMacOSXPuma.c" #else #include #endif @@ -494,20 +828,17 @@ 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; + int flag; + DNSQuestion BrowseQ; + DNSQuestion DefBrowseQ; + DNSQuestion LegacyBrowseQ; + DNSQuestion RegisterQ; + DNSQuestion DefRegisterQ; + ARListElem *AuthRecs; } SearchListElem; @@ -515,32 +846,108 @@ typedef struct 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) +// for domain enumeration and default browsing/registration +static SearchListElem *SearchList = NULL; // where we search for _browse domains +static DNSQuestion LegacyBrowseDomainQ; // our local enumeration query for _legacy._browse domains +static DNameListElem *DefBrowseList = NULL; // cache of answers to above query (where we search for empty string browses) +static DNameListElem *DefRegList = NULL; // manually generated list of domains where we register for empty string registrations +static ARListElem *SCPrefBrowseDomains = NULL; // manually generated local-only PTR records for browse domains we get from SCPreferences + +static domainname DynDNSRegDomain; // Default wide-area zone for service registration +static domainname DynDNSBrowseDomain; // Default wide-area zone for legacy ("empty string") browses +static domainname DynDNSHostname; #define CONFIG_FILE "/etc/mDNSResponder.conf" -#define LH_KEYCHAIN_DESC "Lighthouse Shared Secret" -#define LH_KEYCHAIN_SERVICE "Lighthouse" +#define DYNDNS_KEYCHAIN_SERVICE "DynDNS Shared Secret" #define SYS_KEYCHAIN_PATH "/Library/Keychains/System.keychain" -#define LH_SUFFIX "members.mac.com." + +// Function Prototypes +mDNSlocal void SetSCPrefsBrowseDomain(mDNS *m, const domainname *d, mDNSBool add); // *************************************************************************** -// Macros +// Functions -#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]) +// routines to allow access to default domain lists from daemon layer -#define mDNSAddressIsAllDNSLinkGroup(X) ( \ - ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup )) || \ - ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) ) +mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void) + { + return mDNS_CopyDNameList(DefBrowseList); + } -// *************************************************************************** -// Functions +mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void) + { + return mDNS_CopyDNameList(DefRegList); + } + +// utility routines to manage registration domain lists + +mDNSlocal void AddDefRegDomain(domainname *d) + { + DNameListElem *newelem = NULL, *ptr; + + // make sure name not already in list + for (ptr = DefRegList; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->name, d)) + { debugf("duplicate addition of default reg domain %##s", d->c); return; } + } + + newelem = mallocL("DNameListElem", sizeof(*newelem)); + if (!newelem) { LogMsg("Error - malloc"); return; } + AssignDomainName(newelem->name, *d); + newelem->next = DefRegList; + DefRegList = newelem; + + DefaultRegDomainChanged(d, mDNStrue); + udsserver_default_reg_domain_changed(d, mDNStrue); + } + +mDNSlocal void RemoveDefRegDomain(domainname *d) + { + DNameListElem *ptr = DefRegList, *prev = NULL; + + while (ptr) + { + if (SameDomainName(&ptr->name, d)) + { + if (prev) prev->next = ptr->next; + else DefRegList = ptr->next; + freeL("DNameListElem", ptr); + DefaultRegDomainChanged(d, mDNSfalse); + udsserver_default_reg_domain_changed(d, mDNSfalse); + return; + } + prev = ptr; + ptr = ptr->next; + } + debugf("Requested removal of default registration domain %##s not in contained in list", d->c); + } + +mDNSlocal void NotifyOfElusiveBug(const char *title, mDNSu32 radarid, const char *msg) + { + extern mDNS mDNSStorage; + NetworkInterfaceInfoOSX *i; + static int notifyCount = 0; + if (notifyCount) return; + + // If we display our alert early in the boot process, then it vanishes once the desktop appears. + // To avoid this, we don't try to display alerts in the first three minutes after boot. + if ((mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return; + + // Determine if we're at Apple (17.*.*.*) + for (i = mDNSStorage.p->InterfaceList; i; i = i->next) + if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && i->ifinfo.ip.ip.v4.b[0] == 17) + break; + if (!i) return; // If not at Apple, don't show the alert + + // Send a notification to the user to contact coreos-networking + notifyCount++; + CFStringRef alertHeader = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); + CFStringRef alertFormat = CFSTR("Congratulations, you've reproduced an elusive bug. Please contact the owner of . %s"); + CFStringRef alertMessage = CFStringCreateWithFormat(NULL, NULL, alertFormat, radarid, msg); + CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, NULL); + } mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh) { @@ -557,6 +964,17 @@ mDNSlocal struct ifaddrs* myGetIfAddrs(int refresh) return ifa; } +mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const char *ifname, int type) + { + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + 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); + return(NULL); + } + mDNSlocal int myIfIndexToName(u_short index, char* name) { struct ifaddrs *ifa; @@ -577,7 +995,7 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(const mDNS 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; @@ -591,7 +1009,8 @@ mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(const mDNS *const m // 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, +// OR send via our primary v4 unicast socket +mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) { #pragma unused(m) @@ -604,6 +1023,16 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *co struct sockaddr_storage to; int s = -1, err; + // Sanity check: Make sure that if we're sending a query via unicast, we're sending it using our + // anonymous socket created for this purpose, so that we'll receive the response. + // If we use one of the many multicast sockets bound to port 5353 then we may not receive responses reliably. + if (info && !mDNSAddrIsDNSMulticast(dst)) + { + const DNSMessage *const m = (DNSMessage *)msg; + if ((m->h.flags.b[0] & kDNSFlag0_QR_Mask) == kDNSFlag0_QR_Query) + LogMsg("mDNSPlatformSendUDP: ERROR: Sending query OP from mDNS port to non-mDNS destination %#a:%d", dst, mDNSVal16(dstPort)); + } + if (dst->type == mDNSAddrType_IPv4) { struct sockaddr_in *sin_to = (struct sockaddr_in*)&to; @@ -626,15 +1055,15 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const DNSMessage *co } else { - LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!\n"); + LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!"); return mStatus_BadParamErr; } if (s >= 0) - verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %X %s/%d to %#a:%d skt %d", + verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %p %5s/%ld to %#a:%d skt %d", 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)", + verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %p %5s/%ld (socket of this type not available)", 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 @@ -644,13 +1073,13 @@ 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 - if (errno == EHOSTDOWN && !mDNSAddressIsAllDNSLinkGroup(dst)) return(err); + // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations + if (!mDNSAddressIsAllDNSLinkGroup(dst) && (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH)) return(err); // 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 * 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", + if (errno == EHOSTUNREACH && (mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(err); + LogMsg("mDNSPlatformSendUDP sendto failed to send packet on InterfaceID %p %5s/%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); } @@ -683,18 +1112,18 @@ mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, 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); + if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("mDNSMacOSX.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", + if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.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); + if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); return(-1); } @@ -748,8 +1177,7 @@ mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBack mDNSIPPort senderPort, destPort = MulticastDNSPort; const CFSocketSet *ss = (const CFSocketSet *)context; mDNS *const m = ss->m; - const mDNSInterfaceID InterfaceID = ss->info ? ss->info->ifinfo.InterfaceID : mDNSNULL; - DNSMessage packet; + mDNSInterfaceID InterfaceID = ss->info ? ss->info->ifinfo.InterfaceID : mDNSNULL; struct sockaddr_storage from; size_t fromlen = sizeof(from); char packetifname[IF_NAMESIZE] = ""; @@ -773,7 +1201,7 @@ mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBack } mDNSu8 ttl; - while ((err = myrecvfrom(s1, &packet, sizeof(packet), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl)) >= 0) + while ((err = myrecvfrom(s1, &m->imsg, sizeof(m->imsg), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl)) >= 0) { count++; if (from.ss_family == AF_INET) @@ -796,27 +1224,39 @@ mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBack 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 (!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); + 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 (!ss->info) + { + verbosedebugf("myCFSocketCallBack got multicast packet from %#a to %#a on unicast socket (Ignored)", &senderAddr, &destAddr); + return; + } + else if (!strcmp(ss->info->ifa_name, packetifname)) + verbosedebugf("myCFSocketCallBack got multicast packet from %#a to %#a on interface %#a/%s", + &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifa_name); + else + { + verbosedebugf("myCFSocketCallBack got multicast 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; + } + } else { - 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; + // 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; } - - 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, InterfaceID, ttl); + + mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, destPort, InterfaceID); } if (err < 0 && (errno != EWOULDBLOCK || count == 0)) @@ -848,8 +1288,13 @@ mDNSlocal void myCFSocketCallBack(CFSocketRef cfs, CFSocketCallBackType CallBack 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); + if (numLogMessages > 5) + NotifyOfElusiveBug("Flaw in Kernel (select/recvfrom mismatch)", 3375328, + "Alternatively, you can send email to radar-3387020@group.apple.com. " + "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); + sleep(1); // After logging this error, rate limit so we don't flood syslog - } + } } // TCP socket support for unicast DNS and Dynamic DNS Update @@ -865,7 +1310,7 @@ mDNSlocal void tcpCFSocketCallback(CFSocketRef cfs, CFSocketCallBackType Callbac const void *data, void *context) { #pragma unused(CallbackType, address, data) - mDNSBool connect = mDNSfalse; + mDNSBool connect = mDNSfalse; tcpInfo_t *info = context; if (!info->connected) @@ -874,7 +1319,7 @@ mDNSlocal void tcpCFSocketCallback(CFSocketRef cfs, CFSocketCallBackType Callbac 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! + // NOTE: the callback may call CloseConnection here, which frees the context structure! } mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, @@ -882,11 +1327,10 @@ mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstp { int sd, on = 1; // "on" for setsockopt struct sockaddr_in saddr; - CFSocketContext cfContext = { 0, NULL, 0, 0, 0 }; + CFSocketContext cfContext = { 0, NULL, 0, 0, 0 }; tcpInfo_t *info; CFSocketRef sr; CFRunLoopSourceRef rls; - CFOptionFlags srFlags; (void)InterfaceID; //!!!KRS use this if non-zero!!! @@ -898,11 +1342,8 @@ mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstp } sd = socket(AF_INET, SOCK_STREAM, 0); - if (sd < 0) - { - LogMsg("ERROR: socket; %s", strerror(errno)); - return mStatus_UnknownErr; - } + if (sd < 3) { LogMsg("mDNSPlatformTCPConnect: socket error %d errno %d (%s)", sd, errno, strerror(errno)); return mStatus_UnknownErr; } + // set non-blocking if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) { @@ -930,12 +1371,8 @@ mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstp return mStatus_UnknownErr; } - // prevent closing of native socket - srFlags = CFSocketGetSocketFlags(sr); - CFSocketSetSocketFlags(sr, srFlags & (~kCFSocketCloseOnInvalidate)); - rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sr, 0); - if (!rls) + if (!rls) { LogMsg("ERROR: mDNSPlatformTCPConnect - CFSocketCreateRunLoopSource failed"); freeL("mDNSPlatformTCPConnect", info); @@ -956,16 +1393,16 @@ mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstp { info->connected = 0; *descriptor= sd; - return mStatus_ConnectionPending; + return mStatus_ConnPending; } LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: %s", strerror(errno)); freeL("mDNSPlatformTCPConnect", info); CFSocketInvalidate(sr); - return mStatus_ConnectionFailed; + return mStatus_ConnFailed; } info->connected = 1; *descriptor = sd; - return mStatus_ConnectionEstablished; + return mStatus_ConnEstablished; } mDNSexport void mDNSPlatformTCPCloseConnection(int sd) @@ -974,13 +1411,12 @@ mDNSexport void mDNSPlatformTCPCloseConnection(int sd) 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; - } + if (sd < 3) LogMsg("mDNSPlatformTCPCloseConnection: ERROR sd %d < 3", sd); + + // Get the CFSocket for the descriptor + sr = CFSocketCreateWithNative(kCFAllocatorDefault, sd, kCFSocketNoCallBack, NULL, NULL); + if (!sr) { LogMsg("ERROR: mDNSPlatformTCPCloseConnection - CFSocketCreateWithNative returned NULL"); return; } + CFSocketGetContext(sr, &cfContext); if (!cfContext.info) { @@ -991,10 +1427,13 @@ mDNSexport void mDNSPlatformTCPCloseConnection(int sd) CFRelease(sr); // this only releases the copy we allocated with CreateWithNative above info = cfContext.info; + + // Note: MUST NOT close the underlying native BSD socket. + // 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. CFSocketInvalidate(sr); - CFRelease(sr); - close(sd); - freeL("mDNSPlatformTCPCloseConnection", info); + CFRelease(sr); + freeL("mDNSPlatformTCPCloseConnection", info); } mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen) @@ -1034,8 +1473,8 @@ mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel } } -// This gets the text of the field currently labelled "Rendezvous Name" in the Sharing Prefs Control Panel -mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) +// This gets the text of the field currently labelled "Local Hostname" in the Sharing Prefs Control Panel +mDNSlocal void GetUserSpecifiedLocalHostName(domainlabel *const namelabel) { CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); if (cfs) @@ -1045,9 +1484,90 @@ mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel) } } +mDNSlocal void GetUserSpecifiedDDNSConfig(domainname *const fqdn, domainname *const regDomain, domainname *const browseDomain) + { + char buf[MAX_ESCAPED_DOMAIN_NAME]; + + fqdn->c[0] = 0; + regDomain->c[0] = 0; + browseDomain->c[0] = 0; + buf[0] = 0; + + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetUserSpecifiedDDNSConfig"), NULL, NULL); + if (store) + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, CFSTR("Setup:/Network/DynamicDNS")); + if (dict) + { + CFArrayRef fqdnArray = CFDictionaryGetValue(dict, CFSTR("FQDN")); + CFArrayRef regArray = CFDictionaryGetValue(dict, CFSTR("DefaultRegistrationDomain")); + CFArrayRef browseArray = CFDictionaryGetValue(dict, CFSTR("DefaultBrowseDomain")); + if (fqdnArray) + { + CFStringRef name = CFArrayGetValueAtIndex(fqdnArray, 0); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(fqdn, buf) || !fqdn->c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS host name: %s", buf[0] ? buf : "(unknown)"); + else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS host name: %s", buf); + } + } + if (regArray) + { + CFStringRef name = CFArrayGetValueAtIndex(regArray, 0); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(regDomain, buf) || !regDomain->c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS registration domain: %s", buf[0] ? buf : "(unknown)"); + else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration zone: %s", buf); + } + } + if (browseArray) + { + CFStringRef name = CFArrayGetValueAtIndex(browseArray, 0); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(browseDomain, buf) || !browseDomain->c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS browse domain: %s", buf[0] ? buf : "(unknown)"); + else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browse zone: %s", buf); + } + } + CFRelease(dict); + } + CFRelease(store); + } + } + +mDNSlocal void SetDDNSNameStatus(domainname *const dname, mStatus status) + { + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:SetDDNSNameStatus"), NULL, NULL); + if (store) + { + char uname[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(dname, uname); + char *p = uname; + while (*p) { *p = tolower(*p); p++; } + const void *n1 = CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8); // CFStringRef + const void *v1 = CFNumberCreate(NULL, kCFNumberSInt32Type, &status); // CFNumberRef + const void *n2 = CFSTR("FQDN"); // CFStringRef + const void *v2 = CFDictionaryCreate(NULL, &n1, &v1, 1, NULL, NULL); // CFDictionaryRef + CFDictionaryRef dict = CFDictionaryCreate(NULL, &n2, &v2, 1, NULL, NULL); + SCDynamicStoreSetValue(store, CFSTR("State:/Network/DynamicDNS"), dict); + CFRelease(dict); + CFRelease(v2); + CFRelease(n2); + CFRelease(v1); + CFRelease(n1); + CFRelease(store); + } + } + // 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) +mDNSlocal mStatus SetupSocket(mDNS *const m, CFSocketSet *cp, mDNSBool mcast, 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; @@ -1057,12 +1577,14 @@ mDNSlocal mStatus SetupSocket(CFSocketSet *cp, mDNSIPPort port, const mDNSAddr * mStatus err = mStatus_NoError; char *errstr = mDNSNULL; + mDNSIPPort port = (mcast | m->CanReceiveUnicastOn5353) ? MulticastDNSPort : zeroIPPort; + 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(sa_family, SOCK_DGRAM, IPPROTO_UDP); - if (skt < 0) { LogMsg("socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } + if (skt < 3) { LogMsg("SetupSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno)); return(skt); } // ... with a shared UDP port, if it's for multicast receiving if (port.NotAnInteger) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); @@ -1083,11 +1605,11 @@ mDNSlocal mStatus SetupSocket(CFSocketSet *cp, mDNSIPPort port, const mDNSAddr * // 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, if it's for multicast receiving - if (port.NotAnInteger) + if (mcast) { struct in_addr addr = { ifaddr->ip.v4.NotAnInteger }; struct ip_mreq imr; - imr.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; + imr.imr_multiaddr.s_addr = AllDNSLinkGroupv4.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; } @@ -1133,7 +1655,7 @@ mDNSlocal mStatus SetupSocket(CFSocketSet *cp, mDNSIPPort port, const mDNSAddr * err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; } - if (port.NotAnInteger) + if (mcast) { // Add multicast group membership on this interface, if it's for multicast receiving int interface_id = if_nametoindex(cp->info->ifa_name); @@ -1192,64 +1714,101 @@ mDNSlocal mStatus SetupSocket(CFSocketSet *cp, mDNSIPPort port, const mDNSAddr * fail: LogMsg("%s error %ld errno %d (%s)", errstr, err, errno, strerror(errno)); + if (!strcmp(errstr, "bind") && errno == EADDRINUSE) + NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed", 3814904, + "Alternatively, you can send email to radar-3387020@group.apple.com. " + "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); close(skt); return(err); } mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) { + if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); } + 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); + return(mStatus_NoError); } - else if (sa->sa_family == AF_INET6) + + 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); + return(mStatus_NoError); } - else + + LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); + return(mStatus_Invalid); + } + +mDNSlocal mDNSEthAddr GetBSSID(char *ifa_name) + { + mDNSEthAddr eth = zeroEthAddr; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetBSSID"), NULL, NULL); + if (store) { - LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); - return(-1); + CFStringRef entityname = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Interface/%s/AirPort"), ifa_name); + if (entityname) + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, entityname); + if (dict) + { + CFRange range = { 0, 6 }; // Offset, length + CFDataRef data = CFDictionaryGetValue(dict, CFSTR("BSSID")); + if (data && CFDataGetLength(data) == 6) CFDataGetBytes(data, range, eth.b); + CFRelease(dict); + } + CFRelease(entityname); + } + CFRelease(store); } + return(eth); } mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa) { - mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); - mDNSAddr ip; - SetupAddr(&ip, ifa->ifa_addr); + mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); + mDNSEthAddr bssid = GetBSSID(ifa->ifa_name); + + mDNSAddr ip, mask; + if (SetupAddr(&ip, ifa->ifa_addr ) != mStatus_NoError) return(NULL); + if (SetupAddr(&mask, ifa->ifa_netmask) != mStatus_NoError) return(NULL); + NetworkInterfaceInfoOSX **p; for (p = &m->p->InterfaceList; *p; p = &(*p)->next) - if (scope_id == (*p)->scope_id && mDNSSameAddress(&ip, &(*p)->ifinfo.ip)) + if (scope_id == (*p)->scope_id && mDNSSameAddress(&ip, &(*p)->ifinfo.ip) && mDNSSameEthAddress(&bssid, &(*p)->BSSID)) { - debugf("AddInterfaceToList: Found existing interface %u with address %#a", scope_id, &ip); + debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, *p); (*p)->Exists = mDNStrue; return(*p); } - debugf("AddInterfaceToList: Making new interface %u with address %#a", scope_id, &ip); NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i)); + debugf("AddInterfaceToList: Making new interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, i); if (!i) return(mDNSNULL); + bzero(i, sizeof(NetworkInterfaceInfoOSX)); i->ifa_name = (char *)mallocL("NetworkInterfaceInfoOSX name", strlen(ifa->ifa_name) + 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; + i->ifinfo.mask = mask; + 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.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList i->next = mDNSNULL; i->Exists = mDNStrue; i->scope_id = scope_id; + i->BSSID = bssid; i->sa_family = ifa->ifa_addr->sa_family; i->Multicast = (ifa->ifa_flags & IFF_MULTICAST) && !(ifa->ifa_flags & IFF_POINTOPOINT); @@ -1276,94 +1835,100 @@ mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) { mDNSBool foundav4 = mDNSfalse; + mDNSBool foundav6 = 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); - if (mDNS_DNSRegistered(m)) mDNS_GenerateGlobalFQDN(m); - } + struct ifaddrs *v4Loopback = NULL; + struct ifaddrs *v6Loopback = NULL; + mDNSEthAddr PrimaryMAC = zeroEthAddr; + char defaultname[32]; + int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0); + if (InfoSocket < 3) LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno)); + if (m->SleepState) ifa = NULL; 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", + debugf("UpdateInterfaceList: %5s(%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", + debugf("UpdateInterfaceList: %5s(%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)", + debugf("UpdateInterfaceList: %5s(%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", + debugf("UpdateInterfaceList: %5s(%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", + debugf("UpdateInterfaceList: %5s(%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", + debugf("UpdateInterfaceList: %5s(%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", + debugf("UpdateInterfaceList: %5s(%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_flags & IFF_UP) + + if (ifa->ifa_addr->sa_family == AF_LINK) + { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == sizeof(PrimaryMAC) && mDNSSameEthAddress(&PrimaryMAC, &zeroEthAddr)) + mDNSPlatformMemCopy(sdl->sdl_data + sdl->sdl_nlen, PrimaryMAC.b, 6); + } + + if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr) if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) { - int ifru_flags6 = 0; - if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) + if (!ifa->ifa_netmask) + { + mDNSAddr ip; + SetupAddr(&ip, ifa->ifa_addr); + LogMsg("getifaddrs: ifa_netmask is NULL for %5s(%d) Flags %04X Family %2d %#a", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip); + } + else if (ifa->ifa_addr->sa_family != ifa->ifa_netmask->sa_family) { - 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); + mDNSAddr ip; + SetupAddr(&ip, ifa->ifa_addr); + LogMsg("getifaddrs ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip, ifa->ifa_netmask->sa_family); } - if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) + else { - if (ifa->ifa_flags & IFF_LOOPBACK) - theLoopback = ifa; - else + int ifru_flags6 = 0; + if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) { - AddInterfaceToList(m, ifa); - if (ifa->ifa_addr->sa_family == AF_INET) - foundav4 = mDNStrue; + 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) + if (ifa->ifa_addr->sa_family == AF_INET) v4Loopback = ifa; + else v6Loopback = ifa; + else + { + AddInterfaceToList(m, ifa); + if (ifa->ifa_addr->sa_family == AF_INET) foundav4 = mDNStrue; + else foundav6 = 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); + // For efficiency, we don't register a loopback interface when other interfaces of that family are available + if (!foundav4 && v4Loopback) AddInterfaceToList(m, v4Loopback); + if (!foundav6 && v6Loopback) AddInterfaceToList(m, v6Loopback); // Now the list is complete, set the McastTxRx setting for each interface. // We always send and receive using IPv4. @@ -1384,25 +1949,48 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m) i->Exists = 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 (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); - return(NULL); + mDNS_snprintf(defaultname, sizeof(defaultname), "Macintosh-%02X%02X%02X%02X%02X%02X", + PrimaryMAC.b[0], PrimaryMAC.b[1], PrimaryMAC.b[2], PrimaryMAC.b[3], PrimaryMAC.b[4], PrimaryMAC.b[5]); + + // Set up the nice label + domainlabel nicelabel; + nicelabel.c[0] = 0; + GetUserSpecifiedFriendlyComputerName(&nicelabel); + if (nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&nicelabel, defaultname); + + // Set up the RFC 1034-compliant label + domainlabel hostlabel; + hostlabel.c[0] = 0; + GetUserSpecifiedLocalHostName(&hostlabel); + if (hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&hostlabel, defaultname); + + if (SameDomainLabel(m->p->usernicelabel.c, nicelabel.c)) + debugf("Usernicelabel (%#s) unchanged since last time; not changing m->nicelabel (%#s)", m->p->usernicelabel.c, m->nicelabel.c); + else + { + debugf("Updating m->nicelabel to %#s", nicelabel.c); + m->p->usernicelabel = m->nicelabel = nicelabel; + } + + 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_SetFQDN(m); + } + + return(mStatus_NoError); } -mDNSlocal void SetupActiveInterfaces(mDNS *const m) +// returns count of non-link local V4 addresses registered +mDNSlocal int SetupActiveInterfaces(mDNS *const m) { NetworkInterfaceInfoOSX *i; + int count = 0; for (i = m->p->InterfaceList; i; i = i->next) if (i->Exists) { @@ -1423,29 +2011,31 @@ mDNSlocal void SetupActiveInterfaces(mDNS *const m) // 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); - LogOperation("SetupActiveInterfaces: Registered %s(%lu) InterfaceID %p %#a%s", - i->ifa_name, i->scope_id, primary, &n->ip, n->InterfaceActive ? " (Primary)" : ""); + if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && (i->ifinfo.ip.ip.v4.b[0] != 169 || i->ifinfo.ip.ip.v4.b[1] != 254)) count++; + LogOperation("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p %#a/%#a%s", + i->ifa_name, i->scope_id, &i->BSSID, primary, &n->ip, &n->mask, n->InterfaceActive ? " (Primary)" : ""); } if (!n->McastTxRx) - debugf("SetupActiveInterfaces: No Tx/Rx on %s(%lu) InterfaceID %p %#a", i->ifa_name, i->scope_id, primary, &n->ip); + debugf("SetupActiveInterfaces: No Tx/Rx on %5s(%lu) %.6a InterfaceID %p %#a", i->ifa_name, i->scope_id, &i->BSSID, primary, &n->ip); else { if (i->sa_family == AF_INET && primary->ss.sktv4 == -1) { - 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); + mStatus err = SetupSocket(m, &primary->ss, mDNStrue, &i->ifinfo.ip, AF_INET); + if (err == 0) debugf("SetupActiveInterfaces: v4 socket%2d %5s(%lu) %.6a InterfaceID %p %#a", primary->ss.sktv4, i->ifa_name, i->scope_id, &i->BSSID, n->InterfaceID, &n->ip); + else LogMsg("SetupActiveInterfaces: v4 socket%2d %5s(%lu) %.6a InterfaceID %p %#a FAILED", primary->ss.sktv4, i->ifa_name, i->scope_id, &i->BSSID, n->InterfaceID, &n->ip); } if (i->sa_family == AF_INET6 && primary->ss.sktv6 == -1) { - 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); + mStatus err = SetupSocket(m, &primary->ss, mDNStrue, &i->ifinfo.ip, AF_INET6); + if (err == 0) debugf("SetupActiveInterfaces: v6 socket%2d %5s(%lu) %.6a InterfaceID %p %#a", primary->ss.sktv6, i->ifa_name, i->scope_id, &i->BSSID, n->InterfaceID, &n->ip); + else LogMsg("SetupActiveInterfaces: v6 socket%2d %5s(%lu) %.6a InterfaceID %p %#a FAILED", primary->ss.sktv6, i->ifa_name, i->scope_id, &i->BSSID, n->InterfaceID, &n->ip); } } } + return count; } mDNSlocal void MarkAllInterfacesInactive(mDNS *const m) @@ -1455,19 +2045,29 @@ mDNSlocal void MarkAllInterfacesInactive(mDNS *const m) i->Exists = mDNSfalse; } +mDNSlocal void CloseRunLoopSourceSocket(CFRunLoopSourceRef rls, CFSocketRef cfs) + { + // Comments show retain counts (obtained via CFGetRetainCount()) after each call. rls 3 cfs 3 + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); // rls 2 cfs 3 + CFRelease(rls); // rls ? cfs 3 + CFSocketInvalidate(cfs); // rls ? cfs 1 + CFRelease(cfs); // rls ? cfs ? + } + mDNSlocal void CloseSocketSet(CFSocketSet *ss) { // 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); } + if (ss->cfsv4) CloseRunLoopSourceSocket(ss->rlsv4, ss->cfsv4); + if (ss->cfsv6) CloseRunLoopSourceSocket(ss->rlsv6, ss->cfsv6); ss->sktv4 = ss->sktv6 = -1; ss->cfsv4 = ss->cfsv6 = NULL; ss->rlsv4 = ss->rlsv6 = NULL; } -mDNSlocal void ClearInactiveInterfaces(mDNS *const m) +// returns count of non-link local V4 addresses deregistered +mDNSlocal int ClearInactiveInterfaces(mDNS *const m) { // First pass: // If an interface is going away, then deregister this from the mDNSCore. @@ -1477,6 +2077,7 @@ mDNSlocal void ClearInactiveInterfaces(mDNS *const m) // 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; + int count = 0; for (i = m->p->InterfaceList; i; i = i->next) { // 1. If this interface is no longer active, or its InterfaceID is changing, deregister it @@ -1484,9 +2085,10 @@ mDNSlocal void ClearInactiveInterfaces(mDNS *const m) 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)" : ""); + LogOperation("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p %#a%s", + i->ifa_name, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, &i->ifinfo.ip, i->ifinfo.InterfaceActive ? " (Primary)" : ""); mDNS_DeregisterInterface(m, &i->ifinfo); + if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && (i->ifinfo.ip.ip.v4.b[0] != 169 || i->ifinfo.ip.ip.v4.b[1] != 254)) count++; 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. @@ -1514,19 +2116,89 @@ mDNSlocal void ClearInactiveInterfaces(mDNS *const m) else p = &i->next; } + return count; } +mDNSlocal mStatus RegisterSplitDNS(mDNS *m) + { +#ifndef MAC_OS_X_VERSION_10_4 + static int MessageShown = 0; + (void)m; + if (!MessageShown) { MessageShown = 1; LogMsg("Note: Compiled without Apple-specific split DNS support"); } + return mStatus_UnsupportedErr; +#else + int i; + dns_config_t *config = dns_configuration_copy(); + + if (!config) + { + // When running on 10.3 (build 7xxx) and earlier, we don't expect dns_configuration_copy() to succeed + if (mDNSMacOSXSystemBuildNumber(NULL) < 8) return mStatus_UnsupportedErr; + + // On 10.4, calls to dns_configuration_copy() early in the boot process often fail. + // Apparently this is expected behaviour -- "not a bug". + // Accordingly, we suppress syslog messages for the first three minutes after boot. + // If we are still getting failures after three minutes, then we log them. + if ((mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return mStatus_UnknownErr; + + LogMsg("RegisterSplitDNS: Error: dns_configuration_copy returned NULL"); + return mStatus_UnknownErr; + } + mDNS_DeleteDNSServers(m); + + LogOperation("RegisterSplitDNS: Registering %d resolvers", config->n_resolver); + for (i = 0; i < config->n_resolver; i++) + { + int j, n; + domainname d; + dns_resolver_t *r = config->resolver[i]; + if (r->port == MulticastDNSPort.NotAnInteger) continue; // ignore configurations for .local + if (r->search_order == DEFAULT_SEARCH_ORDER || !r->domain || !*r->domain) d.c[0] = 0; // we ignore domain for "default" resolver + else if (!MakeDomainNameFromDNSNameString(&d, r->domain)) { LogMsg("RegisterSplitDNS: bad domain %s", r->domain); continue; } + + // check if this is the lowest-weighted server for the domain + for (j = 0; j < config->n_resolver; j++) + { + dns_resolver_t *p = config->resolver[j]; + if (p->port == MulticastDNSPort.NotAnInteger) continue; + if (p->search_order <= r->search_order) + { + domainname tmp; + if (p->search_order == DEFAULT_SEARCH_ORDER || !p->domain || !*p->domain) tmp.c[0] = '\0'; + else if (!MakeDomainNameFromDNSNameString(&tmp, p->domain)) { LogMsg("RegisterSplitDNS: bad domain %s", p->domain); continue; } + if (SameDomainName(&d, &tmp)) + if (p->search_order < r->search_order || j < i) break; // if equal weights, pick first in list, otherwise pick lower-weight (p) + } + } + if (j < config->n_resolver) // found a lower-weighted resolver for this domain + { debugf("Rejecting DNS server in slot %d domain %##s (slot %d outranks)", i, d.c, j); continue; } + // we're using this resolver - find the first IPv4 address + for (n = 0; n < r->n_nameserver; n++) + { + if (r->nameserver[n]->sa_family == AF_INET) + { + mDNSAddr saddr; + if (SetupAddr(&saddr, r->nameserver[n])) { LogMsg("RegisterSplitDNS: bad IP address"); continue; } + debugf("Adding dns server from slot %d %d.%d.%d.%d for domain %##s", i, saddr.ip.v4.b[0], saddr.ip.v4.b[1], saddr.ip.v4.b[2], saddr.ip.v4.b[3], d.c); + mDNS_AddDNSServer(m, &saddr, &d); + break; // !!!KRS if we ever support round-robin servers, don't break here + } + } + } + dns_configuration_free(config); + return mStatus_NoError; +#endif + } mDNSlocal mStatus RegisterNameServers(mDNS *const m, CFDictionaryRef dict) { int i, count; CFArrayRef values; - char buf[256]; - mDNSv4Addr saddr; + char buf[256]; + mDNSAddr saddr = { mDNSAddrType_IPv4, { { { 0 } } } }; CFStringRef s; - - mDNS_DeregisterDNSList(m); // deregister orig list + mDNS_DeleteDNSServers(m); // deregister orig list values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses); if (!values) return mStatus_NoError; @@ -1535,17 +2207,18 @@ mDNSlocal mStatus RegisterNameServers(mDNS *const m, CFDictionaryRef dict) { s = CFArrayGetValueAtIndex(values, i); if (!s) { LogMsg("ERROR: RegisterNameServers - CFArrayGetValueAtIndex"); break; } - if (!CFStringGetCString(s, buf, 256, kCFStringEncodingASCII)) + if (!CFStringGetCString(s, buf, 256, kCFStringEncodingUTF8)) { LogMsg("ERROR: RegisterNameServers - CFStringGetCString"); continue; } - if (!inet_aton(buf, (struct in_addr *)saddr.b)) + if (!inet_aton(buf, (struct in_addr *)saddr.ip.v4.b)) { LogMsg("ERROR: RegisterNameServers - invalid address string %s", buf); continue; } - mDNS_RegisterDNS(m, &saddr); + LogOperation("RegisterNameServers: Adding %#a", &saddr); + mDNS_AddDNSServer(m, &saddr, NULL); } return mStatus_NoError; } @@ -1553,32 +2226,37 @@ mDNSlocal mStatus RegisterNameServers(mDNS *const m, CFDictionaryRef dict) mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) { (void)m; // unused - AuthRecordListElem *elem = rr->RecordContext; + ARListElem *elem = rr->RecordContext; if (result == mStatus_MemFree) freeL("FreeARElemCallback", elem); } -mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { SearchListElem *slElem = question->QuestionContext; - AuthRecordListElem *arElem, *ptr, *prev; - AuthRecord *dereg; + ARListElem *arElem, *ptr, *prev; + AuthRecord *dereg; char *name; mStatus err; if (AddRecord) { - arElem = mallocL("FoundDomain - arElem", sizeof(AuthRecordListElem)); + arElem = mallocL("FoundDomain - arElem", sizeof(ARListElem)); 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."; + if (question == &slElem->BrowseQ) name = "_browse._dns-sd._udp.local."; + else if (question == &slElem->DefBrowseQ) name = "_default._browse._dns-sd._udp.local."; + else if (question == &slElem->LegacyBrowseQ) name = "_legacy._browse._dns-sd._udp.local."; + else if (question == &slElem->RegisterQ) name = "_register._dns-sd._udp.local."; + else if (question == &slElem->DefRegisterQ) name = "_default._register._dns-sd._udp.local."; + else { LogMsg("FoundDomain - unknown question"); return; } + MakeDomainNameFromDNSNameString(&arElem->ar.resrec.name, name); - strcpy(arElem->ar.resrec.rdata->u.name.c, answer->rdata->u.name.c); + AssignDomainName(arElem->ar.resrec.rdata->u.name, answer->rdata->u.name); err = mDNS_Register(m, &arElem->ar); if (err) { LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); - freeL("FoundDomain - arElem", arElem); + freeL("FoundDomain - arElem", arElem); return; } arElem->next = slElem->AuthRecs; @@ -1590,14 +2268,14 @@ mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceR prev = NULL; while (ptr) { - if (SameDomainName(&ptr->ar.resrec.name, &answer->name) && SameDomainName(&ptr->ar.resrec.rdata->u.name, &answer->rdata->u.name)) + if (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); + err = mDNS_Deregister(m, dereg); if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err); } else @@ -1609,65 +2287,119 @@ mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceR } } +mDNSlocal void MarkSearchListElem(domainname *domain) + { + SearchListElem *new, *ptr; + + // if domain is in list, mark as pre-existent (0) + for (ptr = SearchList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->domain, domain)) + { + if (ptr->flag != 1) ptr->flag = 0; // gracefully handle duplicates - if it is already marked as add, don't bump down to preexistent + break; + } + + // if domain not in list, add to list, mark as add (1) + if (!ptr) + { + new = mallocL("MarkSearchListElem - SearchListElem", sizeof(SearchListElem)); + if (!new) { LogMsg("ERROR: MarkSearchListElem - malloc"); return; } + bzero(new, sizeof(SearchListElem)); + AssignDomainName(new->domain, *domain); + new->flag = 1; // add + new->next = SearchList; + SearchList = new; + } + } + mDNSlocal mStatus RegisterSearchDomains(mDNS *const m, CFDictionaryRef dict) { + struct ifaddrs *ifa = NULL; int i, count; - CFArrayRef values; domainname domain; - char buf[MAX_ESCAPED_DOMAIN_NAME]; + char buf[MAX_ESCAPED_DOMAIN_NAME]; CFStringRef s; - SearchListElem *new, *ptr, *prev, *freeSLPtr; - AuthRecordListElem *arList; + SearchListElem *ptr, *prev, *freeSLPtr; + ARListElem *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) + // step 1: mark each elem for removal (-1), unless we aren't passed a dictionary in which case we mark as preexistent + for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag = dict ? -1 : 0; + + // get all the domains from "Search Domains" field of sharing prefs + if (dict) { - count = CFArrayGetCount(values); - for (i = 0; i < count; i++) + CFArrayRef searchdomains = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains); + if (searchdomains) { - s = CFArrayGetValueAtIndex(values, i); - if (!s) { LogMsg("ERROR: RegisterNameServers - CFArrayGetValueAtIndex"); break; } - if (!CFStringGetCString(s, buf, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingASCII)) + count = CFArrayGetCount(searchdomains); + for (i = 0; i < count; i++) { - LogMsg("ERROR: RegisterNameServers - CFStringGetCString"); - continue; - } - if (!MakeDomainNameFromDNSNameString(&domain, buf)) + s = CFArrayGetValueAtIndex(searchdomains, i); + if (!s) { LogMsg("ERROR: RegisterSearchDomains - CFArrayGetValueAtIndex"); break; } + if (!CFStringGetCString(s, buf, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingUTF8)) + { + LogMsg("ERROR: RegisterSearchDomains - CFStringGetCString"); + continue; + } + if (!MakeDomainNameFromDNSNameString(&domain, buf)) + { + LogMsg("ERROR: RegisterSearchDomains - invalid search domain %s", buf); + continue; + } + MarkSearchListElem(&domain); + } + } + CFStringRef dname = CFDictionaryGetValue(dict, kSCPropNetDNSDomainName); + if (dname) + { + if (CFStringGetCString(dname, buf, MAX_ESCAPED_DOMAIN_NAME, kCFStringEncodingUTF8)) { - 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) + if (MakeDomainNameFromDNSNameString(&domain, buf)) MarkSearchListElem(&domain); + else LogMsg("ERROR: RegisterSearchDomains - invalid domain %s", buf); + } + else LogMsg("ERROR: RegisterSearchDomains - CFStringGetCString"); + } + } + + // Construct reverse-map search domains + ifa = myGetIfAddrs(1); + while (ifa) + { + mDNSAddr addr; + if (ifa->ifa_addr->sa_family == AF_INET && !SetupAddr(&addr, ifa->ifa_addr) && !IsPrivateV4Addr(&addr) && !(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_netmask) + { + mDNSAddr netmask; + char buffer[256]; + if (!SetupAddr(&netmask, ifa->ifa_netmask)) { - 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; + sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", addr.ip.v4.b[3] & netmask.ip.v4.b[3], + addr.ip.v4.b[2] & netmask.ip.v4.b[2], + addr.ip.v4.b[1] & netmask.ip.v4.b[1], + addr.ip.v4.b[0] & netmask.ip.v4.b[0]); + MakeDomainNameFromDNSNameString(&domain, buffer); + MarkSearchListElem(&domain); } } + ifa = ifa->ifa_next; } + + if (DynDNSRegDomain.c[0]) MarkSearchListElem(&DynDNSRegDomain); // implicitly browse reg domain too (no-op if same as BrowseDomain) + // 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 + { + mDNS_StopQuery(m, &ptr->BrowseQ); + mDNS_StopQuery(m, &ptr->RegisterQ); + mDNS_StopQuery(m, &ptr->DefBrowseQ); + mDNS_StopQuery(m, &ptr->DefRegisterQ); + mDNS_StopQuery(m, &ptr->LegacyBrowseQ); + + // deregister records generated from answers to the query arList = ptr->AuthRecs; ptr->AuthRecs = NULL; while (arList) @@ -1679,151 +2411,271 @@ mDNSlocal mStatus RegisterSearchDomains(mDNS *const m, CFDictionaryRef dict) if (err) LogMsg("ERROR: RegisterSearchDomains mDNS_Deregister returned %d", err); } - // remove elem from list, delete + // remove elem from list, delete if (prev) prev->next = ptr->next; else SearchList = ptr->next; freeSLPtr = ptr; ptr = ptr->next; - freeL("RegisterNameServers - freeSLPtr", freeSLPtr); + freeL("RegisterSearchDomains - 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); + mStatus err1, err2, err3, err4, err5; + err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + err3 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + err5 = mDNS_GetDomains(m, &ptr->LegacyBrowseQ, mDNS_DomainTypeBrowseLegacy, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr); + if (err1 || err2 || err3 || err4 || err5) + LogMsg("GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeBrowse)\n" + "%d (mDNS_DomainTypeBrowseDefault)\n" + "%d (mDNS_DomainTypeRegistration)\n" + "%d (mDNS_DomainTypeRegistrationDefault)" + "%d (mDNS_DomainTypeBrowseLegacy)\n", + ptr->domain.c, err1, err2, err3, err4, err5); ptr->flag = 0; } - if (ptr->flag) { LogMsg("RegisterNameServers - unknown flag %d. Skipping.", ptr->flag); } + if (ptr->flag) { LogMsg("RegisterSearchDomains - 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; +//!!!KRS here is where we will give success/failure notification to the UI +mDNSlocal void SCPrefsDynDNSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) + { + (void)m; // unused + debugf("SCPrefsDynDNSCallback: result %d for registration of name %##s", result, rr->resrec.name.c); + SetDDNSNameStatus(&rr->resrec.name, result); } +mDNSlocal void SetSecretForDomain(mDNS *m, const domainname *domain) + { + OSStatus err = 0; + SecKeychainRef SysKeychain = NULL; + SecKeychainItemRef KeychainItem; + char dstring[MAX_ESCAPED_DOMAIN_NAME]; + mDNSu32 secretlen; + void *secret = NULL; + domainname *d, canon; + int i, dlen; + + // canonicalize name by converting to lower case (keychain and some name servers are case sensitive) + ConvertDomainNameToCString(domain, dstring); + dlen = strlen(dstring); + for (i = 0; i < dlen; i++) dstring[i] = tolower(dstring[i]); // canonicalize -> lower case + MakeDomainNameFromDNSNameString(&canon, dstring); + d = &canon; + + err = SecKeychainOpen(SYS_KEYCHAIN_PATH, &SysKeychain); + if (err) { LogMsg("SetSecretForDomain: couldn't open system keychain (error %d)", err); return; } + // find longest-match key ("account") name, excluding last label (e.g. excluding ".com") + while (d->c[0] && *(d->c + d->c[0] + 1)) + { + if (!ConvertDomainNameToCString(d, dstring)) { LogMsg("SetSecretForDomain: bad domain %##s", d->c); return; } + dlen = strlen(dstring); + if (dstring[dlen-1] == '.') { dstring[dlen-1] = '\0'; dlen--; } // chop trailing dot + err = SecKeychainFindGenericPassword(SysKeychain, strlen(DYNDNS_KEYCHAIN_SERVICE), DYNDNS_KEYCHAIN_SERVICE, dlen, dstring, &secretlen, &secret, &KeychainItem); + if (!err) + { + debugf("Setting shared secret for zone %s with key %##s", dstring, d->c); + mDNS_SetSecretForZone(m, d, d, secret, secretlen, mDNStrue); + free(secret); + return; + } + if (err == errSecItemNotFound) d = (domainname *)(d->c + d->c[0] + 1); + else + { + if (err == errSecNoSuchKeychain) debugf("SetSecretForDomain: keychain not found"); + else LogMsg("SetSecretForDomain: SecKeychainFindGenericPassword returned error %d", err); + return; + } + } + } -mDNSlocal void DNSConfigChanged(SCDynamicStoreRef session, CFArrayRef changes, void *context) +mDNSlocal void DynDNSConfigChanged(mDNS *const m) { - mDNS *m = context; + static mDNSBool LegacyNATInitialized = mDNSfalse; + uDNS_GlobalInfo *u = &m->uDNS_info; CFDictionaryRef dict; CFStringRef key; + domainname BrowseDomain, RegDomain, fqdn; + + // get fqdn, zone from SCPrefs + GetUserSpecifiedDDNSConfig(&fqdn, &RegDomain, &BrowseDomain); + if (!fqdn.c[0] && !RegDomain.c[0]) ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &fqdn, &RegDomain); - 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 + if (!SameDomainName(&BrowseDomain, &DynDNSBrowseDomain)) + { + if (DynDNSBrowseDomain.c[0]) SetSCPrefsBrowseDomain(m, &DynDNSBrowseDomain, mDNSfalse); + AssignDomainName(DynDNSBrowseDomain, BrowseDomain); + if (DynDNSBrowseDomain.c[0]) SetSCPrefsBrowseDomain(m, &DynDNSBrowseDomain, mDNStrue); + } + + if (!SameDomainName(&RegDomain, &DynDNSRegDomain)) + { + if (DynDNSRegDomain.c[0]) RemoveDefRegDomain(&DynDNSRegDomain); + AssignDomainName(DynDNSRegDomain, RegDomain); + if (DynDNSRegDomain.c[0]) { SetSecretForDomain(m, &DynDNSRegDomain); AddDefRegDomain(&DynDNSRegDomain); } + } + + if (!SameDomainName(&fqdn, &DynDNSHostname)) + { + if (DynDNSHostname.c[0]) mDNS_RemoveDynDNSHostName(m, &DynDNSHostname); + AssignDomainName(DynDNSHostname, fqdn); + if (DynDNSHostname.c[0]) + { + SetSecretForDomain(m, &fqdn); // no-op if "zone" secret, above, is to be used for hostname + mDNS_AddDynDNSHostName(m, &DynDNSHostname, SCPrefsDynDNSCallback, NULL); + SetDDNSNameStatus(&DynDNSHostname, 1); + } + } + // get DNS settings + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:DynDNSConfigChanged"), NULL, NULL); + if (!store) return; + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); - if (!key) { LogMsg("ERROR: DNSConfigChanged - SCDynamicStoreKeyCreateNetworkGlobalEntity"); return; } - dict = SCDynamicStoreCopyValue(session, key); + if (!key) { LogMsg("ERROR: DNSConfigChanged - SCDynamicStoreKeyCreateNetworkGlobalEntity"); CFRelease(store); return; } + dict = SCDynamicStoreCopyValue(store, key); CFRelease(key); - if (dict) + + // handle any changes to search domains and DNS server addresses + if (RegisterSplitDNS(m) != mStatus_NoError) + if (dict) RegisterNameServers(m, dict); // fall back to non-split DNS aware configuration on failure + RegisterSearchDomains(m, dict); // note that we register name servers *before* search domains + if (dict) CFRelease(dict); + + // get IPv4 settings + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,kSCDynamicStoreDomainState, kSCEntNetIPv4); + if (!key) { LogMsg("ERROR: RouterChanged - SCDynamicStoreKeyCreateNetworkGlobalEntity"); CFRelease(store); return; } + dict = SCDynamicStoreCopyValue(store, key); + CFRelease(key); + CFRelease(store); + if (!dict) + { mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL); return; } // lost v4 + + // handle router changes + mDNSAddr r; + char buf[256]; + r.type = mDNSAddrType_IPv4; + r.ip.v4.NotAnInteger = 0; + CFStringRef router = CFDictionaryGetValue(dict, kSCPropNetIPv4Router); + if (router) { - RegisterDNSConfig(m, dict, kSCPropNetDNSServerAddresses); - RegisterDNSConfig(m, dict, kSCPropNetDNSSearchDomains); - CFRelease(dict); - } - if (mDNS_DNSRegistered(m)) mDNS_GenerateGlobalFQDN(m); - // no-op if label & domain are unchanged + if (!CFStringGetCString(router, buf, 256, kCFStringEncodingUTF8)) + LogMsg("Could not convert router to CString"); + else inet_aton(buf, (struct in_addr *)&r.ip.v4); + } + + // handle primary interface changes + CFStringRef primary = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface); + if (primary) + { + struct ifaddrs *ifa = myGetIfAddrs(1); + + if (!CFStringGetCString(primary, buf, 256, kCFStringEncodingUTF8)) + { LogMsg("Could not convert router to CString"); goto error; } + + // find primary interface in list + while (ifa) + { + if (ifa->ifa_addr->sa_family == AF_INET && !strcmp(buf, ifa->ifa_name)) + { + mDNSAddr ip; + SetupAddr(&ip, ifa->ifa_addr); + if (ip.ip.v4.b[0] == 169 && ip.ip.v4.b[1] == 254) + { mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL); break; } // primary IP is link-local + if (ip.ip.v4.NotAnInteger != u->PrimaryIP.ip.v4.NotAnInteger || + r.ip.v4.NotAnInteger != u->Router.ip.v4.NotAnInteger) + { + if (LegacyNATInitialized) { LegacyNATDestroy(); LegacyNATInitialized = mDNSfalse; } + if (IsPrivateV4Addr(&ip)) + { + mStatus err = LegacyNATInit(); + if (err) LogMsg("ERROR: LegacyNATInit"); + else LegacyNATInitialized = mDNStrue; + } + mDNS_SetPrimaryInterfaceInfo(m, &ip, r.ip.v4.NotAnInteger ? &r : NULL); + break; + } + } + ifa = ifa->ifa_next; + } + } + + error: + CFRelease(dict); } -mDNSlocal mStatus WatchForDNSChanges(mDNS *const m) - { - CFStringRef key; - CFMutableArrayRef keyList; - CFRunLoopSourceRef rls; - SCDynamicStoreRef session; - SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; +mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) + { + LogOperation("*** Network Configuration Change ***"); + MarkAllInterfacesInactive(m); + UpdateInterfaceList(m); + int nDeletions = ClearInactiveInterfaces(m); + int nAdditions = SetupActiveInterfaces(m); + if (nDeletions || nAdditions) mDNS_UpdateLLQs(m); + DynDNSConfigChanged(m); - 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; - } + if (m->MainCallback) + m->MainCallback(m, mStatus_ConfigChanged); + } mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) { (void)store; // Parameter not used (void)changedKeys; // Parameter not used - debugf("*** Network Configuration Change ***"); - + LogOperation("*** NetworkChanged ***"); mDNS *const m = (mDNS *const)context; - MarkAllInterfacesInactive(m); - UpdateInterfaceList(m); - ClearInactiveInterfaces(m); - SetupActiveInterfaces(m); - - if (m->MainCallback) - m->MainCallback(m, mStatus_ConfigChanged); + mDNS_Lock(m); + m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); + if (!m->SuppressSending || + m->SuppressSending - m->p->NetworkChanged < 0) + m->SuppressSending = m->p->NetworkChanged; + mDNS_Unlock(m); } mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) { mStatus err = -1; SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder"), NetworkChanged, &context); + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:WatchForNetworkChanges"), NetworkChanged, &context); CFStringRef key1 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); CFStringRef key2 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); CFStringRef key3 = SCDynamicStoreKeyCreateComputerName(NULL); CFStringRef key4 = SCDynamicStoreKeyCreateHostNames(NULL); + CFStringRef key5 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); 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 (!store) { LogMsg("SCDynamicStoreCreate failed: %s", 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(keys, key5); + CFArrayAppendValue(keys, CFSTR("Setup:/Network/DynamicDNS")); CFArrayAppendValue(patterns, pattern1); CFArrayAppendValue(patterns, pattern2); + CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort")); if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) - { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError())); goto error; } + { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; } m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); - if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s\n", SCErrorString(SCError())); goto error; } + if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); m->p->Store = store; @@ -1838,6 +2690,7 @@ exit: if (key2) CFRelease(key2); if (key3) CFRelease(key3); if (key4) CFRelease(key4); + if (key5) CFRelease(key5); if (pattern1) CFRelease(pattern1); if (pattern2) CFRelease(pattern2); if (keys) CFRelease(keys); @@ -1852,14 +2705,24 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag (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; + 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"); + // If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now + if (m->SleepState) mDNSCoreMachineSleep(m, false); + // Just to be safe, also make sure our interface list is fully up to date, in case we + // haven't yet received the System Configuration Framework "network changed" event that + // we expect to receive some time shortly after the kIOMessageSystemWillPowerOn message + mDNSMacOSXNetworkChanged(m); break; // E0000300 + case kIOMessageSystemWillRestart: debugf("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310 + case kIOMessageSystemWillPowerOn: debugf("PowerChanged kIOMessageSystemWillPowerOn"); + // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake + mDNSMacOSXNetworkChanged(m); mDNSCoreMachineSleep(m, false); break; // E0000320 + default: debugf("PowerChanged unknown message %X", messageType); break; } IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); } @@ -1877,132 +2740,12 @@ 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; -mDNSexport mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) +mDNSexport int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) { int major = 0, minor = 0; char letter = 0, prodname[256]="Mac OS X", prodvers[256]="", buildver[256]="?"; @@ -2025,119 +2768,28 @@ mDNSexport mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) // 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) +mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void) { - int err; + int err = -1; 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; - - // 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]) + if (s < 3) + LogMsg("mDNSPlatformInit_CanReceiveUnicast: socket error %d errno %d (%s)", s, errno, strerror(errno)); + else { - 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); - } + 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); } - - 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); + if (err) LogMsg("No unicast UDP responses"); + else debugf("Unicast UDP responses okay"); + return(err == 0); } - +// Callback for the _legacy._browse queries - add answer to list of domains to search for empty-string browses mDNSlocal void FoundDefBrowseDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { DNameListElem *ptr, *prev, *new; @@ -2148,9 +2800,11 @@ mDNSlocal void FoundDefBrowseDomain(mDNS *const m, DNSQuestion *question, const { new = mallocL("FoundDefBrowseDomain", sizeof(DNameListElem)); if (!new) { LogMsg("ERROR: malloc"); return; } - strcpy(new->name.c, answer->rdata->u.name.c); + AssignDomainName(new->name, answer->rdata->u.name); new->next = DefBrowseList; DefBrowseList = new; + DefaultBrowseDomainChanged(&new->name, mDNStrue); + udsserver_default_browse_domain_changed(&new->name, mDNStrue); return; } else @@ -2161,18 +2815,64 @@ mDNSlocal void FoundDefBrowseDomain(mDNS *const m, DNSQuestion *question, const { if (SameDomainName(&ptr->name, &answer->rdata->u.name)) { + DefaultBrowseDomainChanged(&ptr->name, mDNSfalse); + udsserver_default_browse_domain_changed(&ptr->name, mDNSfalse); if (prev) prev->next = ptr->next; else DefBrowseList = ptr->next; - freeL("FoundDefBrowseDomain", ptr); + 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); - } + } } +// Add or remove a user-specified domain to the list of empty-string browse domains +// Also register a non-legacy _browse PTR record so that the domain appears in enumeration lists +mDNSlocal void SetSCPrefsBrowseDomain(mDNS *m, const domainname *d, mDNSBool add) + { + AuthRecord rec; + + // Create dummy record pointing to the domain to be added/removed + mDNS_SetupResourceRecord(&rec, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, mDNSNULL, mDNSNULL); + AssignDomainName(rec.resrec.rdata->u.name, *d); + + // add/remove the "_legacy" entry + MakeDomainNameFromDNSNameString(&rec.resrec.name, "_legacy._browse._dns-sd._udp.local."); + FoundDefBrowseDomain(m, &LegacyBrowseDomainQ, &rec.resrec, add); + + if (add) + { + // allocate/register a non-legacy _browse PTR record + ARListElem *ptr = mallocL("ARListElem", sizeof(*ptr)); + mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, ptr); + MakeDomainNameFromDNSNameString(&ptr->ar.resrec.name, "_browse._dns-sd._udp.local."); + AssignDomainName(ptr->ar.resrec.rdata->u.name, *d); + mStatus err = mDNS_Register(m, &ptr->ar); + if (err) + { + LogMsg("SetSCPrefsBrowseDomain: mDNS_Register returned error %d", err); + freeL("ARListElem", ptr); + } + else + { + ptr->next = SCPrefBrowseDomains; + SCPrefBrowseDomains = ptr; + } + } + else + { + ARListElem **remove = &SCPrefBrowseDomains; + while (*remove && !SameDomainName(&(*remove)->ar.resrec.rdata->u.name, d)) remove = &(*remove)->next; + if (!*remove) { LogMsg("SetSCPrefsBrowseDomain (remove) - domain %##s not found!", d->c); return; } + mDNS_Deregister(m, &(*remove)->ar); + *remove = (*remove)->next; + } + } + + // 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) @@ -2186,22 +2886,15 @@ mDNSlocal void FoundDefBrowseDomain(mDNS *const m, DNSQuestion *question, const 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); + err = mDNS_GetDomains(m, &LegacyBrowseDomainQ, mDNS_DomainTypeBrowseLegacy, 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); - + SetSCPrefsBrowseDomain(m, &localdomain, mDNStrue); return mStatus_NoError; } - + mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) { mStatus err; @@ -2217,7 +2910,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) char HINFO_SWstring[256] = ""; if (mDNSMacOSXSystemBuildNumber(HINFO_SWstring) < 7) m->KnownBugs |= mDNS_KnownBug_PhantomInterfaces; - if (mDNSPlatformInit_ReceiveUnicast()) m->CanReceiveUnicast = mDNStrue; + if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); @@ -2235,11 +2928,22 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) 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); + err = SetupSocket(m, &m->p->unicastsockets, mDNSfalse, &zeroAddr, AF_INET); + err = SetupSocket(m, &m->p->unicastsockets, mDNSfalse, &zeroAddr, AF_INET6); + + struct sockaddr_in s4; + struct sockaddr_in6 s6; + int n4 = sizeof(s4); + int n6 = sizeof(s6); + if (getsockname(m->p->unicastsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno)); + else m->UnicastPort4.NotAnInteger = s4.sin_port; + if (getsockname(m->p->unicastsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno)); + else m->UnicastPort6.NotAnInteger = s6.sin6_port; m->p->InterfaceList = mDNSNULL; m->p->userhostlabel.c[0] = 0; + m->p->usernicelabel.c[0] = 0; + m->p->NotifyUser = 0; UpdateInterfaceList(m); SetupActiveInterfaces(m); @@ -2248,16 +2952,12 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) err = WatchForPowerChanges(m); if (err) return err; - - err = WatchForDNSChanges(m); - InitDNSConfig(m); + DynDNSRegDomain.c[0] = '\0'; + DynDNSBrowseDomain.c[0] = '\0'; + DynDNSConfigChanged(m); // Get initial DNS configuration - m->uDNS_info.ServiceRegDomain[0] = '\0'; - m->uDNS_info.NameRegDomain[0] = '\0'; - InitAuthInfo(m); - ReadRegDomainFromConfig(m); - + InitDNSConfig(m); return(err); } @@ -2279,8 +2979,8 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) CFRunLoopSourceInvalidate(m->p->PowerRLS); CFRelease(m->p->PowerRLS); IODeregisterForSystemPower(&m->p->PowerNotifier); - m->p->PowerConnection = NULL; - m->p->PowerNotifier = NULL; + m->p->PowerConnection = 0; + m->p->PowerNotifier = 0; m->p->PowerRLS = NULL; } @@ -2299,9 +2999,14 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) CloseSocketSet(&m->p->unicastsockets); } -mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; +mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) + { + return(mach_absolute_time()); + } + +mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; -mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) +mDNSexport mStatus mDNSPlatformTimeInit(void) { // Notes: Typical values for mach_timebase_info: // tbi.numer = 1000 million @@ -2323,16 +3028,27 @@ mDNSexport mStatus mDNSPlatformTimeInit(mDNSs32 *timenow) // 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); + if (result == KERN_SUCCESS) clockdivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer; + return(result); } -mDNSexport mDNSs32 mDNSPlatformTimeNow(void) +mDNSexport mDNSs32 mDNSPlatformRawTime(void) { - if (clockdivisor == 0) { LogMsg("mDNSPlatformTimeNow called before mDNSPlatformTimeInit"); return(0); } - return((mDNSs32)(mach_absolute_time() / clockdivisor)); + if (clockdivisor == 0) { LogMsg("mDNSPlatformRawTime called before mDNSPlatformTimeInit"); return(0); } + + static uint64_t last_mach_absolute_time = 0; + uint64_t this_mach_absolute_time = mach_absolute_time(); + if ((int64_t)this_mach_absolute_time - (int64_t)last_mach_absolute_time < 0) + { + LogMsg("mDNSPlatformRawTime: last_mach_absolute_time %08X%08X", last_mach_absolute_time); + LogMsg("mDNSPlatformRawTime: this_mach_absolute_time %08X%08X", this_mach_absolute_time); + // Update last_mach_absolute_time *before* calling NotifyOfElusiveBug() + last_mach_absolute_time = this_mach_absolute_time; + NotifyOfElusiveBug("mach_absolute_time went backwards!", 3438376, ""); + } + last_mach_absolute_time = this_mach_absolute_time; + + return((mDNSs32)(this_mach_absolute_time / clockdivisor)); } mDNSexport mDNSs32 mDNSPlatformUTC(void) diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index ba07125..2a81939 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,43 @@ Change History (most recent first): $Log: mDNSMacOSX.h,v $ +Revision 1.48 2004/12/07 01:31:31 cheshire +mDNSMacOSXSystemBuildNumber() returns int, not mDNSBool + +Revision 1.47 2004/11/30 03:24:03 cheshire + Defer processing network configuration changes until configuration has stabilized + +Revision 1.46 2004/11/03 03:45:16 cheshire + mDNSResponder does not inform user of Computer Name collisions + +Revision 1.45 2004/10/28 00:53:57 cheshire +Export mDNSMacOSXNetworkChanged() so it's callable from outside this mDNSMacOSX.c; +Add LogOperation() call to record when we get network change events + +Revision 1.44 2004/10/23 01:16:01 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.43 2004/10/15 23:00:18 ksekar + Need to update LLQs on location changes + +Revision 1.42 2004/10/04 05:56:04 cheshire + mDNSResponder doesn't respond to certain AirPort changes + +Revision 1.41 2004/09/30 00:24:59 ksekar + Dynamically update default registration domains on config change + +Revision 1.40 2004/09/17 01:08:52 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.39 2004/08/18 17:35:41 ksekar +: Feature #9586: Need support for Legacy NAT gateways + +Revision 1.38 2004/07/13 21:24:25 rpantos +Fix for . + Revision 1.37 2004/06/04 08:58:30 ksekar : Keychain integration for secure dynamic update @@ -36,7 +71,7 @@ Tidy up all checkin comments to use consistent "" format 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" +from mDNSMacOSX.h to mDNSEmbeddedAPI.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. @@ -116,10 +151,10 @@ 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 -: Feature: New Rendezvous APIs (#7875) +: Feature: New DNS-SD 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. +DNS-SD APIs, and integrated with existing Mach-port based daemon. Revision 1.9 2003/06/10 01:14:11 cheshire New APIs require a mDNSPlatformInterfaceIDfromInterfaceIndex() call @@ -167,7 +202,7 @@ Defines mDNS_PlatformSupport_struct for OS X #include #include #include -#include "mDNSClientAPI.h" // for domain name structure +#include "mDNSEmbeddedAPI.h" // for domain name structure typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX; @@ -192,6 +227,7 @@ struct NetworkInterfaceInfoOSX_struct // 2 = exists, but McastTxRx state changed char *ifa_name; // Memory for this is allocated using malloc mDNSu32 scope_id; // interface index / IPv6 scope ID + mDNSEthAddr BSSID; // BSSID of 802.11 base station, if applicable u_short sa_family; mDNSBool Multicast; CFSocketSet ss; @@ -202,6 +238,9 @@ struct mDNS_PlatformSupport_struct NetworkInterfaceInfoOSX *InterfaceList; CFSocketSet unicastsockets; domainlabel userhostlabel; + domainlabel usernicelabel; + mDNSs32 NotifyUser; + mDNSs32 NetworkChanged; SCDynamicStoreRef Store; CFRunLoopSourceRef StoreRLS; io_connect_t PowerConnection; @@ -209,10 +248,19 @@ struct mDNS_PlatformSupport_struct CFRunLoopSourceRef PowerRLS; }; -extern mDNSBool mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); +extern void mDNSMacOSXNetworkChanged(mDNS *const m); +extern int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); extern const char mDNSResponderVersionString[]; +// Legacy NAT Traversal Support Setup/Teardown +extern int LegacyNATDestroy(void); +extern int LegacyNATInit(void); + +// Allow platform layer to tell daemon when default registration/browse domains +extern void DefaultRegDomainChanged(const domainname *d, mDNSBool add); +extern void DefaultBrowseDomainChanged(const domainname *d, mDNSBool add); + #ifdef __cplusplus } #endif diff --git a/mDNSMacOSX/CFSocketPuma.c b/mDNSMacOSX/mDNSMacOSXPuma.c similarity index 96% rename from mDNSMacOSX/CFSocketPuma.c rename to mDNSMacOSX/mDNSMacOSXPuma.c index 3a7e266..7764e2a 100644 --- a/mDNSMacOSX/CFSocketPuma.c +++ b/mDNSMacOSX/mDNSMacOSXPuma.c @@ -3,8 +3,6 @@ * * @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 @@ -24,7 +22,7 @@ * * 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 + * in mDNSMacOSX.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 @@ -32,7 +30,10 @@ Change History (most recent first): -$Log: CFSocketPuma.c,v $ +$Log: mDNSMacOSXPuma.c,v $ +Revision 1.5 2004/09/20 23:52:02 cheshire +CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c + Revision 1.4 2003/08/12 19:56:25 cheshire Update to APSL 2.0 @@ -43,7 +44,7 @@ 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 +Move Puma support to mDNSMacOSXPuma.c */ diff --git a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj index 1a2786a..8ba74f8 100644 --- a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj @@ -11,15 +11,9 @@ path = mDNSMacOSX.h; refType = 4; }; - 004EFB9604CC78130CCA2C71 = { - fileEncoding = 4; - isa = PBXFileReference; - name = dnssd_clientstub.c; - path = ../mDNSShared/dnssd_clientstub.c; - refType = 2; - }; 00AD62A3032D799A0CCA2C71 = { buildPhases = ( + FFF7174A07614A8600E10551, 00AD62A4032D799A0CCA2C71, 00AD62AC032D799A0CCA2C71, 00AD62B3032D799A0CCA2C71, @@ -28,12 +22,13 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; 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)"; - OTHER_LDFLAGS = ""; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -D_LEGACY_NAT_TRAVERSAL_ -DMDNS_DEBUGMSGS=1"; + OTHER_LDFLAGS = "-weak-ldnsinfo"; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder.debug; REZ_EXECUTABLE = YES; @@ -72,14 +67,16 @@ 00AD62AD032D799A0CCA2C71, 00AD62AE032D799A0CCA2C71, 00AD62AF032D799A0CCA2C71, - 00AD62B0032D799A0CCA2C71, - F5E11B5E04A28126019798ED, - F525E72B04AA167A01F1CF4D, - DBAAFE2B057E8F4D0085CAD0, - DBAAFE2E057E8F660085CAD0, - 7F18A9FA0587CEF6001880B3, 7F18A9FB0587CEF6001880B3, + 7F18A9FA0587CEF6001880B3, 7F461DB7062DBF2900672BF3, + DBAAFE2E057E8F660085CAD0, + DBAAFE2B057E8F4D0085CAD0, + F525E72B04AA167A01F1CF4D, + F5E11B5E04A28126019798ED, + FFCB6D75075D595E00B8AF62, + 00AD62B0032D799A0CCA2C71, + 7FC8F9D606D14E66007E879D, 00AD62B1032D799A0CCA2C71, ); isa = PBXSourcesBuildPhase; @@ -174,9 +171,11 @@ SECTORDER_FLAGS = ""; }; dependencies = ( + FF25795106C9AB1D00376F7B, 00AD62BC032D7A160CCA2C71, 00AD62BD032D7A1B0CCA2C71, 00AD62BE032D7A1D0CCA2C71, + FF16238F07023BD2001AB7D7, FFD41DDB0664169900F0C438, FFD41DDC0664169B00F0C438, ); @@ -249,8 +248,10 @@ 08FB779FFE84155DC02AAC07, 00AD62A3032D799A0CCA2C71, 6575FC1C022EB76000000109, + FF1C919207021C84001048AB, DB2CC4530662DD6800335AB3, DB2CC4660662DF5C00335AB3, + FF25792906C9A70800376F7B, ); }; 08FB7794FE84155DC02AAC07 = { @@ -268,6 +269,7 @@ }; 08FB7795FE84155DC02AAC07 = { children = ( + 7FC8F9D406D14E66007E879D, 7F461DB5062DBF2900672BF3, F525E72804AA167501F1CF4D, F5E11B5A04A28126019798ED, @@ -280,10 +282,16 @@ DBAAFE29057E8F4D0085CAD0, 000753D303367C1C0CCA2C71, DBAAFE2C057E8F660085CAD0, + FFCB6D73075D539900B8AF62, FF0E0B5D065ADC7600FE4D9C, + FF1C919D07021D77001048AB, FF485D5105632E0000130380, + FFF4F63A06CFE4DD00459EFD, 7F18A9F60587CEF6001880B3, 7F18A9F70587CEF6001880B3, + FF25794606C9A8BF00376F7B, + FF25794C06C9A9D500376F7B, + FF25794F06C9AA8B00376F7B, ); isa = PBXGroup; name = "mDNS Server Sources"; @@ -304,22 +312,23 @@ }; 08FB779FFE84155DC02AAC07 = { buildPhases = ( + FF37BE9207614059003C0420, 08FB77A0FE84155DC02AAC07, 08FB77A1FE84155DC02AAC07, 08FB77A3FE84155DC02AAC07, 08FB77A5FE84155DC02AAC07, FF5A0AE705632EA600743C27, - FF0E0B5C065ADC3800FE4D9C, ); buildSettings = { FRAMEWORK_SEARCH_PATHS = ""; GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; 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)"; - OTHER_LDFLAGS = ""; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -D_LEGACY_NAT_TRAVERSAL_"; + OTHER_LDFLAGS = "-weak-ldnsinfo"; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNSResponder; REZ_EXECUTABLE = YES; @@ -350,14 +359,16 @@ 6575FC0D022EB18700000109, 6575FC0E022EB18700000109, 6575FBEA022EAF5A00000109, - 6575FBED022EAF7200000109, - F5E11B5C04A28126019798ED, - F525E72904AA167501F1CF4D, - DBAAFE2A057E8F4D0085CAD0, - DBAAFE2D057E8F660085CAD0, - 7F18A9F80587CEF6001880B3, 7F18A9F90587CEF6001880B3, + 7F18A9F80587CEF6001880B3, 7F461DB6062DBF2900672BF3, + DBAAFE2D057E8F660085CAD0, + DBAAFE2A057E8F4D0085CAD0, + F525E72904AA167501F1CF4D, + F5E11B5C04A28126019798ED, + FFCB6D74075D539900B8AF62, + 6575FBED022EAF7200000109, + 7FC8F9D506D14E66007E879D, 6575FBEE022EAF7200000109, ); isa = PBXSourcesBuildPhase; @@ -420,6 +431,8 @@ 00AD62B8032D799A0CCA2C71, DB2CC4670662DF5C00335AB3, FFD41DDA0664157900F0C438, + FF25794406C9A70800376F7B, + FF1C919B07021C84001048AB, ); isa = PBXGroup; name = Products; @@ -438,8 +451,8 @@ 654BE64F02B63B93000001D1 = { fileEncoding = 4; isa = PBXFileReference; - name = mDNSClientAPI.h; - path = ../mDNSCore/mDNSClientAPI.h; + name = mDNSEmbeddedAPI.h; + path = ../mDNSCore/mDNSEmbeddedAPI.h; refType = 4; }; 654BE65002B63B93000001D1 = { @@ -481,7 +494,7 @@ fileEncoding = 4; indentWidth = 4; isa = PBXFileReference; - path = CFSocket.c; + path = mDNSMacOSX.c; refType = 4; tabWidth = 4; usesTabs = 1; @@ -596,12 +609,14 @@ 6575FC19022EB76000000109, 6575FC1A022EB76000000109, 6575FC1B022EB76000000109, + FFF4F63C06CFE53300459EFD, ); buildSettings = { GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; INSTALL_PATH = /usr/bin; MACOSX_DEPLOYMENT_TARGET = 10.2; - OTHER_CFLAGS = "-no-cpp-precomp -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; PRODUCT_NAME = mDNS; @@ -613,9 +628,9 @@ dependencies = ( ); isa = PBXToolTarget; - name = "mDNS Command-Line tool"; + name = "mDNS command-line tool"; productInstallPath = /usr/bin; - productName = "Sample mDNS Client"; + productName = "mDNS command-line tool"; productReference = 6575FC1D022EB76000000109; }; 6575FC1D022EB76000000109 = { @@ -626,10 +641,10 @@ 6575FC1F022EB78C00000109 = { children = ( 6575FC20022EB7AA00000109, - 004EFB9604CC78130CCA2C71, + FF1C919F07021E3F001048AB, ); isa = PBXGroup; - name = SampleMulticastDNSClient; + name = "Command-Line Clients"; refType = 4; }; 6575FC20022EB7AA00000109 = { @@ -637,7 +652,7 @@ indentWidth = 4; isa = PBXFileReference; path = SamplemDNSClient.c; - refType = 4; + refType = 2; tabWidth = 4; usesTabs = 0; }; @@ -670,14 +685,14 @@ //7F3 //7F4 7F18A9F60587CEF6001880B3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSCommon.c; path = ../mDNSCore/DNSCommon.c; refType = 2; }; 7F18A9F70587CEF6001880B3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = uDNS.c; path = ../mDNSCore/uDNS.c; @@ -708,7 +723,7 @@ }; }; 7F461DB5062DBF2900672BF3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSDigest.c; path = ../mDNSCore/DNSDigest.c; @@ -744,6 +759,24 @@ settings = { }; }; + 7FC8F9D406D14E66007E879D = { + fileEncoding = 4; + isa = PBXFileReference; + path = LegacyNATTraversal.c; + refType = 2; + }; + 7FC8F9D506D14E66007E879D = { + fileRef = 7FC8F9D406D14E66007E879D; + isa = PBXBuildFile; + settings = { + }; + }; + 7FC8F9D606D14E66007E879D = { + fileRef = 7FC8F9D406D14E66007E879D; + isa = PBXBuildFile; + settings = { + }; + }; //7F0 //7F1 //7F2 @@ -775,91 +808,91 @@ refType = 4; }; DB2CC4430662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = BaseListener.java; path = ../mDNSShared/Java/BaseListener.java; refType = 2; }; DB2CC4440662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = BrowseListener.java; path = ../mDNSShared/Java/BrowseListener.java; refType = 2; }; DB2CC4450662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSRecord.java; path = ../mDNSShared/Java/DNSRecord.java; refType = 2; }; DB2CC4460662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSSD.java; path = ../mDNSShared/Java/DNSSD.java; refType = 2; }; DB2CC4470662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSSDException.java; path = ../mDNSShared/Java/DNSSDException.java; refType = 2; }; DB2CC4480662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSSDRegistration.java; path = ../mDNSShared/Java/DNSSDRegistration.java; refType = 2; }; DB2CC4490662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DNSSDService.java; path = ../mDNSShared/Java/DNSSDService.java; refType = 2; }; DB2CC44A0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = DomainListener.java; path = ../mDNSShared/Java/DomainListener.java; refType = 2; }; DB2CC44B0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = JNISupport.c; path = ../mDNSShared/Java/JNISupport.c; refType = 2; }; DB2CC44C0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = QueryListener.java; path = ../mDNSShared/Java/QueryListener.java; refType = 2; }; DB2CC44D0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = RegisterListener.java; path = ../mDNSShared/Java/RegisterListener.java; refType = 2; }; DB2CC44E0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = ResolveListener.java; path = ../mDNSShared/Java/ResolveListener.java; refType = 2; }; DB2CC44F0662DD1100335AB3 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = TXTRecord.java; path = ../mDNSShared/Java/TXTRecord.java; @@ -909,6 +942,7 @@ buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; + GCC_VERSION = 3.3; INSTALL_PATH = /System/Library/Java/Extensions; JAVA_ARCHIVE_CLASSES = YES; JAVA_ARCHIVE_COMPRESSION = YES; @@ -1062,7 +1096,8 @@ DEBUGGING_SYMBOLS = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; - 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\""; + GCC_VERSION = 3.3; + 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\""; INSTALL_PATH = /usr/lib/java; LIBRARY_STYLE = DYNAMIC; OTHER_CFLAGS = ""; @@ -1107,7 +1142,7 @@ }; }; DBAAFE29057E8F4D0085CAD0 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = mDNSDebug.c; path = ../mDNSShared/mDNSDebug.c; @@ -1126,7 +1161,7 @@ }; }; DBAAFE2C057E8F660085CAD0 = { - fileEncoding = 30; + fileEncoding = 4; isa = PBXFileReference; name = GenLinkedList.c; path = ../mDNSShared/GenLinkedList.c; @@ -1239,29 +1274,329 @@ //FF2 //FF3 //FF4 - FF0E0B5C065ADC3800FE4D9C = { + FF0E0B5D065ADC7600FE4D9C = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNS.1; + path = ../mDNSShared/mDNS.1; + refType = 2; + }; + FF16238F07023BD2001AB7D7 = { + isa = PBXTargetDependency; + target = FF1C919207021C84001048AB; + }; + FF1C919207021C84001048AB = { + buildPhases = ( + FF1C919307021C84001048AB, + FF1C919407021C84001048AB, + FF1C919607021C84001048AB, + FF1C919807021C84001048AB, + FF1C919907021C84001048AB, + ); + buildSettings = { + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; + INSTALL_PATH = /usr/bin; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS) -I../mDNSShared"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "dns-sd"; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = "dns-sd command-line tool"; + productInstallPath = /usr/bin; + productName = "dns-sd command-line tool"; + productReference = FF1C919B07021C84001048AB; + }; + FF1C919307021C84001048AB = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919407021C84001048AB = { + buildActionMask = 2147483647; + files = ( + FF1C91A007021E40001048AB, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919607021C84001048AB = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919807021C84001048AB = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919907021C84001048AB = { buildActionMask = 8; dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( - FF0E0B5E065ADCA400FE4D9C, + FF1C919E07021D78001048AB, ); isa = PBXCopyFilesBuildPhase; runOnlyForDeploymentPostprocessing = 1; }; - FF0E0B5D065ADC7600FE4D9C = { + FF1C919B07021C84001048AB = { + isa = PBXExecutableFileReference; + path = "dns-sd"; + refType = 3; + }; + FF1C919D07021D77001048AB = { fileEncoding = 4; isa = PBXFileReference; - name = mDNS.1; - path = ../mDNSShared/mDNS.1; + name = "dns-sd.1"; + path = "../mDNSShared/dns-sd.1"; refType = 2; }; - FF0E0B5E065ADCA400FE4D9C = { - fileRef = FF0E0B5D065ADC7600FE4D9C; + FF1C919E07021D78001048AB = { + fileRef = FF1C919D07021D77001048AB; + isa = PBXBuildFile; + settings = { + }; + }; + FF1C919F07021E3F001048AB = { + fileEncoding = 4; + isa = PBXFileReference; + name = "dns-sd.c"; + path = "../Clients/dns-sd.c"; + refType = 2; + }; + FF1C91A007021E40001048AB = { + fileRef = FF1C919F07021E3F001048AB; + isa = PBXBuildFile; + settings = { + }; + }; + FF25792906C9A70800376F7B = { + buildPhases = ( + FF25792A06C9A70800376F7B, + FF25792D06C9A70800376F7B, + FF25793A06C9A70800376F7B, + FF25793F06C9A70800376F7B, + FF25794006C9A70800376F7B, + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_VERSION = 3.3; + 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 -mdynamic-no-pic -D__MACOSX__ -DmDNSResponderVersion=$(MVERS)"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = dnsextd; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = dnsextd; + productInstallPath = /usr/sbin; + productName = mDNSResponder; + productReference = FF25794406C9A70800376F7B; + }; + FF25792A06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + FF25792B06C9A70800376F7B, + FF25792C06C9A70800376F7B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25792B06C9A70800376F7B = { + fileRef = 6575FBFF022EAFBA00000109; isa = PBXBuildFile; settings = { }; }; + FF25792C06C9A70800376F7B = { + fileRef = F5E11B5B04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + FF25792D06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + FF25793606C9A70800376F7B, + FF25793806C9A70800376F7B, + FF25794706C9A8BF00376F7B, + FF25794906C9A97400376F7B, + FF25794A06C9A98700376F7B, + FF25794D06C9A9D500376F7B, + FF25794E06C9AA3000376F7B, + FF25795006C9AA8B00376F7B, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25793606C9A70800376F7B = { + fileRef = 7F18A9F60587CEF6001880B3; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793806C9A70800376F7B = { + fileRef = 7F461DB5062DBF2900672BF3; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793A06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + FF25793B06C9A70800376F7B, + FF25793C06C9A70800376F7B, + FF25793D06C9A70800376F7B, + FF25793E06C9A70800376F7B, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25793B06C9A70800376F7B = { + fileRef = 09AB6884FE841BABC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793C06C9A70800376F7B = { + fileRef = 65713D46025A293200000109; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793D06C9A70800376F7B = { + fileRef = 00CA213D02786FC30CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793E06C9A70800376F7B = { + fileRef = 7F869685066EE02400D2A2DC; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793F06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25794006C9A70800376F7B = { + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + FFF4F63B06CFE4DD00459EFD, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FF25794406C9A70800376F7B = { + isa = PBXExecutableFileReference; + path = dnsextd; + refType = 3; + }; + FF25794606C9A8BF00376F7B = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnsextd.c; + path = ../mDNSPosix/dnsextd.c; + refType = 2; + }; + FF25794706C9A8BF00376F7B = { + fileRef = FF25794606C9A8BF00376F7B; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794906C9A97400376F7B = { + fileRef = 7F18A9F70587CEF6001880B3; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794A06C9A98700376F7B = { + fileRef = DBAAFE29057E8F4D0085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794C06C9A9D500376F7B = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNSPosix.c; + path = ../mDNSPosix/mDNSPosix.c; + refType = 2; + }; + FF25794D06C9A9D500376F7B = { + fileRef = FF25794C06C9A9D500376F7B; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794E06C9AA3000376F7B = { + fileRef = DBAAFE2C057E8F660085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794F06C9AA8B00376F7B = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNSUNP.c; + path = ../mDNSPosix/mDNSUNP.c; + refType = 2; + }; + FF25795006C9AA8B00376F7B = { + fileRef = FF25794F06C9AA8B00376F7B; + isa = PBXBuildFile; + settings = { + }; + }; + FF25795106C9AB1D00376F7B = { + isa = PBXTargetDependency; + target = FF25792906C9A70800376F7B; + }; + FF37BE9207614059003C0420 = { + buildActionMask = 2147483647; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -e /usr/local/lib/libdnsinfo.a ]\nthen\nrm -f \"${OBJROOT}/libdnsinfo.a\"\nelse\ntouch ${OBJROOT}/empty.c\ncc ${OBJROOT}/empty.c -c -o \"${OBJROOT}/libdnsinfo.a\"\nrm -f ${OBJROOT}/empty.c\nfi"; + }; FF485D5105632E0000130380 = { fileEncoding = 4; isa = PBXFileReference; @@ -1285,6 +1620,25 @@ settings = { }; }; + FFCB6D73075D539900B8AF62 = { + fileEncoding = 4; + isa = PBXFileReference; + name = PlatformCommon.c; + path = ../mDNSShared/PlatformCommon.c; + refType = 2; + }; + FFCB6D74075D539900B8AF62 = { + fileRef = FFCB6D73075D539900B8AF62; + isa = PBXBuildFile; + settings = { + }; + }; + FFCB6D75075D595E00B8AF62 = { + fileRef = FFCB6D73075D539900B8AF62; + isa = PBXBuildFile; + settings = { + }; + }; FFD41DDA0664157900F0C438 = { includeInIndex = 0; isa = PBXZipArchiveReference; @@ -1316,6 +1670,48 @@ isa = PBXTargetDependency; target = DB2CC4530662DD6800335AB3; }; + FFF4F63A06CFE4DD00459EFD = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnsextd.8; + path = ../mDNSShared/dnsextd.8; + refType = 2; + }; + FFF4F63B06CFE4DD00459EFD = { + fileRef = FFF4F63A06CFE4DD00459EFD; + isa = PBXBuildFile; + settings = { + }; + }; + FFF4F63C06CFE53300459EFD = { + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + FFF4F63D06CFE54300459EFD, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FFF4F63D06CFE54300459EFD = { + fileRef = FF0E0B5D065ADC7600FE4D9C; + isa = PBXBuildFile; + settings = { + }; + }; + FFF7174A07614A8600E10551 = { + buildActionMask = 2147483647; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -e /usr/local/lib/libdnsinfo.a ]\nthen\nrm -f \"${OBJROOT}/libdnsinfo.a\"\nelse\ntouch ${OBJROOT}/empty.c\ncc ${OBJROOT}/empty.c -c -o \"${OBJROOT}/libdnsinfo.a\"\nrm -f ${OBJROOT}/empty.c\nfi"; + }; }; rootObject = 08FB7793FE84155DC02AAC07; } diff --git a/mDNSPosix/Client.c b/mDNSPosix/Client.c index 193272d..ad3480c 100755 --- a/mDNSPosix/Client.c +++ b/mDNSPosix/Client.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * 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 @@ -25,6 +24,20 @@ Change History (most recent first): $Log: Client.c,v $ +Revision 1.14 2004/11/30 22:37:00 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.13 2004/10/19 21:33:20 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.12 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + Revision 1.11 2003/11/17 20:14:32 cheshire Typo: Wrote "domC" where it should have said "domainC" @@ -70,7 +83,7 @@ First checkin #include #include -#include "mDNSClientAPI.h"// Defines the interface to the mDNS core code +#include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include "ExampleClientApp.h" @@ -236,7 +249,7 @@ int main(int argc, char **argv) MakeDomainNameFromDNSNameString(&type, gServiceType); MakeDomainNameFromDNSNameString(&domain, "local."); - status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, BrowseCallback, NULL); + status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, mDNSfalse, BrowseCallback, NULL); // Run the platform main event loop until the user types ^C. // The BrowseCallback routine is responsible for printing diff --git a/mDNSPosix/ExampleClientApp.c b/mDNSPosix/ExampleClientApp.c index 8561388..17843af 100644 --- a/mDNSPosix/ExampleClientApp.c +++ b/mDNSPosix/ExampleClientApp.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * 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 @@ -25,6 +24,18 @@ Change History (most recent first): $Log: ExampleClientApp.c,v $ +Revision 1.12 2004/11/30 22:37:00 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.11 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.10 2004/09/16 01:58:22 cheshire +Fix compiler warnings + Revision 1.9 2003/08/12 19:56:26 cheshire Update to APSL 2.0 @@ -49,7 +60,7 @@ Add log header #include // For gethostbyname() #include // For SIGINT, etc. -#include "mDNSClientAPI.h" // Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform //******************************************************************************************* @@ -60,7 +71,7 @@ static volatile mDNSBool StopNow; mDNSlocal void HandleSIG(int signal) { (void)signal; // Unused - debugf(""); + debugf("%s",""); debugf("HandleSIG"); StopNow = mDNStrue; } diff --git a/mDNSPosix/ExampleClientApp.h b/mDNSPosix/ExampleClientApp.h index d9b20c5..fa9f0fc 100644 --- a/mDNSPosix/ExampleClientApp.h +++ b/mDNSPosix/ExampleClientApp.h @@ -1,10 +1,9 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- + * * 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 @@ -25,6 +24,9 @@ Change History (most recent first): $Log: ExampleClientApp.h,v $ +Revision 1.6 2004/11/30 22:37:00 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + Revision 1.5 2003/08/12 19:56:26 cheshire Update to APSL 2.0 diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c index ed9fd7d..27b2505 100644 --- a/mDNSPosix/Identify.c +++ b/mDNSPosix/Identify.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * 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 @@ -38,6 +37,41 @@ Change History (most recent first): $Log: Identify.c,v $ +Revision 1.33 2004/11/30 22:37:00 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.32 2004/10/19 21:33:21 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.31 2004/10/16 00:17:00 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.30 2004/09/21 23:29:51 cheshire + DNSServiceResolve should delay sending packets + +Revision 1.29 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.28 2004/09/17 00:31:52 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.27 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.26 2004/08/24 21:55:07 cheshire +Don't try to build IPv6 code on systems that don't have IPv6 + +Revision 1.25 2004/07/20 23:42:37 cheshire +Update to use only "_services._dns-sd._udp.local." meta-query for service enumeration + +Revision 1.24 2004/06/15 02:39:47 cheshire +When displaying error message, only show command name, not entire path + Revision 1.23 2004/05/18 23:51:26 cheshire Tidy up all checkin comments to use consistent "" format for bug numbers @@ -137,7 +171,7 @@ Add mDNSIdentify tool, used to discover what version of mDNSResponder a particul #include #include -#include "mDNSClientAPI.h"// Defines the interface to the mDNS core code +#include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include "ExampleClientApp.h" @@ -177,16 +211,16 @@ mDNSlocal mDNSu32 mprintf(const char *format, ...) mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSInterfaceID InterfaceID) { + (void)dstaddr; // Unused // 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); + // For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet + __MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, &AllDNSLinkGroup_v4, dstport, InterfaceID); } static void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) @@ -199,7 +233,7 @@ static void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRec { ConvertDomainNameToCString(&answer->rdata->u.name, hostname); StopNow = 1; - mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.name.c); + mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c); } } @@ -213,9 +247,9 @@ static void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRec if (!id.NotAnInteger) id = lastid; NumAnswers++; NumAddr++; - mprintf("%##s %s %.4a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ip); + mprintf("%##s %s %.4a\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4); hostaddr.type = mDNSAddrType_IPv4; // Prefer v4 target to v6 target, for now - hostaddr.ip.v4 = answer->rdata->u.ip; + hostaddr.ip.v4 = answer->rdata->u.ipv4; } else if (answer->rrtype == kDNSType_AAAA) { @@ -257,7 +291,7 @@ static void ServicesCallback(mDNS *const m, DNSQuestion *question, const Resourc { NumAnswers++; NumAddr++; - mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), &answer->rdata->u.name.c); + mprintf("%##s %s %##s\n", answer->name.c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c); StopNow = 1; } } @@ -295,9 +329,12 @@ mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const m q->Target = target ? *target : zeroAddr; q->TargetPort = MulticastDNSPort; q->TargetQID = zeroID; - q->InterfaceID = mDNSInterface_ForceMCast; + q->InterfaceID = mDNSInterface_Any; q->qtype = qtype; q->qclass = kDNSClass_IN; + q->LongLived = mDNSfalse; + q->ExpectUnique = mDNStrue; + q->ForceMCast = mDNStrue; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa. q->QuestionCallback = callback; q->QuestionContext = NULL; @@ -333,17 +370,20 @@ mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr mDNSlocal void HandleSIG(int signal) { (void)signal; // Unused - debugf(""); + debugf("%s",""); debugf("HandleSIG"); StopNow = 2; } mDNSexport int main(int argc, char **argv) { + const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; int this_arg = 1; mStatus status; struct in_addr s4; +#if HAVE_IPV6 struct in6_addr s6; +#endif char buffer[256]; DNSQuestion q; @@ -382,6 +422,7 @@ mDNSexport int main(int argc, char **argv) DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); if (StopNow == 2) break; } +#if HAVE_IPV6 else if (inet_pton(AF_INET6, arg, &s6) == 1) { int i; @@ -400,6 +441,7 @@ mDNSexport int main(int argc, char **argv) DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); if (StopNow == 2) break; } +#endif else strcpy(hostname, arg); @@ -409,16 +451,14 @@ mDNSexport int main(int argc, char **argv) if (hardware[0] || software[0]) { - DNSQuestion q1, q2; + DNSQuestion q1; 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); + StartQuery(&q1, "_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) @@ -436,6 +476,6 @@ mDNSexport int main(int argc, char **argv) return(0); usage: - fprintf(stderr, "%s or or ...\n", argv[0]); + fprintf(stderr, "%s or or ...\n", progname); return(-1); } diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile index 9d28ad5..1f8b65b 100755 --- a/mDNSPosix/Makefile +++ b/mDNSPosix/Makefile @@ -1,25 +1,93 @@ -# Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ +# Copyright (c) 2002-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. # # $Log: Makefile,v $ +# Revision 1.53 2004/12/01 20:04:31 cheshire +# Tidy up alignment +# +# Revision 1.52 2004/12/01 19:46:12 cheshire +# Add install case for Suse 9 (rc*.d directories *inside* the init.d directory) +# +# Revision 1.51 2004/12/01 03:30:29 cheshire +# Add Unicast DNS support to mDNSPosix +# +# Revision 1.50 2004/12/01 01:14:20 cheshire +# Add $(LIBFLAGS) to cc command to build dnsextd (required for Solaris) +# +# Revision 1.49 2004/11/11 01:44:52 cheshire +# Updated error message +# +# Revision 1.48 2004/10/06 02:22:19 cheshire +# Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" +# +# Revision 1.47 2004/10/01 22:15:54 rpantos +# rdar://problem/3824265: Replace APSL in client lib with BSD license. +# +# Revision 1.46 2004/09/24 21:15:25 cheshire +# Library "libmdns" misnamed; should be "libdns_sd" +# +# Revision 1.45 2004/09/22 16:23:41 cheshire +# Modify installation for compatibility with Gentoo Linux +# (Thanks to David Black for this information) +# +# Revision 1.44 2004/09/17 01:08:53 cheshire +# Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h +# The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces +# declared in that file are ONLY appropriate to single-address-space embedded applications. +# For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. +# +# Revision 1.43 2004/09/17 00:30:11 cheshire +# Added some '@' signs to make build output less verbose -- +# when there's too much on the screen it's easy to miss build errors and warnings +# +# Revision 1.42 2004/08/24 22:04:37 cheshire +# Need to specify -lpthread for building dnsextd +# +# Revision 1.41 2004/08/11 00:43:26 ksekar +# : DNS Extension daemon for DNS Update Lease +# +# Revision 1.40 2004/07/08 21:45:55 cheshire +# Make nss_mdns only build on Linux. We can add it to other targets (Solaris, +# FreeBSD, etc., as we verify them). In particular, NSS is NOT supported on +# OS X, so including it for "os=jaguar" or "os=panther" broke those builds. +# +# Revision 1.39 2004/06/29 03:34:28 cheshire +# Add 'dot-local' Name Service Switch support from Andrew White at NICTA +# +# Revision 1.38 2004/06/25 02:19:40 rpantos +# And FreeBSD... +# +# Revision 1.37 2004/06/25 00:51:09 rpantos +# And fix the Java build for Posix on Solaris, too. +# +# Revision 1.36 2004/06/25 00:26:27 rpantos +# Changes to fix the Posix build on Solaris. +# +# Revision 1.35 2004/06/18 18:51:31 cheshire +# Add (commented out) "-pedantic" for when we want to check for "mixed declarations and code" warnings +# # 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. @@ -85,7 +153,7 @@ # # 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. +# Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file. # # Revision 1.13 2003/08/06 18:20:51 cheshire # Makefile cleanup @@ -114,13 +182,13 @@ # Added NetMonitor.c # -# This Makefile builds an mDNSResponder daemon and a libmdns.so shared library +# This Makefile builds an mDNSResponder daemon and a libdns_sd.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. +# 'sudo make install [DEBUG=1]' to install mdnsd daemon and libdns_sd. # # Notes: # $@ means "The file name of the target of the rule" @@ -137,15 +205,16 @@ COREDIR = ../mDNSCore SHAREDDIR = ../mDNSShared JDK = /usr/jdk -CC = cc -LD = ld +CC = @cc +LD = ld -shared 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 +LIBFLAGS = +DNSEXT_FLAGS = -D_REENTRANT -g -Wall -lpthread LDSUFFIX = so -JAVACFLAGS_OS = -fPIC -shared -lmdns +JAVACFLAGS_OS = -fPIC -shared -ldns_sd # Set up diverging paths for debug vs. prod builds DEBUG=0 @@ -155,7 +224,7 @@ OBJDIR = objects/debug BUILDDIR = build/debug STRIP = echo else -CFLAGS_DEBUG = -Os -DMDNS_DEBUGMSGS=0 +CFLAGS_DEBUG = -O0 -DMDNS_DEBUGMSGS=0 OBJDIR = objects/prod BUILDDIR = build/prod STRIP = strip -S @@ -163,16 +232,29 @@ 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 -DNOT_HAVE_SOCKLEN_T -DNOT_HAVE_IF_NAMETOINDEX \ + -DLOG_PERROR=0 -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME -DUSE_TCP_LOOPBACK +CC = gcc +LD = gcc -shared +LIBFLAGS = -lsocket -lnsl +JAVACFLAGS_OS += -I$(JDK)/include/solaris +ifneq ($(DEBUG),1) +STRIP = strip +endif else + ifeq ($(os),linux) CFLAGS_OS = -DNOT_HAVE_SA_LEN -DUSES_NETLINK JAVACFLAGS_OS += -I$(JDK)/include/linux +OPTIONALTARG = nss_mdns +OPTINSTALL = InstalledNSS 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" @@ -180,29 +262,35 @@ LOCALBASE?=/usr/local INSTBASE=$(LOCALBASE) STARTUPSCRIPTNAME=mdns.sh CFLAGS_OS = +JAVACFLAGS_OS += -I$(JDK)/include/freebsd LDCONFIG = ldconfig else + ifeq ($(os),openbsd) CFLAGS_OS = -DHAVE_BROKEN_RECVDSTADDR LDCONFIG = ldconfig else + ifeq ($(os),jaguar) CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -DNOT_HAVE_SOCKLEN_T -LD = libtool -LDFLAGS = -dynamic -lSystem +LD = libtool -dynamic +LIBFLAGS = -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 -no-cpp-precomp -LD = libtool -LDFLAGS = -dynamic -lSystem +CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp #-pedantic +LD = libtool -dynamic +LIBFLAGS = -lSystem LDSUFFIX = dylib JDK = /System/Library/Frameworks/JavaVM.framework/Home JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM else -$(error ERROR: Must specify target OS on command-line: "make os={jaguar,panther,linux,netbsd,freebsd,openbsd,solaris} [target]") + +$(error ERROR: Must specify target OS on command-line, e.g. "make os=panther [target]".\ +Supported operating systems include: jaguar, panther, linux, netbsd, freebsd, openbsd, solaris) endif endif endif @@ -211,6 +299,12 @@ endif endif endif +NSSLIBNAME := libnss_mdns +NSSVERSION := 0.2 +NSSLIBFILE := $(NSSLIBNAME)-$(NSSVERSION).so +NSSLINKNAME := $(NSSLIBNAME).so.2 +NSSINSTPATH := /lib + # 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 @@ -224,7 +318,19 @@ CFLAGS_OS += -DHAVE_IPV6=0 endif endif -# If directory /etc/rc.d/init.d/ exists, then we install into that (old Linux) +# If directory /usr/share/man exists, then we install man pages into that, else /usr/man +ifeq ($(wildcard /usr/share/man), /usr/share/man) +MANPATH := /usr/share/man +else +MANPATH := /usr/man +endif + +# If directories /etc/init.d/rc*.d exist, then we install into that (Suse) +ifeq ($(wildcard /etc/init.d/rc2.d/), /etc/init.d/rc2.d/) +STARTUPSCRIPTDIR = /etc/init.d +RUNLEVELSCRIPTSDIR = /etc/init.d +else +# else 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 @@ -238,14 +344,15 @@ else STARTUPSCRIPTDIR = $(INSTBASE)/etc/rc.d endif endif +endif CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) $(CFLAGS_DEBUG) ############################################################################# -all: setup Daemon libmdns Client Responder ProxyResponder Identify NetMonitor +all: setup Daemon libdns_sd Client Responder ProxyResponder Identify NetMonitor dnsextd $(OPTIONALTARG) -install: setup InstalledDaemon InstalledLib InstalledStartup +install: setup InstalledDaemon InstalledLib InstalledStartup InstalledManPages $(OPTINSTALL) # 'setup' sets up the build directory structure the way we want setup: @@ -264,41 +371,57 @@ clean: # 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 + $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/PlatformCommon.c.o Daemon: setup $(BUILDDIR)/mdnsd @echo "Responder daemon done" $(BUILDDIR)/mdnsd: $(DAEMONOBJS) - $(CC) -o $@ $+ - $(STRIP) $@ + $(CC) -o $@ $+ $(CFLAGS) $(LIBFLAGS) + @$(STRIP) $@ -# libmdns target builds the client library -libmdns: setup $(BUILDDIR)/libmdns.$(LDSUFFIX) +# libdns_sd target builds the client library +libdns_sd: setup $(BUILDDIR)/libdns_sd.$(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) $@ +CLIENTLIBOBJS = $(OBJDIR)/dnssd_clientlib.c.so.o $(OBJDIR)/dnssd_clientstub.c.so.o $(OBJDIR)/dnssd_ipc.c.so.o + +$(BUILDDIR)/libdns_sd.$(LDSUFFIX): $(CLIENTLIBOBJS) + @$(LD) $(LIBFLAGS) -o $@ $+ + @$(STRIP) $@ + +# nss_mdns target builds the Name Service Switch module +nss_mdns: setup $(BUILDDIR)/$(NSSLIBFILE) + @echo "Name Service Switch module done" + +$(BUILDDIR)/$(NSSLIBFILE): $(CLIENTLIBOBJS) $(OBJDIR)/nss_mdns.c.so.o + @$(LD) $(LIBFLAGS) -o $@ $+ + @$(STRIP) $@ ############################################################################# # The Install targets place built stuff in their proper places InstalledDaemon: $(INSTBASE)/sbin/mdnsd - @echo $< " installed" + @echo $+ " installed" -InstalledLib: $(INSTBASE)/lib/libmdns.$(LDSUFFIX).$(LIBVERS) $(INSTBASE)/include/dns_sd.h - @echo $< " installed" +InstalledLib: $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS) $(INSTBASE)/include/dns_sd.h + @echo $+ " installed" InstalledStartup: $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) - @echo $< " installed" + @echo $+ " installed" + +InstalledNSS: $(NSSINSTPATH)/$(NSSLINKNAME) /etc/nss_mdns.conf $(MANPATH)/man5/nss_mdns.conf.5 $(MANPATH)/man8/libnss_mdns.8 + @echo $+ " installed" + +InstalledManPages: $(MANPATH)/man8/mdnsd.8 + @echo $+ " installed" $(INSTBASE)/sbin/mdnsd: $(BUILDDIR)/mdnsd $(CP) $< $@ -$(INSTBASE)/lib/libmdns.$(LDSUFFIX).$(LIBVERS): $(BUILDDIR)/libmdns.$(LDSUFFIX) +$(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS): $(BUILDDIR)/libdns_sd.$(LDSUFFIX) $(CP) $< $@ - $(LN) $@ $(INSTBASE)/lib/libmdns.$(LDSUFFIX) + $(LN) $@ $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX) ifdef LDCONFIG # -m means 'merge into existing database', -R means 'rescan directories' $(LDCONFIG) -mR @@ -311,6 +434,9 @@ $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME): mdnsd.sh $(STARTUPSCRIPTDIR) $(CP) $< $@ chmod ugo+x $@ ifdef RUNLEVELSCRIPTSDIR +ifeq ($(wildcard $(RUNLEVELSCRIPTSDIR)/runlevels/default), $(RUNLEVELSCRIPTSDIR)/runlevels/default) + $(LN) $@ $(RUNLEVELSCRIPTSDIR)/runlevels/default/mdns +else $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc2.d/S52mdns $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc3.d/S52mdns $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc4.d/S52mdns @@ -318,6 +444,35 @@ ifdef RUNLEVELSCRIPTSDIR $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc0.d/K16mdns $(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc6.d/K16mdns endif +endif + +$(NSSINSTPATH)/$(NSSLINKNAME): $(NSSINSTPATH)/$(NSSLIBFILE) + $(LN) $< $@ + ldconfig + +$(NSSINSTPATH)/$(NSSLIBFILE): $(BUILDDIR)/$(NSSLIBFILE) + $(CP) $< $@ + chmod 444 $@ + +/etc/nss_mdns.conf: nss_mdns.conf + $(CP) $< $@ + chmod 444 $@ + # Check the nsswitch.conf file. + # If 'mdns' does not already appear on the "hosts:" line, then add it right before 'dns' + cp -f /etc/nsswitch.conf /etc/nsswitch.conf.pre-mdns + sed -e '/mdns/!s/^\(hosts:.*\)dns\(.*\)/\1mdns dns\2/' /etc/nsswitch.conf.pre-mdns > /etc/nsswitch.conf + +$(MANPATH)/man5/%.5: %.5 + cp $< $@ + chmod 444 $@ + +$(MANPATH)/man8/%.8: %.8 + cp $< $@ + chmod 444 $@ + +$(MANPATH)/man8/mdnsd.8: $(SHAREDDIR)/mDNSResponder.8 + cp $< $@ + chmod 444 $@ ############################################################################# @@ -394,20 +549,26 @@ Identify: setup $(BUILDDIR)/mDNSIdentify NetMonitor: setup $(BUILDDIR)/mDNSNetMonitor @echo "NetMonitor done" +dnsextd: setup $(BUILDDIR)/dnsextd + @echo "dnsextd done" + $(BUILDDIR)/mDNSClientPosix: $(APPOBJ) $(OBJDIR)/Client.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) $(BUILDDIR)/mDNSResponderPosix: $(COMMONOBJ) $(OBJDIR)/Responder.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) $(BUILDDIR)/mDNSProxyResponderPosix: $(COMMONOBJ) $(OBJDIR)/ProxyResponder.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) $(BUILDDIR)/mDNSIdentify: $(SPECIALOBJ) $(OBJDIR)/Identify.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) $(BUILDDIR)/mDNSNetMonitor: $(SPECIALOBJ) $(OBJDIR)/NetMonitor.c.o - $(CC) $+ -o $@ + $(CC) $+ -o $@ $(LIBFLAGS) + +$(BUILDDIR)/dnsextd: $(SPECIALOBJ) $(OBJDIR)/dnsextd.c.o + $(CC) $+ -o $@ $(LIBFLAGS) $(DNSEXT_FLAGS) ############################################################################# diff --git a/mDNSPosix/NetMonitor.c b/mDNSPosix/NetMonitor.c index 8bce09e..c8aad7b 100644 --- a/mDNSPosix/NetMonitor.c +++ b/mDNSPosix/NetMonitor.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * 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 @@ -38,6 +37,27 @@ Change History (most recent first): $Log: NetMonitor.c,v $ +Revision 1.70 2004/12/04 02:13:20 cheshire +Pass proper record type in GetLargeResourceRecord() calls + +Revision 1.69 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.68 2004/10/23 01:16:01 cheshire + uDNS operations not always reliable on multi-homed hosts + +Revision 1.67 2004/10/16 00:17:00 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.66 2004/09/17 00:31:52 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.65 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.64 2004/06/15 02:39:47 cheshire +When displaying error message, only show command name, not entire path + Revision 1.63 2004/05/18 23:51:26 cheshire Tidy up all checkin comments to use consistent "" format for bug numbers @@ -521,7 +541,7 @@ mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *nam m->ExpectUnicastResponse = m->timenow; } - mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, target, MulticastDNSPort); + mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, target, MulticastDNSPort, -1, mDNSNULL); } mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID) @@ -563,7 +583,7 @@ mDNSlocal void ShowSortedHostList(HostList *list, int max) { int len = mprintf("%#-25a", &e->addr); if (len > 25) mprintf("\n%25s", ""); - mprintf("%8d %8d %8d %8d %8d %8d %8d", e->totalops, + mprintf("%8lu %8lu %8lu %8lu %8lu %8lu %8lu", e->totalops, e->stat[OP_probe], e->stat[OP_goodbye], e->stat[OP_browseq], e->stat[OP_browsea], e->stat[OP_resolveq], e->stat[OP_resolvea]); @@ -653,7 +673,7 @@ mDNSlocal void printstats(int max) if (!m) return; m->printed = mDNStrue; if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner); - mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", &m->srvtype, m->totalops, m->stat[OP_probe], + mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.c, m->totalops, m->stat[OP_probe], m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]); } } @@ -665,7 +685,7 @@ mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, for (i = 0; i < query->h.numAuthorities; i++) { const mDNSu8 *p2 = ptr; - ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, pkt); + ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt); if (!ptr) break; if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2); } @@ -681,7 +701,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, const mDNSAddr *dstaddr, mDNSu8 ttl) +mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr) { const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " : (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-"; @@ -692,8 +712,6 @@ mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *co 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) @@ -713,12 +731,12 @@ mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char * RDataBody *rd = &pktrr->rdata->u; mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; - mDNSu32 n = mprintf("%#-16a %-5s %-5s%5d %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name.c); + int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name.c); switch(pktrr->rrtype) { - case kDNSType_A: n += mprintf("%.4a", &rd->ip); break; - case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, &rd->name); break; + case kDNSType_A: n += mprintf("%.4a", &rd->ipv4); break; + case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, rd->name.c); break; case kDNSType_HINFO:// same as kDNSType_TXT below case kDNSType_TXT: { mDNSu8 *t = rd->txt.c; @@ -745,7 +763,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, mDNSVal16(rd->srv.port)); break; + case kDNSType_SRV: n += mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break; default: { mDNSu8 *s = rd->data; while (s < rdend && p < buffer+MaxWidth) @@ -792,7 +810,7 @@ mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mD } 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) + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) { int i; const mDNSu8 *ptr = msg->data; @@ -801,7 +819,7 @@ mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mD HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id); LargeCacheRecord pkt; - DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr, ttl); + DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr); if (msg->h.id.NotAnInteger != 0xFFFF) { if (MQ) NumPktQ++; else NumPktL++; @@ -838,7 +856,7 @@ mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mD for (i=0; ih.numAnswers; i++) { const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; } DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec); @@ -860,14 +878,14 @@ mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mD } 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) + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) { int i; const mDNSu8 *ptr = msg->data; HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); LargeCacheRecord pkt; - DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr, ttl); + DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr); if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++; for (i=0; ih.numQuestions; i++) @@ -885,7 +903,7 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const for (i=0; ih.numAnswers; i++) { const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; } if (pkt.r.resrec.rroriginalttl) { @@ -905,7 +923,7 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const for (i=0; ih.numAuthorities; i++) { const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt); if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name.c); @@ -914,7 +932,7 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const for (i=0; ih.numAdditionals; i++) { const mDNSu8 *ep = ptr; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt); if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } NumAdditionals++; DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec); @@ -934,7 +952,7 @@ mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg for (i=0; ih.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++) { LargeCacheRecord pkt; - ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt); + ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); if (pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec); } } @@ -948,7 +966,7 @@ mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr) } mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl) + const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) { const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; @@ -964,17 +982,6 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); - if (ttl < 254) - { - debugf("** Apparent spoof mDNS %s packet from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s", - (QR_OP == StdQ) ? "Query" : (QR_OP == StdR) ? "Response" : "Unkown", - srcaddr, dstaddr, ttl, InterfaceID, - msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", - msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", - msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", - msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s"); - } - // For now we're only interested in monitoring IPv4 traffic. // All IPv6 packets should just be duplicates of the v4 packets. if (AddressMatchesFilterList(srcaddr)) @@ -987,8 +994,8 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS } else { - 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); + if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); + else if (QR_OP == StdR) DisplayResponse (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); else { debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]); @@ -1053,7 +1060,7 @@ mDNSlocal mStatus mDNSNetMonitor(void) localtime_r((time_t*)&tv_end.tv_sec, &tm); mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec); mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec); - if (!Filters) mprintf("Unique source addresses seen on network: %d\n", IPv4HostList.num + IPv6HostList.num); + if (!Filters) mprintf("Unique source addresses seen on network: %ld\n", IPv4HostList.num + IPv6HostList.num); mprintf("\n"); mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div); mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div); @@ -1081,6 +1088,7 @@ mDNSlocal mStatus mDNSNetMonitor(void) mDNSexport int main(int argc, char **argv) { + const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; int i; mStatus status; @@ -1115,12 +1123,12 @@ mDNSexport int main(int argc, char **argv) } status = mDNSNetMonitor(); - if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", argv[0], status); return(status); } + if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", progname, status); return(status); } return(0); usage: fprintf(stderr, "\nmDNS traffic monitor\n"); - fprintf(stderr, "Usage: %s ()\n", argv[0]); + fprintf(stderr, "Usage: %s ()\n", progname); fprintf(stderr, "Optional parameter displays only packets from that host\n"); fprintf(stderr, "\nPer-packet header output:\n"); diff --git a/mDNSPosix/PosixDaemon.c b/mDNSPosix/PosixDaemon.c index 6e943de..fcb3990 100644 --- a/mDNSPosix/PosixDaemon.c +++ b/mDNSPosix/PosixDaemon.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * 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,17 +21,55 @@ * * @APPLE_LICENSE_HEADER_END@ - File: daemon.c + File: daemon.c - Contains: main & associated Application layer for mDNSResponder on Linux. + Contains: main & associated Application layer for mDNSResponder on Linux. - Version: 1.0 - Tabs: 4 spaces - - Change History (most recent first): + Change History (most recent first): $Log: PosixDaemon.c,v $ -Revision 1.10 2004/06/08 04:59:40 cheshire +Revision 1.22 2004/12/10 13:12:08 cheshire +Create no-op function RecordUpdatedNiceLabel(), required by uds_daemon.c + +Revision 1.21 2004/12/01 20:57:20 ksekar + Wide Area Rendezvous must be split-DNS aware + +Revision 1.20 2004/12/01 04:28:43 cheshire + Darwin patches for Solaris and Suse +Use version of daemon() provided in mDNSUNP.c instead of local copy + +Revision 1.19 2004/12/01 03:30:29 cheshire + Add Unicast DNS support to mDNSPosix + +Revision 1.18 2004/11/30 22:45:59 cheshire +Minor code tidying + +Revision 1.17 2004/11/30 22:18:59 cheshire + Posix needs to read the list of unicast DNS servers and set server list + +Revision 1.16 2004/09/21 21:05:12 cheshire +Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c, +into mDNSShared/uds_daemon.c + +Revision 1.15 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.14 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.13 2004/08/11 01:59:41 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init() + +Revision 1.12 2004/06/28 23:19:19 cheshire +Fix "Daemon_Init declared but never defined" warning on Linux + +Revision 1.11 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.10 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.9 2004/05/29 00:14:20 rpantos @@ -61,7 +98,6 @@ 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 @@ -70,218 +106,221 @@ Add support for mDNSResponder on Linux. #include #include #include +#include #include #include +#include +#include -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "mDNSDebug.h" #include "mDNSPosix.h" #include "uds_daemon.h" +#include "PlatformCommon.h" +#define uDNS_SERVERS_FILE "/etc/resolv.conf" -static void ParseCmdLinArgs( int argc, char **argv); -static void DumpStateLog( mDNS *m); -static mStatus MainLoop( mDNS *m); - +#define CONFIG_FILE "/etc/mdnsd.conf" +static domainname DynDNSZone; // Default wide-area zone for service registration +static domainname DynDNSHostname; #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) +static int ParseDNSServers(mDNS *m, const char *filePath) { - 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"); + char line[256]; + char nameserver[16]; + char keyword[10]; + int numOfServers = 0; + FILE *fp = fopen(filePath, "r"); + if (fp == NULL) return -1; + while (fgets(line,sizeof(line),fp)) + { + struct in_addr ina; + line[255]='\0'; // just to be safe + if (sscanf(line,"%10s %15s", keyword, nameserver) != 2) continue; // it will skip whitespaces + if (strncmp(keyword,"nameserver",10)) continue; + if (inet_aton(nameserver, (struct in_addr *)&ina) != 0) + { + mDNSAddr DNSAddr; + DNSAddr.type = mDNSAddrType_IPv4; + DNSAddr.ip.v4.NotAnInteger = ina.s_addr; + mDNS_AddDNSServer(m, &DNSAddr, NULL); + numOfServers++; + } + } + return (numOfServers > 0) ? 0 : -1; } - 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) +static void Reconfigure(mDNS *m) { - if ( 0 == strcmp( argv[1], "-debug")) - { - mDNS_DebugMode = mDNStrue; - } - else - printf( "Usage: mDNSResponder [-debug]\n"); + mDNSAddr DynDNSIP; + mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL); + mDNS_DeleteDNSServers(m); + if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) + LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable"); + ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone); + FindDefaultRouteIP(&DynDNSIP); + if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL); + if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL); } - if ( !mDNS_DebugMode) +// Do appropriate things at startup with command line arguments. Calls exit() if unhappy. +static void ParseCmdLinArgs(int argc, char **argv) { - int result = daemon( 0, 0); - - if ( result != 0) + if (argc > 1) { - LogMsg("Could not run as daemon - exiting"); - exit( result); + if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue; + else printf("Usage: mDNSResponder [-debug]\n"); } -#if __APPLE__ + if (!mDNS_DebugMode) { - LogMsg("The POSIX mDNSResponder should only be used on OS X for testing - exiting"); - exit( -1); - } + 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) +static void DumpStateLog(mDNS *const 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(); - + udsserver_info(m); LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----"); -} + } -static mStatus MainLoop( mDNS *m) -// Loop until we quit. -{ +static mStatus MainLoop(mDNS *m) // Loop until we quit. + { sigset_t signals; mDNSBool gotData = mDNSfalse; - mDNSPosixListenForSignalInEventLoop( SIGINT); - mDNSPosixListenForSignalInEventLoop( SIGTERM); - mDNSPosixListenForSignalInEventLoop( SIGUSR1); - mDNSPosixListenForSignalInEventLoop( SIGPIPE); + mDNSPosixListenForSignalInEventLoop(SIGINT); + mDNSPosixListenForSignalInEventLoop(SIGTERM); + mDNSPosixListenForSignalInEventLoop(SIGUSR1); + mDNSPosixListenForSignalInEventLoop(SIGPIPE); + mDNSPosixListenForSignalInEventLoop(SIGHUP) ; - for ( ; ;) - { + 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) - { + if (!gotData) + { mDNSs32 nextTimerEvent = mDNS_Execute(m); - - nextTimerEvent = udsserver_idle( nextTimerEvent); - - ticks = nextTimerEvent - mDNSPlatformTimeNow(); + nextTimerEvent = udsserver_idle(nextTimerEvent); + ticks = nextTimerEvent - mDNS_TimeNow(m); 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); + (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; + if (sigismember(&signals, SIGHUP )) Reconfigure(m); + if (sigismember(&signals, SIGUSR1)) DumpStateLog(m); + // SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up. + if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring"); + if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break; + } + return EINTR; } - return EINTR; -} +int main(int argc, char **argv) + { + #define mDNSRecord mDNSStorage + 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(); + + Reconfigure(&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; + } // uds_daemon support //////////////////////////////////////////////////////////// #if MDNS_MALLOC_DEBUGGING >= 2 #define LogMalloc LogMsg #else -#define LogMalloc(ARGS...) ((void)0) +#define LogMalloc(ARGS...) ((void)0) #endif - -mStatus udsSupportAddFDToEventLoop( int fd, udsEventCallback callback, void *context) +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); -} + return mDNSPosixAddFDToEventLoop(fd, callback, context); + } -mStatus udsSupportRemoveFDFromEventLoop( int fd) -{ - return mDNSPosixRemoveFDFromEventLoop( fd); -} +mStatus udsSupportRemoveFDFromEventLoop(int fd) + { + return mDNSPosixRemoveFDFromEventLoop(fd); + } + +mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) + { + (void)m; + (void)delay; + // No-op, for now + } #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; @@ -289,15 +328,15 @@ void *mallocL(char *msg, unsigned int 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; } @@ -308,13 +347,11 @@ void freeL(char *msg, void *x) 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__ ") "; diff --git a/mDNSPosix/ProxyResponder.c b/mDNSPosix/ProxyResponder.c index 5030d93..bca7356 100644 --- a/mDNSPosix/ProxyResponder.c +++ b/mDNSPosix/ProxyResponder.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * 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 @@ -25,6 +24,31 @@ Change History (most recent first): $Log: ProxyResponder.c,v $ +Revision 1.34 2004/12/01 04:27:28 cheshire + Darwin patches for Solaris and Suse +Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc. + +Revision 1.33 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.32 2004/10/26 03:59:41 cheshire +Update comments + +Revision 1.31 2004/09/17 01:08:53 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.30 2004/09/17 00:31:52 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.29 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.28 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + Revision 1.27 2004/03/12 08:03:14 cheshire Update comments @@ -75,27 +99,32 @@ Revision 1.13 2003/04/18 22:46:12 cheshire Fix mistake in 1.8 -- INADDR_NONE is 0xFFFFFFFF, not 0 Revision 1.12 2003/04/16 02:11:07 cheshire -Fixed mDNS_RegisterNoSuchService non-existance function so that it works again +Fixed mDNS_RegisterNoSuchService non-existence function so that it works again Revision 1.11 2003/03/31 22:49:35 cheshire Add "$Log" header */ -#include // For printf() -#include // For exit() etc. -#include // For strlen() etc. -#include // For select() -#include // For SIGINT, SIGTERM -#include // For errno, EINTR -#include // For inet_addr() -#include // For INADDR_NONE -#include // For gethostbyname() - -#include "mDNSClientAPI.h" // Defines the interface to the client layer above -#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform +#include // For printf() +#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 +#include // For gethostbyname() + +#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above +#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include "ExampleClientApp.h" +// Compatibility workaround: Solaris 2.5 has no INADDR_NONE +#ifndef INADDR_NONE +#define INADDR_NONE (mDNSu32)0xffffffff +#endif + //************************************************************************************************************* // Globals static mDNS mDNSStorage; // mDNS core uses this to store its globals @@ -116,10 +145,10 @@ mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus res { ProxyHost *f = (ProxyHost*)rr->RecordContext; if (result == mStatus_NoError) - debugf("Host name successfully registered: %##s", &rr->resrec.name); + debugf("Host name successfully registered: %##s", rr->resrec.name.c); else { - debugf("Host name conflict for %##s", &rr->resrec.name); + debugf("Host name conflict for %##s", rr->resrec.name.c); mDNS_Deregister(m, &f->RR_A); mDNS_Deregister(m, &f->RR_PTR); exit(-1); @@ -140,13 +169,13 @@ mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p->ip.b[3], p->ip.b[2], p->ip.b[1], p->ip.b[0]); MakeDomainNameFromDNSNameString(&p->RR_PTR.resrec.name, buffer); - p->RR_A. resrec.rdata->u.ip = p->ip; + p->RR_A. resrec.rdata->u.ipv4 = p->ip; p->RR_PTR.resrec.rdata->u.name = p->RR_A.resrec.name; mDNS_Register(m, &p->RR_A); mDNS_Register(m, &p->RR_PTR); - debugf("Made Proxy Host Records for %##s", &p->RR_A.resrec.name); + debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name.c); return(mStatus_NoError); } @@ -162,10 +191,10 @@ mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatu { switch (result) { - case mStatus_NoError: debugf("Callback: %##s Name Registered", &sr->RR_SRV.resrec.name); break; - case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", &sr->RR_SRV.resrec.name); break; - case mStatus_MemFree: debugf("Callback: %##s Memory Free", &sr->RR_SRV.resrec.name); break; - default: debugf("Callback: %##s Unknown Result %d", &sr->RR_SRV.resrec.name, result); break; + case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name.c); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name.c); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name.c); break; + default: debugf("Callback: %##s Unknown Result %ld", sr->RR_SRV.resrec.name.c, result); break; } if (result == mStatus_NoError) @@ -235,10 +264,10 @@ mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatu domainname *proxyhostname = (domainname *)rr->RecordContext; switch (result) { - case mStatus_NoError: debugf("Callback: %##s Name Registered", &rr->resrec.name); break; - case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", &rr->resrec.name); break; - case mStatus_MemFree: debugf("Callback: %##s Memory Free", &rr->resrec.name); break; - default: debugf("Callback: %##s Unknown Result %d", &rr->resrec.name, result); break; + case mStatus_NoError: debugf("Callback: %##s Name Registered", rr->resrec.name.c); break; + case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", rr->resrec.name.c); break; + case mStatus_MemFree: debugf("Callback: %##s Memory Free", rr->resrec.name.c); break; + default: debugf("Callback: %##s Unknown Result %ld", rr->resrec.name.c, result); break; } if (result == mStatus_NoError) diff --git a/mDNSPosix/ReadMe.txt b/mDNSPosix/ReadMe.txt index 06df6e7..d128c8d 100755 --- a/mDNSPosix/ReadMe.txt +++ b/mDNSPosix/ReadMe.txt @@ -20,8 +20,8 @@ zeroconf technologies. This sample is designed to show how easy it is to make a device "Rendezvous compatible". The code in this sample was compiled and tested on Mac OS X (10.1.x, -10.2), Solaris (SunOS 5.6), Linux (Redhat 2.4.9-21), and OpenBSD (2.9). -YMMV. +10.2, 10.3), Solaris (SunOS 5.8), Linux (Redhat 2.4.9-21, Fedora Core 1), +and OpenBSD (2.9). YMMV. Packing List @@ -72,6 +72,7 @@ When you compile, you will get: o Main products for general-purpose use (e.g. on a desktop computer): - mdnsd - libmdns + - nss_mdns (See nss_ReadMe.txt for important information about nss_mdns) o Standalone products for dedicated devices (printer, network camera, etc.) - mDNSClientPosix @@ -82,14 +83,20 @@ o Debugging tools - mDNSNetMonitor - mDNSIdentify -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) - -Once you've installed the header and the library, and started the -daemon running, you can cd to the "Clients" folder and type "make". +As root type "make install" to install six 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) +o manual pages (usually in /usr/share/man) +o nss_mdns (usually in /lib) +o nss configuration files (usually in /etc) + +Once you've installed the files in their respective places, +you need to start the daemon running, either by rebooting, +or by running the startup script "/etc/init.d/mdns start" +(the exact path may be different on your system). +Then 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. @@ -117,6 +124,11 @@ 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. +Note that, strictly speaking, nss_mdns could be just another client of +mdnsd, linking with libmdns just like any other client. However, because +of its central role in the normal operation of multicast DNS, it is built +and installed along with the other essential system support components. + Clients for Embedded Systems ---------------------------- diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c index a8c297a..93b0c81 100755 --- a/mDNSPosix/Responder.c +++ b/mDNSPosix/Responder.c @@ -1,10 +1,9 @@ -/* - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * 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 @@ -25,6 +24,31 @@ Change History (most recent first): $Log: Responder.c,v $ +Revision 1.27 2004/12/01 04:28:43 cheshire + Darwin patches for Solaris and Suse +Use version of daemon() provided in mDNSUNP.c instead of local copy + +Revision 1.26 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.25 2004/11/11 02:00:51 cheshire +Minor fixes to getopt, error message + +Revision 1.24 2004/11/09 19:32:10 rpantos +Suggestion from Ademar de Souza Reis Jr. to allow comments in services file + +Revision 1.23 2004/09/17 01:08:54 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.22 2004/09/16 01:58:22 cheshire +Fix compiler warnings + +Revision 1.21 2004/06/15 03:48:07 cheshire +Update mDNSResponderPosix to take multiple name=val arguments in a sane way + Revision 1.20 2004/05/18 23:51:26 cheshire Tidy up all checkin comments to use consistent "" format for bug numbers @@ -89,7 +113,7 @@ First checkin */ -#include "mDNSClientAPI.h"// Defines the interface to the client layer above +#include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform #include @@ -250,91 +274,6 @@ static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool p return result; } -static mDNSBool CheckThatServiceTextIsUsable(const char *serviceText, mDNSBool printExplanation, - mDNSu8 *pStringList, mDNSu16 *pStringListLen) - // Checks that serviceText is a reasonable service text record - // and, if it isn't and printExplanation is true, prints - // an explanation of why not. Also parse the text into - // the packed PString buffer denoted by pStringList and - // return the length of that buffer in *pStringListLen. - // Note that this routine assumes that the buffer is - // sizeof(RDataBody) bytes long. -{ - mDNSBool result; - size_t serviceTextLen; - - // Note that parsing a C string into a PString list always - // expands the data by one character, so the following - // compare is ">=", not ">". Here's the logic: - // - // #1 For a string with not ^A's, the PString length is one - // greater than the C string length because we add a length - // byte. - // #2 For every regular (not ^A) character you add to the C - // string, you add a regular character to the PString list. - // This does not affect the equivalence stated in #1. - // #3 For every ^A you add to the C string, you add a length - // byte to the PString list but you also eliminate the ^A, - // which again does not affect the equivalence stated in #1. - - result = mDNStrue; - serviceTextLen = strlen(serviceText); - if (result && strlen(serviceText) >= sizeof(RDataBody)) { - if (printExplanation) { - fprintf(stderr, - "%s: Service text record is too long (must be less than %d characters)\n", - gProgramName, - (int) sizeof(RDataBody) ); - } - result = mDNSfalse; - } - - // Now break the string up into PStrings delimited by ^A. - // We know the data will fit so we can ignore buffer overrun concerns. - // However, we still have to treat runs long than 255 characters as - // an error. - - if (result) { - int lastPStringOffset; - int i; - int thisPStringLen; - - // This algorithm is a little tricky. We start by copying - // the string directly into the output buffer, shifted up by - // one byte. We then fill in the first byte with a ^A. - // We then walk backwards through the buffer and, for each - // ^A that we find, we replace it with the difference between - // its offset and the offset of the last ^A that we found - // (ie lastPStringOffset). - - memcpy(&pStringList[1], serviceText, serviceTextLen); - pStringList[0] = 1; - lastPStringOffset = serviceTextLen + 1; - for (i = serviceTextLen; i >= 0; i--) { - if ( pStringList[i] == 1 ) { - thisPStringLen = (lastPStringOffset - i - 1); - assert(thisPStringLen >= 0); - if (thisPStringLen > 255) { - result = mDNSfalse; - if (printExplanation) { - fprintf(stderr, - "%s: Each component of the service text record must be 255 characters or less\n", - gProgramName); - } - break; - } else { - pStringList[i] = thisPStringLen; - lastPStringOffset = i; - } - } - } - - *pStringListLen = serviceTextLen + 1; - } - - return result; -} - static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation) // Checks that portNumber is a reasonable port number // and, if it isn't and printExplanation is true, prints @@ -368,7 +307,7 @@ enum { static void PrintUsage() { fprintf(stderr, - "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-x TXT] [-p port] [-f file] [-b] [-P pidfile]\n", + "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n", gProgramName); fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n"); fprintf(stderr, " 0 = no debugging info (default)\n"); @@ -379,13 +318,15 @@ static void PrintUsage() fprintf(stderr, " -n uses 'name' as the host name (default is none)\n"); fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType); fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain); - fprintf(stderr, " -x uses 'TXT' as the service TXT record (default is empty)\n"); fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber); fprintf(stderr, " -f reads a service list from 'file'\n"); fprintf(stderr, " -b forces daemon (background) mode\n"); fprintf(stderr, " -P uses 'pidfile' as the PID file\n"); fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile); fprintf(stderr, " only meaningful if -b also specified\n"); + fprintf(stderr, " -x stores name=val in TXT record (default is empty).\n"); + fprintf(stderr, " MUST be the last command-line argument;\n"); + fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n"); } static mDNSBool gAvoidPort53 = mDNStrue; @@ -417,7 +358,7 @@ static void ParseArguments(int argc, char **argv) // Parse command line options using getopt. do { - ch = getopt(argc, argv, "v:rn:t:d:x:p:f:dPb"); + ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx"); if (ch != -1) { switch (ch) { case 'v': @@ -447,11 +388,6 @@ static void ParseArguments(int argc, char **argv) case 'd': gServiceDomain = optarg; break; - case 'x': - if ( ! CheckThatServiceTextIsUsable(optarg, mDNStrue, gServiceText, &gServiceTextLen) ) { - exit(1); - } - break; case 'p': gPortNumber = atol(optarg); if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) { @@ -467,6 +403,16 @@ static void ParseArguments(int argc, char **argv) case 'P': gPIDFile = optarg; break; + case 'x': + while (optind < argc) + { + gServiceText[gServiceTextLen] = strlen(argv[optind]); + memcpy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]); + gServiceTextLen += 1 + gServiceText[gServiceTextLen]; + optind++; + } + ch = -1; + break; case '?': default: PrintUsage(); @@ -559,7 +505,7 @@ static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegi break; default: - debugf("Callback: %##s Unknown Status %d", thisRegistration->RR_SRV.resrec.name.c, status); + debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name.c, status); break; } } @@ -621,91 +567,88 @@ static mStatus RegisterOneService(const char * richTextHostName, } static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp) +// Read a line, skipping over any blank lines or lines starting with '#' +{ + mDNSBool good, skip; + do { + good = (fgets(buf, bufSize, fp) != NULL); + skip = (good && (buf[0] == '#' || buf[0] == '\r' || buf[0] == '\n')); + } while (good && skip); + if (good) { - 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; - } - return good; + int len = strlen( buf); + if ( buf[len - 1] == '\r' || buf[len - 1] == '\n') + buf[len - 1] = '\0'; } + return good; +} static mStatus RegisterServicesInFile(const char *filePath) { - mStatus status; - FILE * fp; + mStatus status = mStatus_NoError; + FILE * fp = fopen(filePath, "r"); int junk; - mDNSBool good; - int ch; - char name[256]; - char type[256]; - const char *dom = kDefaultServiceDomain; - char rawText[1024]; - mDNSu8 text[sizeof(RDataBody)]; - mDNSu16 textLen; - char port[256]; - status = mStatus_NoError; - fp = fopen(filePath, "r"); if (fp == NULL) { status = mStatus_UnknownErr; } if (status == mStatus_NoError) { - good = mDNStrue; + mDNSBool good = mDNStrue; do { - // Skip over any blank lines. - do { - ch = fgetc(fp); - } while ( ch == '\n' || ch == '\r' ); - if (ch != EOF) { - good = (ungetc(ch, fp) == ch); - } + char name[256]; + char type[256]; + const char *dom = kDefaultServiceDomain; + char rawText[1024]; + mDNSu8 text[sizeof(RDataBody)]; + mDNSu16 textLen = 0; + char port[256]; // Read three lines, check them for validity, and register the service. - if ( good && ! feof(fp) ) { - good = ReadALine(name, sizeof(name), fp); - if (good) { - good = ReadALine(type, sizeof(type), fp); - } - if (good) { - char *p = type; - while (*p && *p != ' ') p++; - if (*p) { - *p = 0; - dom = p+1; - } - } - if (good) { - good = ReadALine(rawText, sizeof(rawText), fp); - } - if (good) { - good = ReadALine(port, sizeof(port), fp); - } - if (good) { - good = CheckThatRichTextHostNameIsUsable(name, mDNSfalse) - && CheckThatServiceTypeIsUsable(type, mDNSfalse) - && CheckThatServiceTextIsUsable(rawText, mDNSfalse, text, &textLen) - && CheckThatPortNumberIsUsable(atol(port), mDNSfalse); - } - if (good) { - status = RegisterOneService(name, type, dom, text, textLen, atol(port)); - if (status != mStatus_NoError) { - fprintf(stderr, - "%s: Failed to register service, name = %s, type = %s, port = %s\n", - gProgramName, - name, - type, - port); - status = mStatus_NoError; // keep reading - } - } - } + good = ReadALine(name, sizeof(name), fp); + if (good) { + good = ReadALine(type, sizeof(type), fp); + } + if (good) { + char *p = type; + while (*p && *p != ' ') p++; + if (*p) { + *p = 0; + dom = p+1; + } + } + if (good) { + good = ReadALine(port, sizeof(port), fp); + } + if (good) { + good = CheckThatRichTextHostNameIsUsable(name, mDNSfalse) + && CheckThatServiceTypeIsUsable(type, mDNSfalse) + && CheckThatPortNumberIsUsable(atol(port), mDNSfalse); + } + if (good) { + while (1) { + if (!ReadALine(rawText, sizeof(rawText), fp)) break; + text[textLen] = strlen(rawText); + if (text[textLen] == 0) break; + memcpy(text + textLen + 1, rawText, text[textLen]); + textLen += 1 + text[textLen]; + } + } + if (good) { + status = RegisterOneService(name, type, dom, text, textLen, atol(port)); + if (status != mStatus_NoError) { + fprintf(stderr, + "%s: Failed to register service, name = %s, type = %s, port = %s\n", + gProgramName, + name, + type, + port); + status = mStatus_NoError; // keep reading + } + } } while (good && !feof(fp)); if ( ! good ) { - fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, gServiceFile); + fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath); } } @@ -761,43 +704,6 @@ static void DeregisterOurServices(void) #pragma mark **** Main #endif -#ifdef NOT_HAVE_DAEMON - - // The version of Solaris that I tested on didn't have the daemon - // call. This implementation was basically stolen from the - // Mac OS X standard C library. - - static int daemon(int nochdir, int noclose) - { - int fd; - - switch (fork()) { - case -1: - return (-1); - case 0: - break; - default: - _exit(0); - } - - if (setsid() == -1) - return (-1); - - if (!nochdir) - (void)chdir("/"); - - if (!noclose && (fd = _open("/dev/null", O_RDWR, 0)) != -1) { - (void)dup2(fd, STDIN_FILENO); - (void)dup2(fd, STDOUT_FILENO); - (void)dup2(fd, STDERR_FILENO); - if (fd > 2) - (void)_close(fd); - } - return (0); - } - -#endif /* NOT_HAVE_DAEMON */ - int main(int argc, char **argv) { mStatus status; diff --git a/mDNSPosix/Services.txt b/mDNSPosix/Services.txt index a7b73da..f5870bb 100755 --- a/mDNSPosix/Services.txt +++ b/mDNSPosix/Services.txt @@ -1,14 +1,15 @@ Tweedlebug _afpovertcp._tcp. -name=val1 548 +name=val1 Tweedlebug2 _afpovertcp._tcp. local. -name=val2name2=anotherattribute 548 +name=val2 +name2=anotherattribute Tweedlebug3 -_afpovertcp._tcp. apple.com. -name=val3 +_afpovertcp._tcp. 548 +name=val3 diff --git a/mDNSPosix/dnsextd.c b/mDNSPosix/dnsextd.c new file mode 100644 index 0000000..41bc5e7 --- /dev/null +++ b/mDNSPosix/dnsextd.c @@ -0,0 +1,1983 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * 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@ + + Change History (most recent first): + +$Log: dnsextd.c,v $ +Revision 1.23 2004/12/06 20:24:31 ksekar + dnsextd leaks sockets + +Revision 1.22 2004/12/03 20:20:29 ksekar + dnsextd: support delivery of large records via LLQ events + +Revision 1.21 2004/12/03 06:11:34 ksekar + clean up dnsextd arguments + +Revision 1.20 2004/12/01 04:27:28 cheshire + Darwin patches for Solaris and Suse +Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc. + +Revision 1.19 2004/12/01 01:16:29 cheshire +Solaris compatibility fixes + +Revision 1.18 2004/11/30 23:51:06 cheshire +Remove double semicolons + +Revision 1.17 2004/11/30 22:37:01 cheshire +Update copyright dates and add "Mode: C; tab-width: 4" headers + +Revision 1.16 2004/11/25 02:02:28 ksekar +Fixed verbose log message argument + +Revision 1.15 2004/11/19 02:35:02 ksekar + Wide Area Rendezvous Security: Add LLQ-ID to events + +Revision 1.14 2004/11/17 06:17:58 cheshire +Update comments to show correct SRV names: _dns-update._udp.. and _dns-llq._udp.. + +Revision 1.13 2004/11/13 02:22:36 ksekar + Refresh Acks from daemon malformatted + +Revision 1.12 2004/11/12 01:05:01 ksekar + dnsextd: daemon registers the SRV same record +twice at startup + +Revision 1.11 2004/11/12 01:03:31 ksekar + dnsextd: KnownAnswers (CacheRecords) leaked + +Revision 1.10 2004/11/12 00:35:28 ksekar + dnsextd: uninitialized pointer can cause crash + +Revision 1.9 2004/11/10 20:38:17 ksekar + dnsextd: allow a "fudge" in LLQ lease echo + +Revision 1.8 2004/11/01 17:48:14 cheshire +Changed SOA serial number back to signed. RFC 1035 may describe it as "unsigned", but +it's wrong. The SOA serial is a modular counter, as explained in "DNS & BIND", page +137. Since C doesn't have a modular type, we used signed, C's closest approximation. + +Revision 1.7 2004/10/30 00:06:58 ksekar + Support Long Lived Queries in DNS Extension daemon + +Revision 1.6 2004/09/17 01:08:54 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.5 2004/09/16 00:50:54 cheshire +Don't use MSG_WAITALL -- it returns "Invalid argument" on some Linux versions + +Revision 1.4 2004/09/14 23:27:48 cheshire +Fix compile errors + +Revision 1.3 2004/09/02 01:39:40 cheshire +For better readability, follow consistent convention that QR bit comes first, followed by OP bits + +Revision 1.2 2004/08/24 23:27:57 cheshire +Fixes for Linux compatibility: +Don't use strings.h +Don't assume SIGINFO +Don't try to set servaddr.sin_len on platforms that don't have sa_len + +Revision 1.1 2004/08/11 00:43:26 ksekar +: DNS Extension daemon for DNS Update Lease + +*/ + +#include "../mDNSCore/mDNSEmbeddedAPI.h" +#include "../mDNSCore/DNSCommon.h" +#include "../mDNSCore/mDNS.c" +//!!!KRS we #include mDNS.c for the various constants defined there - we should move these to DNSCommon.h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Compatibility workaround +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + +// +// Constants +// + +#define LOOPBACK "127.0.0.1" +#define NS_PORT 53 +#define DAEMON_PORT 5355 // default, may be overridden via command line argument +#define LISTENQ 128 // tcp connection backlog +#define RECV_BUFLEN 9000 +#define LEASETABLE_INIT_NBUCKETS 256 // initial hashtable size (doubles as table fills) +#define LLQ_TABLESIZE 1024 // !!!KRS make this dynamically growable +#define EXPIRATION_INTERVAL 300 // check for expired records every 5 minutes +#define SRV_TTL 7200 // TTL For _dns-update SRV records + +// LLQ Lease bounds (seconds) +#define LLQ_MIN_LEASE (15 * 60) +#define LLQ_MAX_LEASE (120 * 60) +#define LLQ_LEASE_FUDGE 60 + +// LLQ SOA poll interval (microseconds) +#define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000) +#define LLQ_MONITOR_INTERVAL 250000 +#ifdef SIGINFO +#define INFO_SIGNAL SIGINFO +#else +#define INFO_SIGNAL SIGUSR1 +#endif + +#define SAME_INADDR(x,y) (*((mDNSu32 *)&x) == *((mDNSu32 *)&y)) +#define ZERO_LLQID(x) (!memcmp(x, "\x0\x0\x0\x0", 8)) + +// +// Data Structures +// Structs/fields that must be locked for thread safety are explicitly commented +// + +typedef struct + { + struct sockaddr_in src; + size_t len; + DNSMessage msg; + // Note: extra storage for oversized (TCP) messages goes here + } PktMsg; + +// lease table entry +typedef struct RRTableElem + { + struct RRTableElem *next; + struct sockaddr_in cli; // client's source address + long expire; // expiration time, in seconds since epoch + domainname zone; // from zone field of update message + CacheRecord rr; // last field in struct allows for allocation of oversized RRs + } RRTableElem; + +typedef enum + { + RequestReceived = 0, + ChallengeSent = 1, + Established = 2 + } LLQState; + +// llq table entry +typedef struct LLQEntry + { + struct LLQEntry *next; + struct sockaddr_in cli; // clien'ts source address + domainname qname; + mDNSu16 qtype; + mDNSu8 id[8]; + LLQState state; + mDNSu32 lease; // original lease, in seconds + mDNSs32 expire; // expiration, absolute, in seconds since epoch + CacheRecord *KnownAnswers;// !!!KRS this should be shared amongst identical questions + } LLQEntry; + +// daemon-wide information +typedef struct + { + // server variables - read only after initialization (no locking) + struct in_addr saddr; // server address + domainname zone; // zone being updated + int tcpsd; // listening TCP socket + int udpsd; // listening UDP socket + + // daemon variables - read only after initialization (no locking) + uDNS_AuthInfo *AuthInfo; // linked list of keys for signing deletion updates + mDNSIPPort port; // listening port + + // lease table variables (locked via mutex after initialization) + RRTableElem **table; // hashtable for records with leases + pthread_mutex_t tablelock; // mutex for lease table + mDNSs32 nbuckets; // buckets allocated + mDNSs32 nelems; // elements in table + + // LLQ table variables + LLQEntry *LLQTable[LLQ_TABLESIZE]; // !!!KRS change this and RRTable to use a common data structure + int LLQEventListenSock; // Unix domain socket pair - polling thread writes to ServPollSock, which wakes + int LLQServPollSock; // the main thread listening on EventListenSock, indicating that the zone has changed + } DaemonInfo; + +// args passed to UDP request handler thread as void* +typedef struct + { + PktMsg pkt; + struct sockaddr_in cliaddr; + DaemonInfo *d; + } UDPRequestArgs; + +// args passed to TCP request handler thread as void* +typedef struct + { + int sd; // socket connected to client + struct sockaddr_in cliaddr; + DaemonInfo *d; + } TCPRequestArgs; + +// +// Global Variables +// + +// booleans to determine runtime output +// read-only after initialization (no mutex protection) +static mDNSBool foreground = 0; +static mDNSBool verbose = 0; + +// globals set via signal handler (accessed exclusively by main select loop and signal handler) +static mDNSBool terminate = 0; +static mDNSBool dumptable = 0; + +// +// Logging Routines +// Log messages are delivered to syslog unless -f option specified +// + +// common message logging subroutine +mDNSlocal void PrintLog(const char *buffer) + { + if (foreground) + { + fprintf(stderr,"%s\n", buffer); + fflush(stderr); + } + else + { + openlog("dnsextd", LOG_CONS | LOG_PERROR, LOG_DAEMON); + syslog(LOG_ERR, "%s", buffer); + closelog(); + } + } + +// Verbose Logging (conditional on -v option) +mDNSlocal void VLog(const char *format, ...) + { + unsigned char buffer[512]; + va_list ptr; + + if (!verbose) return; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + PrintLog(buffer); + } + +// Unconditional Logging +mDNSlocal void Log(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); + PrintLog(buffer); + } + +// Error Logging +// prints message "dnsextd : - " +// must be compiled w/ -D_REENTRANT for thread-safe errno usage +mDNSlocal void LogErr(const char *fn, const char *operation) + { + char buf[512]; + snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, strerror(errno)); + PrintLog(buf); + } + +// +// Networking Utility Routines +// + +// Convert DNS Message Header from Network to Host byte order +mDNSlocal void HdrNToH(PktMsg *pkt) + { + // Read the integer parts which are in IETF byte-order (MSB first, LSB second) + mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions; + pkt->msg.h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + pkt->msg.h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + pkt->msg.h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + pkt->msg.h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + } + +// Convert DNS Message Header from Host to Network byte order +mDNSlocal void HdrHToN(PktMsg *pkt) + { + mDNSu16 numQuestions = pkt->msg.h.numQuestions; + mDNSu16 numAnswers = pkt->msg.h.numAnswers; + mDNSu16 numAuthorities = pkt->msg.h.numAuthorities; + mDNSu16 numAdditionals = pkt->msg.h.numAdditionals; + mDNSu8 *ptr = (mDNSu8 *)&pkt->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); + } + +// create a socket connected to nameserver +// caller terminates connection via close() +mDNSlocal int ConnectToServer(DaemonInfo *d) + { + struct sockaddr_in servaddr; + int sd; + + bzero(&servaddr, sizeof(servaddr)); + if (d->saddr.s_addr) servaddr.sin_addr = d->saddr; + else inet_pton(AF_INET, LOOPBACK, &d->saddr); // use loopback if server not explicitly specified + servaddr.sin_port = htons(NS_PORT); + servaddr.sin_family = AF_INET; +#ifndef NOT_HAVE_SA_LEN + servaddr.sin_len = sizeof(servaddr); +#endif + sd = socket(AF_INET, SOCK_STREAM, 0); + if (sd < 0) { LogErr("ConnectToServer", "socket"); return -1; } + if (connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { LogErr("ConnectToServer", "connect"); return -1; } + return sd; + } + +// send an entire block of data over a connected socket, blocking if buffers are full +mDNSlocal int MySend(int sd, const void *msg, int len) + { + int n, nsent = 0; + + while (nsent < len) + { + n = send(sd, (char *)msg + nsent, len - nsent, 0); + if (n < 0) { LogErr("MySend", "send"); return -1; } + nsent += n; + } + return 0; + } + +// Transmit a DNS message, prefixed by its length, over TCP, blocking if necessary +mDNSlocal int SendTCPMsg(int sd, PktMsg *pkt) + { + // send the lenth, in network byte order + mDNSu16 len = htons((mDNSu16)pkt->len); + if (MySend(sd, &len, sizeof(len)) < 0) return -1; + + // send the message + return MySend(sd, &pkt->msg, pkt->len); + } + +// Receive len bytes, waiting until we have all of them. +// Returns number of bytes read (which should always be the number asked for). +static int my_recv(const int sd, void *const buf, const int len) + { + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; + // use an explicit while() loop instead. + // Also, don't try to do '+=' arithmetic on the original "void *" pointer -- + // arithmetic on "void *" pointers is compiler-dependent. + int remaining = len; + char *ptr = (char *)buf; + while (remaining) + { + ssize_t num_read = recv(sd, ptr, remaining, 0); + if ((num_read == 0) || (num_read < 0) || (num_read > remaining)) return -1; + ptr += num_read; + remaining -= num_read; + } + return(len); + } + +// Return a DNS Message read off of a TCP socket, or NULL on failure +// If storage is non-null, result is placed in that buffer. Otherwise, +// returned value is allocated with Malloc, and contains sufficient extra +// storage for a Lease OPT RR + +mDNSlocal PktMsg *ReadTCPMsg(int sd, PktMsg *storage) + { + int nread, allocsize; + mDNSu16 msglen = 0; + PktMsg *pkt = NULL; + int srclen; + + nread = my_recv(sd, &msglen, sizeof(msglen)); + if (nread < 0) { LogErr("TCPRequestForkFn", "recv"); goto error; } + msglen = ntohs(msglen); + if (nread != sizeof(msglen)) { Log("Could not read length field of message"); goto error; } + + if (storage) + { + if (msglen > sizeof(storage->msg)) { Log("ReadTCPMsg: provided buffer too small."); goto error; } + pkt = storage; + } + else + { + // buffer extra space to add an OPT RR + if (msglen > sizeof(DNSMessage)) allocsize = sizeof(PktMsg) - sizeof(DNSMessage) + msglen; + else allocsize = sizeof(PktMsg); + pkt = malloc(allocsize); + if (!pkt) { LogErr("ReadTCPMsg", "malloc"); goto error; } + bzero(pkt, sizeof(*pkt)); + } + + pkt->len = msglen; + srclen = sizeof(pkt->src); + if (getpeername(sd, (struct sockaddr *)&pkt->src, &srclen) || + srclen != sizeof(pkt->src)) { LogErr("ReadTCPMsg", "getpeername"); bzero(&pkt->src, sizeof(pkt->src)); } + nread = my_recv(sd, &pkt->msg, msglen); + if (nread < 0) { LogErr("TCPRequestForkFn", "recv"); goto error; } + if (nread != msglen) { Log("Could not read entire message"); goto error; } + if (pkt->len < sizeof(DNSMessageHeader)) + { Log("ReadTCPMsg: Message too short (%d bytes)", pkt->len); goto error; } + HdrNToH(pkt); + return pkt; + //!!!KRS convert to HBO here? + error: + if (pkt && pkt != storage) free(pkt); + return NULL; + } + +// +// Dynamic Update Utility Routines +// + +// Get the lease life of records in a dynamic update +// returns -1 on error or if no lease present +mDNSlocal mDNSs32 GetPktLease(PktMsg *pkt) + { + mDNSs32 lease = -1; + const mDNSu8 *ptr = NULL, *end = (mDNSu8 *)&pkt->msg + pkt->len; + LargeCacheRecord lcr; + int i; + + HdrNToH(pkt); + ptr = LocateAdditionals(&pkt->msg, end); + if (ptr) + for (i = 0; i < pkt->msg.h.numAdditionals; i++) + { + ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); + if (!ptr) { Log("Unable to read additional record"); 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 = (mDNSs32)lcr.r.resrec.rdata->u.opt.OptData.lease; + break; + } + } + + HdrHToN(pkt); + return lease; + } + +// check if a request and server response complete a successful dynamic update +mDNSlocal mDNSBool SuccessfulUpdateTransaction(PktMsg *request, PktMsg *reply) + { + char buf[32]; + char *vlogmsg = NULL; + + // check messages + if (!request || !reply) { vlogmsg = "NULL message"; goto failure; } + if (request->len < sizeof(DNSMessageHeader) || reply->len < sizeof(DNSMessageHeader)) { vlogmsg = "Malformatted message"; goto failure; } + + // check request operation + if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask)) + { vlogmsg = "Request opcode not an update"; goto failure; } + + // check result + if ((reply->msg.h.flags.b[1] & kDNSFlag1_RC)) { vlogmsg = "Reply contains non-zero rcode"; goto failure; } + if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_OP_Update | kDNSFlag0_QR_Response)) + { vlogmsg = "Reply opcode not an update response"; goto failure; } + + VLog("Successful update from %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32)); + return mDNStrue; + + failure: + VLog("Request %s: %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32), vlogmsg); + return mDNSfalse; + } + +// Allocate an appropriately sized CacheRecord and copy data from original +mDNSlocal CacheRecord *CopyCacheRecord(CacheRecord *orig) + { + CacheRecord *cr; + size_t size = sizeof(*cr); + if (orig->resrec.rdlength > InlineCacheRDSize) size += orig->resrec.rdlength - InlineCacheRDSize; + cr = malloc(size); + if (!cr) { LogErr("CopyCacheRecord", "malloc"); return NULL; } + memcpy(cr, orig, size); + cr->resrec.rdata = (RData*)&cr->rdatastorage; + return cr; + } + + +// +// Lease Hashtable Utility Routines +// + +// double hash table size +// caller must lock table prior to invocation +mDNSlocal void RehashTable(DaemonInfo *d) + { + RRTableElem *ptr, *tmp, **new; + int i, bucket, newnbuckets = d->nbuckets * 2; + + new = malloc(sizeof(RRTableElem *) * newnbuckets); + if (!new) { LogErr("RehashTable", "malloc"); return; } + bzero(new, newnbuckets * sizeof(RRTableElem *)); + + for (i = 0; i < d->nbuckets; i++) + { + ptr = d->table[i]; + while (ptr) + { + bucket = ptr->rr.resrec.namehash % newnbuckets; + tmp = ptr; + ptr = ptr->next; + tmp->next = new[bucket]; + new[bucket] = tmp; + } + } + d->nbuckets = newnbuckets; + } + +// print entire contents of hashtable, invoked via SIGINFO +mDNSlocal void PrintLeaseTable(DaemonInfo *d) + { + int i; + RRTableElem *ptr; + char rrbuf[80], addrbuf[16]; + struct timeval now; + int hr, min, sec; + + if (gettimeofday(&now, NULL)) { LogErr("PrintTable", "gettimeofday"); return; } + if (pthread_mutex_lock(&d->tablelock)) { LogErr("PrintTable", "pthread_mutex_lock"); return; } + + Log("Dumping Lease Table Contents (table contains %d resource records)", d->nelems); + for (i = 0; i < d->nbuckets; i++) + { + for (ptr = d->table[i]; ptr; ptr = ptr->next) + { + hr = ((ptr->expire - now.tv_sec) / 60) / 60; + min = ((ptr->expire - now.tv_sec) / 60) % 60; + sec = (ptr->expire - now.tv_sec) % 60; + Log("Update from %s, Expires in %d:%d:%d\n\t%s", inet_ntop(AF_INET, &ptr->cli.sin_addr, addrbuf, 16), hr, min, sec, + GetRRDisplayString_rdb(&ptr->rr.resrec, &ptr->rr.resrec.rdata->u, rrbuf)); + } + } + pthread_mutex_unlock(&d->tablelock); + } + +// +// Startup SRV Registration Routines +// Register _dns-update._udp/_tcp. SRV records indicating the port on which +// the daemon accepts requests +// + +// delete all RRS of a given name/type +mDNSlocal mDNSu8 *putRRSetDeletion(DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit, ResourceRecord *rr) + { + ptr = putDomainNameAsLabels(msg, ptr, limit, &rr->name); + if (!ptr || ptr + 10 >= limit) return NULL; // out of space + ptr[0] = (mDNSu8)(rr->rrtype >> 8); + ptr[1] = (mDNSu8)(rr->rrtype & 0xFF); + ptr[2] = (mDNSu8)((mDNSu16)kDNSQClass_ANY >> 8); + ptr[3] = (mDNSu8)((mDNSu16)kDNSQClass_ANY & 0xFF); + bzero(ptr+4, sizeof(rr->rroriginalttl) + sizeof(rr->rdlength)); // zero ttl/rdata + msg->h.mDNS_numUpdates++; + return ptr + 10; + } + +mDNSlocal mDNSu8 *PutUpdateSRV(DaemonInfo *d, PktMsg *pkt, mDNSu8 *ptr, char *regtype, mDNSBool registration) + { + AuthRecord rr; + char hostname[1024], buf[80]; + mDNSu8 *end = (mDNSu8 *)&pkt->msg + sizeof(DNSMessage); + + mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, NULL, NULL); + rr.resrec.rrclass = kDNSClass_IN; + rr.resrec.rdata->u.srv.priority = 0; + rr.resrec.rdata->u.srv.weight = 0; + rr.resrec.rdata->u.srv.port.NotAnInteger = d->port.NotAnInteger; + if (!gethostname(hostname, 1024) < 0 || MakeDomainNameFromDNSNameString(&rr.resrec.rdata->u.srv.target, hostname)) + rr.resrec.rdata->u.srv.target.c[0] = '\0'; + + MakeDomainNameFromDNSNameString(&rr.resrec.name, regtype); + strcpy(rr.resrec.name.c + strlen(rr.resrec.name.c), d->zone.c); + VLog("%s %s", registration ? "Registering SRV record" : "Deleting existing RRSet", + GetRRDisplayString_rdb(&rr.resrec, &rr.resrec.rdata->u, buf)); + if (registration) ptr = PutResourceRecord(&pkt->msg, ptr, &pkt->msg.h.mDNS_numUpdates, &rr.resrec); + else ptr = putRRSetDeletion(&pkt->msg, ptr, end, &rr.resrec); + return ptr; + } + + +// perform dynamic update. +// specify deletion by passing false for the register parameter, otherwise register the records. +mDNSlocal int UpdateSRV(DaemonInfo *d, mDNSBool registration) + { + int sd = -1; + mDNSOpaque16 id; + PktMsg pkt; + mDNSu8 *ptr = pkt.msg.data; + mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage); + mDNSu16 nAdditHBO; // num additionas, in host byte order, required by message digest routine + PktMsg *reply = NULL; + + int result = -1; + + // Initialize message + id.NotAnInteger = 0; + InitializeDNSMessage(&pkt.msg.h, id, UpdateReqFlags); + pkt.src.sin_addr.s_addr = htonl(INADDR_ANY); // address field set solely for verbose logging in subroutines + pkt.src.sin_family = AF_INET; + + // format message body + ptr = putZone(&pkt.msg, ptr, end, &d->zone, mDNSOpaque16fromIntVal(kDNSClass_IN)); + if (!ptr) goto end; + + ptr = PutUpdateSRV(d, &pkt, ptr, "_dns-update._udp.", registration); if (!ptr) goto end; + ptr = PutUpdateSRV(d, &pkt, ptr, "_dns-update._tcp.", registration); if (!ptr) goto end; + ptr = PutUpdateSRV(d, &pkt, ptr, "_dns-llq._udp.", registration); if (!ptr) goto end; + + nAdditHBO = pkt.msg.h.numAdditionals; + HdrHToN(&pkt); + if (d->AuthInfo) + { + ptr = DNSDigest_SignMessage(&pkt.msg, &ptr, &nAdditHBO, d->AuthInfo); + if (!ptr) goto end; + } + pkt.len = ptr - (mDNSu8 *)&pkt.msg; + + // send message, receive reply + sd = ConnectToServer(d); + if (sd < 0) { Log("UpdateSRV: ConnectToServer failed"); goto end; } + if (SendTCPMsg(sd, &pkt)) { Log("UpdateSRV: SendTCPMsg failed"); } + reply = ReadTCPMsg(sd, NULL); + if (!SuccessfulUpdateTransaction(&pkt, reply)) + Log("SRV record registration failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC); + else result = 0; + + end: + if (!ptr) { Log("UpdateSRV: Error constructing lease expiration update"); } + if (sd >= 0) close(sd); + if (reply) free(reply); + return result; + } + +// wrapper routines/macros +#define ClearUpdateSRV(d) UpdateSRV(d, 0) + +// clear any existing records prior to registration +mDNSlocal int SetUpdateSRV(DaemonInfo *d) + { + int err; + + err = ClearUpdateSRV(d); // clear any existing record + if (!err) err = UpdateSRV(d, 1); + return err; + } + +// +// Argument Parsing and Configuration +// + +// read authentication information for a zone from command line argument +// global optind corresponds to keyname argument on entry +mDNSlocal int ReadAuthKey(int argc, char *argv[], DaemonInfo *d) + { + uDNS_AuthInfo *auth = NULL; + char keybuf[512]; + mDNSs32 keylen; + + auth = malloc(sizeof(*auth)); + if (!auth) { perror("ReadAuthKey, malloc"); goto error; } + auth->next = NULL; + if (argc < optind + 1) return -1; // keyname + secret + if (!MakeDomainNameFromDNSNameString(&auth->keyname, optarg)) + { fprintf(stderr, "Bad key name %s", optarg); goto error; } + keylen = DNSDigest_Base64ToBin(argv[optind++], keybuf, 512); + if (keylen < 0) + { fprintf(stderr, "Bad shared secret %s (must be base-64 encoded string)", argv[optind-1]); goto error; } + DNSDigest_ConstructHMACKey(auth, keybuf, (mDNSu32)keylen); + d->AuthInfo = auth; + return 0; + + error: + if (auth) free(auth); + return -1; + } + +mDNSlocal int SetPort(DaemonInfo *d, char *PortAsString) + { + long l; + + l = strtol(PortAsString, NULL, 10); // convert string to long + if ((!l && errno == EINVAL) || l > 65535) return -1; // error check conversion + d->port.NotAnInteger = htons((mDNSu16)l); // set to network byte order + return 0; + } + +mDNSlocal void PrintUsage(void) + { + fprintf(stderr, "Usage: dnsextd -z [-vf] [ -s server ] [-k zone keyname secret] ...\n" + "Use \"dnsextd -h\" for help\n"); + } + +mDNSlocal void PrintHelp(void) + { + fprintf(stderr, "\n\n"); + PrintUsage(); + + fprintf(stderr, + "dnsextd is a daemon that implements DNS extensions supporting Dynamic DNS Update Leases\n" + "and Long Lived Queries, used in Wide-Area DNS Service Discovery, on behalf of name servers\n" + "that do not natively support these extensions. (See dns-sd.org for more info on DNS Service\n" + "Discovery, Update Leases, and Long Lived Queries.)\n\n" + + "dnsextd requires one argument,the zone, which is the domain for which Update Leases\n" + "and Long Lived Queries are to be administered. dnsextd communicates directly with the\n" + "primary master server for this zone.\n\n" + + "The options are as follows:\n\n" + + "-f Run daemon in foreground.\n\n" + + "-h Print help.\n\n" + + "-k Specify TSIG authentication key for dynamic updates from daemon to name server.\n" + " -k option is followed by the name of the key, and the shared secret as a base-64\n" + " encoded string. This key/secret are used by the daemon to delete resource records\n" + " from the server when leases expire. Clients are responsible for signing their\n" + " update requests.\n\n" + + "-s Specify address (IPv4 address in dotted-decimal notation) of the Primary Master\n" + " name server. Defaults to loopback (127.0.0.1), i.e. daemon and name server\n" + " running on the same machine.\n\n" + + "-v Verbose output.\n\n" + ); + } + +// Note: ProcessArgs called before process is daemonized, and therefore must open no descriptors +// returns 0 (success) if program is to continue execution +// output control arguments (-f, -v) do not affect this routine +mDNSlocal int ProcessArgs(int argc, char *argv[], DaemonInfo *d) + { + int opt; + + if (argc < 2) goto arg_error; + + d->port.NotAnInteger = htons(DAEMON_PORT); // default, may be overriden by command option + while ((opt = getopt(argc, argv, "z:p:hfvs:k:")) != -1) + { + switch(opt) + { + case 'p': if (SetPort(d, optarg) < 0) goto arg_error; + break; + + case 'h': PrintHelp(); return -1; + case 'f': foreground = 1; break; + case 'v': verbose = 1; break; + case 's': if (!inet_pton(AF_INET, optarg, &d->saddr)) goto arg_error; + break; + case 'k': if (ReadAuthKey(argc, argv, d) < 0) goto arg_error; + break; + case 'z': if (!MakeDomainNameFromDNSNameString(&d->zone, optarg)) + { + fprintf(stderr, "Bad zone %s", optarg); + goto arg_error; + } + break; + default: goto arg_error; + } + } + + if (!d->zone.c[0]) goto arg_error; // zone is the only required argument + if (d->AuthInfo) AssignDomainName(d->AuthInfo->zone, d->zone); // if we have a shared secret, use it for the entire zone + return 0; + + arg_error: + PrintUsage(); + return -1; + } + + +// +// Initialization Routines +// + +// Allocate memory, initialize locks and bookkeeping variables +mDNSlocal int InitLeaseTable(DaemonInfo *d) + { + if (pthread_mutex_init(&d->tablelock, NULL)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; } + d->nbuckets = LEASETABLE_INIT_NBUCKETS; + d->nelems = 0; + d->table = malloc(sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS); + if (!d->table) { LogErr("InitLeaseTable", "malloc"); return -1; } + bzero(d->table, sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS); + return 0; + } +mDNSlocal int SetupSockets(DaemonInfo *daemon) + { + struct sockaddr_in daddr; + int sockpair[2]; + + // set up sockets on which we receive requests + bzero(&daddr, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (daemon->port.NotAnInteger) daddr.sin_port = daemon->port.NotAnInteger; + else daddr.sin_port = htons(DAEMON_PORT); + + daemon->tcpsd = socket(AF_INET, SOCK_STREAM, 0); + if (!daemon->tcpsd) { LogErr("SetupSockets", "socket"); return -1; } + if (bind(daemon->tcpsd, (struct sockaddr *)&daddr, sizeof(daddr)) < 0) { LogErr("SetupSockets", "bind"); return -1; } + if (listen(daemon->tcpsd, LISTENQ) < 0) { LogErr("SetupSockets", "listen"); return -1; } + + daemon->udpsd = socket(AF_INET, SOCK_DGRAM, 0); + if (!daemon->udpsd) { LogErr("SetupSockets", "socket"); return -1; } + if (bind(daemon->udpsd, (struct sockaddr *)&daddr, sizeof(daddr)) < 0) { LogErr("SetupSockets", "bind"); return -1; } + + // set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockpair) < 0) { LogErr("SetupSockets", "socketpair"); return -1; } + daemon->LLQEventListenSock = sockpair[0]; + daemon->LLQServPollSock = sockpair[1]; + return 0; + } + +// +// periodic table updates +// + +// Delete a resource record from the nameserver via a dynamic update +mDNSlocal void DeleteRecord(DaemonInfo *d, CacheRecord *rr, domainname *zone) + { + int sd = -1; + mDNSOpaque16 id; + PktMsg pkt; + mDNSu8 *ptr = pkt.msg.data; + mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage); + mDNSu16 nAdditHBO; // num additionas, in host byte order, required by message digest routine + char buf[80]; + PktMsg *reply = NULL; + + VLog("Expiring record %s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, buf)); + sd = ConnectToServer(d); + if (sd < 0) { Log("DeleteRecord: ConnectToServer failed"); goto end; } + + id.NotAnInteger = 0; + InitializeDNSMessage(&pkt.msg.h, id, UpdateReqFlags); + + ptr = putZone(&pkt.msg, ptr, end, zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass)); + if (!ptr) goto end; + ptr = putDeletionRecord(&pkt.msg, ptr, &rr->resrec); + if (!ptr) goto end; + + nAdditHBO = pkt.msg.h.numAdditionals; + HdrHToN(&pkt); + + if (d->AuthInfo) + { + ptr = DNSDigest_SignMessage(&pkt.msg, &ptr, &nAdditHBO, d->AuthInfo); + if (!ptr) goto end; + } + + pkt.len = ptr - (mDNSu8 *)&pkt.msg; + pkt.src.sin_addr.s_addr = htonl(INADDR_ANY); // address field set solely for verbose logging in subroutines + pkt.src.sin_family = AF_INET; + if (SendTCPMsg(sd, &pkt)) { Log("DeleteRecord: SendTCPMsg failed"); } + reply = ReadTCPMsg(sd, NULL); + if (!SuccessfulUpdateTransaction(&pkt, reply)) + Log("Expiration update failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC); + + end: + if (!ptr) { Log("DeleteRecord: Error constructing lease expiration update"); } + if (sd >= 0) close(sd); + if (reply) free(reply); + } + +// iterate over table, deleting expired records +mDNSlocal void DeleteExpiredRecords(DaemonInfo *d) + { + int i; + RRTableElem *ptr, *prev, *fptr; + struct timeval now; + + if (gettimeofday(&now, NULL)) { LogErr("DeleteExpiredRecords ", "gettimeofday"); return; } + if (pthread_mutex_lock(&d->tablelock)) { LogErr("DeleteExpiredRecords", "pthread_mutex_lock"); return; } + for (i = 0; i < d->nbuckets; i++) + { + ptr = d->table[i]; + prev = NULL; + while (ptr) + { + if (ptr->expire - now.tv_sec < 0) + { + // delete record from server + DeleteRecord(d, &ptr->rr, &ptr->zone); + if (prev) prev->next = ptr->next; + else d->table[i] = ptr->next; + fptr = ptr; + ptr = ptr->next; + free(fptr); + d->nelems--; + } + else + { + prev = ptr; + ptr = ptr->next; + } + } + } + pthread_mutex_unlock(&d->tablelock); + } + +// +// main update request handling +// + +// Add, delete, or refresh records in table based on contents of a successfully completed dynamic update +mDNSlocal void UpdateLeaseTable(PktMsg *pkt, DaemonInfo *d, mDNSs32 lease) + { + RRTableElem *prev, *rptr, *new = NULL; + int i, bucket; + LargeCacheRecord lcr; + const mDNSu8 *ptr, *end; + struct timeval time; + DNSQuestion zone; + char buf[80]; + + if (pthread_mutex_lock(&d->tablelock)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; } + HdrNToH(pkt); + ptr = pkt->msg.data; + end = (mDNSu8 *)&pkt->msg + pkt->len; + ptr = getQuestion(&pkt->msg, ptr, end, 0, &zone); + if (!ptr) { Log("UpdateLeaseTable: cannot read zone"); goto cleanup; } + ptr = LocateAuthorities(&pkt->msg, end); + if (!ptr) { Log("UpdateLeaseTable: Format error"); goto cleanup; } + + for (i = 0; i < pkt->msg.h.mDNS_numUpdates; i++) + { + ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { Log("UpdateLeaseTable: GetLargeResourceRecord returned NULL"); goto cleanup; } + //!!!KRS we should include rdata in hash here + bucket = lcr.r.resrec.namehash % d->nbuckets; + + // look for RR in table + prev = NULL; + rptr = d->table[bucket]; + while (rptr) + { + if (SameResourceRecord(&rptr->rr.resrec, &lcr.r.resrec)) break; + prev = rptr; + rptr = rptr->next; + } + + if (rptr) + { + // Record is already in table + if (!lcr.r.resrec.rroriginalttl && lcr.r.resrec.rrclass == kDNSClass_NONE) + { + // deletion record + VLog("Received deletion update for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); + if (prev) prev->next = rptr->next; + else d->table[bucket] = rptr->next; + free(rptr); + d->nelems--; + } + else + { + // refresh + if (lease < 0) + { + Log("Update for record %s already in lease table with no refresh lease specified", + GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); + } + else + { + if (gettimeofday(&time, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; } + rptr->expire = time.tv_sec + (unsigned)lease; + VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); + } + } + } + else if (lease > 0) + { + // New record - add to table + if (d->nelems > d->nbuckets) RehashTable(d); + if (gettimeofday(&time, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; } + new = malloc(sizeof(RRTableElem) + lcr.r.resrec.rdlength - InlineCacheRDSize); + if (!new) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup; } + memcpy(&new->rr, &lcr.r, sizeof(CacheRecord) + lcr.r.resrec.rdlength - InlineCacheRDSize); + new->rr.resrec.rdata = (RData *)&new->rr.rdatastorage; + new->expire = time.tv_sec + (unsigned)lease; + new->cli.sin_addr = pkt->src.sin_addr; + strcpy(new->zone.c, zone.qname.c); + new->next = d->table[bucket]; + d->table[bucket] = new; + d->nelems++; + VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf)); + } + } + + cleanup: + pthread_mutex_unlock(&d->tablelock); + HdrHToN(pkt); + } + +// Given a successful reply from a server, create a new reply that contains lease information +// Replies are currently not signed !!!KRS change this +mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease) + { + PktMsg *reply; + mDNSu8 *ptr, *end; + mDNSOpaque16 flags; + + (void)d; //unused + reply = malloc(sizeof(*reply)); + if (!reply) { LogErr("FormatLeaseReply", "malloc"); return NULL; } + flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; + flags.b[1] = 0; + + InitializeDNSMessage(&reply->msg.h, orig->msg.h.id, flags); + reply->src.sin_addr.s_addr = htonl(INADDR_ANY); // unused except for log messages + reply->src.sin_family = AF_INET; + ptr = reply->msg.data; + end = (mDNSu8 *)&reply->msg + sizeof(DNSMessage); + ptr = putUpdateLease(&reply->msg, ptr, lease); + if (!ptr) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply); return NULL; } + reply->len = ptr - (mDNSu8 *)&reply->msg; + return reply; + } + +// pkt is thread-local, not requiring locking +mDNSlocal PktMsg *HandleRequest(PktMsg *pkt, DaemonInfo *d) + { + int sd = -1; + PktMsg *reply = NULL, *LeaseReply; + mDNSs32 lease; + char buf[32]; + + // send msg to server, read reply + sd = ConnectToServer(d); + if (sd < 0) + { Log("Discarding request from %s due to connection errors", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; } + if (SendTCPMsg(sd, pkt) < 0) + { Log("Couldn't relay message from %s to server. Discarding.", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; } + reply = ReadTCPMsg(sd, NULL); + + // process reply + if (!SuccessfulUpdateTransaction(pkt, reply)) + { VLog("Message from %s not a successful update.", inet_ntop(AF_INET, &pkt->src.sin_addr, buf, 32)); goto cleanup; } + lease = GetPktLease(pkt); + UpdateLeaseTable(pkt, d, lease); + if (lease > 0) + { + LeaseReply = FormatLeaseReply(d, reply, lease); + if (!LeaseReply) Log("HandleRequest - unable to format lease reply"); + free(reply); + reply = LeaseReply; + } + cleanup: + if (sd >= 0) close(sd); + return reply; + } + + +// +// LLQ Support Routines +// + +// Set fields of an LLQ Opt Resource Record +mDNSlocal void FormatLLQOpt(AuthRecord *opt, int opcode, mDNSu8 *id, mDNSs32 lease) + { + bzero(opt, sizeof(*opt)); + opt->resrec.rdata = &opt->rdatastorage; + opt->resrec.RecordType = kDNSRecordTypeKnownUnique; // to suppress warnings from other layers + opt->resrec.rrtype = kDNSType_OPT; + opt->resrec.rdlength = LLQ_OPT_SIZE; + opt->resrec.rdestimate = LLQ_OPT_SIZE; + opt->resrec.rdata->u.opt.opt = kDNSOpt_LLQ; + opt->resrec.rdata->u.opt.optlen = sizeof(LLQOptData); + opt->resrec.rdata->u.opt.OptData.llq.vers = kLLQ_Vers; + opt->resrec.rdata->u.opt.OptData.llq.llqOp = opcode; + opt->resrec.rdata->u.opt.OptData.llq.err = LLQErr_NoError; + memcpy(opt->resrec.rdata->u.opt.OptData.llq.id, id, 8); + opt->resrec.rdata->u.opt.OptData.llq.lease = lease; + } + +// Calculate effective remaining lease of an LLQ +mDNSlocal mDNSu32 LLQLease(LLQEntry *e) + { + struct timeval t; + + gettimeofday(&t, NULL); + if (e->expire < t.tv_sec) return 0; + else return e->expire - t.tv_sec; + } + +mDNSlocal void FreeKnownAnswers(LLQEntry *e) + { + CacheRecord *tmp; + + while(e->KnownAnswers) + { + tmp = e->KnownAnswers; + e->KnownAnswers = e->KnownAnswers->next; + free(tmp); + } + } + +mDNSlocal void DeleteLLQ(DaemonInfo *d, LLQEntry *e) + { + int bucket = bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE; + LLQEntry *prev = NULL, *ptr = d->LLQTable[bucket]; + char addr[32]; + + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + VLog("Deleting LLQ table entry for %##s client %s", e->qname.c, addr); + + FreeKnownAnswers(e); + while(ptr) + { + if (ptr == e) + { + if (prev) prev->next = ptr->next; + else d->LLQTable[bucket] = ptr->next; + free(e); + return; + } + prev = ptr; + ptr = ptr->next; + } + Log("Error: DeleteLLQ - LLQ not in table"); + } + +mDNSlocal int SendLLQ(DaemonInfo *d, PktMsg *pkt, struct sockaddr_in dst) + { + char addr[32]; + int err = -1; + + HdrHToN(pkt); + if (sendto(d->udpsd, &pkt->msg, pkt->len, 0, (struct sockaddr *)&dst, sizeof(dst)) != (int)pkt->len) + { + LogErr("DaemonInfo", "sendto"); + Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32)); + } + else err = 0; + HdrNToH(pkt); + return err; + } + +// if non-negative, sd is a TCP socket connected to the nameserver +// otherwise, this routine creates and closes its own socket +mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, LLQEntry *e, int sd) + { + PktMsg q; + int i; + const mDNSu8 *ansptr; + mDNSu8 *end = q.msg.data; + mDNSOpaque16 id, flags = QueryFlags; + PktMsg *reply = NULL; + LargeCacheRecord lcr; + CacheRecord *AnswerList = NULL; + mDNSu8 rcode; + mDNSBool CloseSDOnExit = sd < 0; + + VLog("Querying server for %##s type %d", e->qname.c, e->qtype); + + flags.b[0] |= kDNSFlag0_RD; // recursion desired + id.NotAnInteger = 0; + InitializeDNSMessage(&q.msg.h, id, flags); + + end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); + if (!end) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end; } + q.len = (int)(end - (mDNSu8 *)&q.msg); + + if (sd < 0) sd = ConnectToServer(d); + if (sd < 0) { Log("AnswerQuestion: ConnectToServer failed"); goto end; } + if (SendTCPMsg(sd, &q)) { Log("AnswerQuestion: SendTCPMsg failed"); close(sd); goto end; } + reply = ReadTCPMsg(sd, NULL); + + if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery)) + { Log("AnswerQuestion: %##s type %d - Invalid response flags from server"); goto end; } + rcode = (mDNSu8)(reply->msg.h.flags.b[1] & kDNSFlag1_RC); + if (rcode && rcode != kDNSFlag1_RC_NXDomain) { Log("AnswerQuestion: %##s type %d - non-zero rcode %d from server", e->qname.c, e->qtype, rcode); goto end; } + + end = (mDNSu8 *)&reply->msg + reply->len; + ansptr = LocateAnswers(&reply->msg, end); + if (!ansptr) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end; } + + for (i = 0; i < reply->msg.h.numAnswers; i++) + { + //rr = malloc(sizeof(*rr)); + //if (!rr) { LogErr("AnswerQuestion", "malloc"); goto end; } + ansptr = GetLargeResourceRecord(NULL, &reply->msg, ansptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ansptr) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end; } + if (lcr.r.resrec.rrtype != e->qtype || lcr.r.resrec.rrclass != kDNSClass_IN || !SameDomainName(&lcr.r.resrec.name, &e->qname)) + { + Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d. Discarding", + lcr.r.resrec.name.c, lcr.r.resrec.rrtype, e->qname.c, e->qtype); + } + else + { + CacheRecord *cr = CopyCacheRecord(&lcr.r); + if (!cr) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end; } + cr->next = AnswerList; + AnswerList = cr; + } + } + + end: + if (sd > -1 && CloseSDOnExit) close(sd); + if (reply) free(reply); + e->state = Established; + return AnswerList; + } + +mDNSlocal void UpdateAnswerList(DaemonInfo *d, LLQEntry *e, CacheRecord *answers) + { + CacheRecord *prev = NULL, *na, *ka; // "new answer", "known answer" + PktMsg response; + mDNSu8 *end = (mDNSu8 *)&response.msg.data; + mDNSOpaque16 msgID; + char rrbuf[80], addrbuf[32]; + AuthRecord opt; + + // first pass - mark all answers for deletion + for (ka = e->KnownAnswers; ka; ka = ka->next) + ka->resrec.rroriginalttl = -1; // -1 means delete + + // second pass - mark answers pre-existent + for (ka = e->KnownAnswers; ka; ka = ka->next) + { + for (na = answers; na; na = na->next) + { + if (SameResourceRecord(&ka->resrec, &na->resrec)) + { ka->resrec.rroriginalttl = 0; break; } // 0 means no change + } + } + + // third pass - add new records + for (na = answers; na; na = na->next) + { + for (ka = e->KnownAnswers; ka; ka = ka->next) + if (SameResourceRecord(&ka->resrec, &na->resrec)) break; + if (!ka) + { + // answer is not in KA list + CacheRecord *cr = CopyCacheRecord(na); + if (!cr) { Log("Error: UpdateAnswerList - CopyCacheRecord returned NULL"); return; } + cr->resrec.rroriginalttl = 1; // 1 means add + cr->next = e->KnownAnswers; + e->KnownAnswers = cr; + } + } + + // now send the update + msgID.NotAnInteger = random(); + InitializeDNSMessage(&response.msg.h, msgID, ResponseFlags); + end = putQuestion(&response.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); + if (!end) { Log("Error: UpdateAnswerList - putQuestion returned NULL"); return; } + + if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32); + // put adds/removes in packet + for (ka = e->KnownAnswers; ka; ka = ka->next) + { + if (ka->resrec.rroriginalttl) + { + if (verbose) GetRRDisplayString_rdb(&ka->resrec, &ka->resrec.rdata->u, rrbuf); + VLog("%s (%s): %s", addrbuf, (mDNSs32)ka->resrec.rroriginalttl < 0 ? "Remove": "Add", rrbuf); + end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAnswers, &ka->resrec, ka->resrec.rroriginalttl); + if (!end) { Log("Error: UpdateAnswerList - UpdateAnswerList returned NULL"); return; } + } + } + + // delete removes from list + ka = e->KnownAnswers; + while (ka) + { + if ((mDNSs32)ka->resrec.rroriginalttl < 0) + { + CacheRecord *fptr = ka; + if (prev) prev->next = ka->next; + else e->KnownAnswers = ka->next; + ka = ka->next; + free(fptr); + } + else + { + prev = ka; + ka = ka->next; + } + } + + FormatLLQOpt(&opt, kLLQOp_Event, e->id, LLQLease(e)); + end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAdditionals, &opt.resrec, 0); + if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; } + + response.len = (int)(end - (mDNSu8 *)&response.msg); + if (response.msg.h.numAnswers) + if (SendLLQ(d, &response, e->cli) < 0) LogErr("UpdateAnswerList", "SendLLQ"); + } + +mDNSlocal void PrintLLQTable(DaemonInfo *d) + { + LLQEntry *e; + char addr[32]; + int i; + + Log("Printing LLQ table contents"); + + for (i = 0; i < LLQ_TABLESIZE; i++) + { + e = d->LLQTable[i]; + while(e) + { + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + Log("LLQ from %##s type %d lease %d (%d remaining)", + addr, e->qname.c, e->qtype, e->lease, LLQLease(e)); + e = e->next; + } + } + } + +// Send events to clients as a result of a change in the zone +mDNSlocal void GenLLQEvents(DaemonInfo *d) + { + LLQEntry *e, *tmp; + int i, sd; + struct timeval t; + char addr[32]; + VLog("Generating LLQ Events"); + + gettimeofday(&t, NULL); + sd = ConnectToServer(d); + if (sd < 0) { Log("GenLLQEvents: ConnectToServer failed"); return; } + + for (i = 0; i < LLQ_TABLESIZE; i++) + { + e = d->LLQTable[i]; + while(e) + { + if (e->expire < t.tv_sec) + { + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + VLog("Expiring LLQ %##s from %s", e->qname.c, addr); + tmp = e; + e = e->next; + DeleteLLQ(d, tmp); + } + else + { + CacheRecord *answers = AnswerQuestion(d, e, sd); + UpdateAnswerList(d, e, answers); + e = e->next; + } + } + } + close(sd); + } + +// Monitor zone for changes that may produce LLQ events +mDNSlocal void *LLQEventMonitor(void *DInfoPtr) + { + DaemonInfo *d = DInfoPtr; + PktMsg q; + mDNSu8 *end = q.msg.data; + const mDNSu8 *ptr; + mDNSOpaque16 id, flags = QueryFlags; + PktMsg reply; + mDNSs32 serial = 0; + mDNSBool SerialInitialized = mDNSfalse; + int sd; + LargeCacheRecord lcr; + ResourceRecord *rr = &lcr.r.resrec; + int i, sleeptime = 0; + domainname zone; + char pingmsg[4]; + + // create question + id.NotAnInteger = 0; + InitializeDNSMessage(&q.msg.h, id, flags); + AssignDomainName(zone, d->zone); + end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &zone, kDNSType_SOA, kDNSClass_IN); + if (!end) { Log("Error: LLQEventMonitor - putQuestion returned NULL"); return NULL; } + q.len = (int)(end - (mDNSu8 *)&q.msg); + + sd = ConnectToServer(d); + if (sd < 0) { Log("LLQEventMonitor: ConnectToServer failed"); return NULL; } + + while(1) + { + usleep(sleeptime); + sleeptime = LLQ_MONITOR_ERR_INTERVAL; // if we bail on error below, rate limit retry + + // send message, receive response + if (SendTCPMsg(sd, &q)) { Log("LLQEventMonitor: SendTCPMsg failed"); continue; } + if (!ReadTCPMsg(sd, &reply)) { Log("LLQEventMonitor: ReadTCPMsg failed"); continue; } + end = (mDNSu8 *)&reply.msg + reply.len; + if (reply.msg.h.flags.b[1] & kDNSFlag1_RC) { Log("LLQEventMonitor - received non-zero rcode"); continue; } + + // find answer + ptr = LocateAnswers(&reply.msg, end); + if (!ptr) { Log("Error: LLQEventMonitor - LocateAnswers returned NULL"); continue; } + for (i = 0; i < reply.msg.h.numAnswers; i++) + { + ptr = GetLargeResourceRecord(NULL, &reply.msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); + if (!ptr) { Log("Error: LLQEventMonitor - GetLargeResourceRecord returned NULL"); continue; } + if (rr->rrtype != kDNSType_SOA || rr->rrclass != kDNSClass_IN || !SameDomainName(&rr->name, &zone)) continue; + if (!SerialInitialized) + { + // first time through loop + SerialInitialized = mDNStrue; + serial = rr->rdata->u.soa.serial; + sleeptime = LLQ_MONITOR_INTERVAL; + break; + } + else if (rr->rdata->u.soa.serial != serial) + { + // update serial, wake main thread + serial = rr->rdata->u.soa.serial; + VLog("LLQEventMonitor: zone changed. Signaling main thread."); + if (send(d->LLQServPollSock, pingmsg, sizeof(pingmsg), 0) != sizeof(pingmsg)) + { LogErr("LLQEventMonitor", "send"); break; } + } + sleeptime = LLQ_MONITOR_INTERVAL; + break; + } + if (!ptr) Log("LLQEventMonitor: response to query did not contain SOA"); + } + } + + // Allocate LLQ entry, insert into table +mDNSlocal LLQEntry *NewLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, mDNSu32 lease) + { + char addr[32]; + struct timeval t; + int bucket; + LLQEntry *e = malloc(sizeof(*e)); + if (!e) { LogErr("NewLLQ", "malloc"); return NULL; } + + inet_ntop(AF_INET, &cli.sin_addr, addr, 32); + VLog("Allocating LLQ entry for client %s question %##s type %d", addr, qname->c, qtype); + + // initialize structure + e->cli = cli; + AssignDomainName(e->qname, *qname); + e->qtype = qtype; + memset(e->id, 0, 8); + e->state = RequestReceived; + e->KnownAnswers = NULL; + + if (lease < LLQ_MIN_LEASE) lease = LLQ_MIN_LEASE; + else if (lease > LLQ_MAX_LEASE) lease = LLQ_MIN_LEASE; + gettimeofday(&t, NULL); + e->expire = t.tv_sec + (int)lease; + e->lease = lease; + + // add to table + bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE; + e->next = d->LLQTable[bucket]; + d->LLQTable[bucket] = e; + + return e; + } + +// Handle a refresh request from client +mDNSlocal void LLQRefresh(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID) + { + AuthRecord opt; + PktMsg ack; + mDNSu8 *end = (mDNSu8 *)&ack.msg.data; + char addr[32]; + + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + VLog("%s LLQ for %##s from %s", llq->lease ? "Refreshing" : "Deleting", e->qname.c, addr); + + if (llq->lease) + { + if (llq->lease < LLQ_MIN_LEASE) llq->lease = LLQ_MIN_LEASE; + else if (llq->lease > LLQ_MAX_LEASE) llq->lease = LLQ_MIN_LEASE; + } + + ack.src.sin_addr.s_addr = 0; // unused + InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags); + end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); + if (!end) { Log("Error: putQuestion"); return; } + + FormatLLQOpt(&opt, kLLQOp_Refresh, e->id, llq->lease ? LLQLease(e) : 0); + end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0); + if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; } + + ack.len = (int)(end - (mDNSu8 *)&ack.msg); + if (SendLLQ(d, &ack, e->cli)) Log("Error: LLQRefresh"); + + if (llq->lease) e->state = Established; + else DeleteLLQ(d, e); + } + +// Complete handshake with Ack an initial answers +mDNSlocal void LLQCompleteHandshake(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID) + { + char addr[32]; + CacheRecord *ptr; + AuthRecord opt; + PktMsg ack; + mDNSu8 *end = (mDNSu8 *)&ack.msg.data; + char rrbuf[80], addrbuf[32]; + + inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32); + + if (memcmp(llq->id, e->id, 8) || + llq->vers != kLLQ_Vers || + llq->llqOp != kLLQOp_Setup || + llq->err != LLQErr_NoError || + llq->lease > e->lease + LLQ_LEASE_FUDGE || + llq->lease < e->lease - LLQ_LEASE_FUDGE) + { Log("Incorrect challenge response from %s", addr); return; } + + if (e->state == Established) VLog("Retransmitting LLQ ack + answers for %##s", e->qname.c); + else VLog("Delivering LLQ ack + answers for %##s", e->qname.c); + + // format ack + answers + ack.src.sin_addr.s_addr = 0; // unused + InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags); + end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN); + if (!end) { Log("Error: putQuestion"); return; } + + if (e->state != Established) e->KnownAnswers = AnswerQuestion(d, e, -1); // only fetch KA list the first time through + if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32); + for (ptr = e->KnownAnswers; ptr; ptr = ptr->next) + { + if (verbose) GetRRDisplayString_rdb(&ptr->resrec, &ptr->resrec.rdata->u, rrbuf); + VLog("%s Intitial Answer - %s", addr, rrbuf); + end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAnswers, &ptr->resrec, 1); + if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; } + } + + FormatLLQOpt(&opt, kLLQOp_Setup, e->id, LLQLease(e)); + end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0); + if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; } + + ack.len = (int)(end - (mDNSu8 *)&ack.msg); + if (SendLLQ(d, &ack, e->cli)) Log("Error: LLQCompleteHandshake"); + } + +mDNSlocal void LLQSetupChallenge(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID) + { + struct timeval t; + mDNSu32 randval; + PktMsg challenge; + mDNSu8 *end = challenge.msg.data; + AuthRecord opt; + + if (e->state == ChallengeSent) VLog("Retransmitting LLQ setup challenge for %##s", e->qname.c); + else VLog("Sending LLQ setup challenge for %##s", e->qname.c); + + if (!ZERO_LLQID(llq->id)) { Log("Error: LLQSetupChallenge - nonzero ID"); return; } // server bug + if (llq->llqOp != kLLQOp_Setup) { Log("LLQSetupChallenge - incorrrect operation from client"); return; } // client error + + if (ZERO_LLQID(e->id)) // don't regenerate random ID for retransmissions + { + // construct ID

wQnxDKE>_jGEM8pVh_o*h z@5&dC3;wR0`#VTShm&*vKtriklcdhRbe_^CNp8eLdG>eNPBl3&OWq&>Ii5W8#%`Qj z%JGP~jJyXVCxCK%;yJE4Kiy~#XqW!snlnix;u<;V8aC425)a(*11 zBb6FYSJ-Y^GWW0-^;;f$IX#ChK0k$>!b zoZ=#9mmnKI<@($F>g5(=uW8uUjeA6FlAwBx)a!3Bj+(^6)&*XFD>|Oxy8hM~Z<5aq z3$sGOp@;W6pZyR#J2tjBIF>$A^+&;gX&TKgeh zcFd~ZTPEMGk6BBO$38=uF*mi|3+RfoSYpMBhIzL@}JcZ3qmmJTK+pVy%aq;;n zY;0UYJaU_!XZ{oQhuf2eCiQNFY^#!K7V^Tdja9-?+v_{7 z(+7DEucH8JQN`(8PVb~_J{hY5?{Bv_Gt8fy;hJA7sL!>?jq81G$?-Vsb4!lLVV_%i zJhl;`j>(6>S86;?``qH=DXfnwKAyt*s8Zu`G7ph?cF|Yc6-?-!fOaxZFH6+%Nwolj6cgnHb~47p{WrrCPhroP7M-60$KXZBGhElW#WSWk zh1%iIg3ghs6qr2K?4Yg!8q-?K3v~`E0wuZ}Z zv;hh-e9yAlW=C=%KXQ&o(=)Re`r$UARhE-<jiy%(s7@W+}xe||qIXXAu zaWY5eMm%DUPHtuj{v)3&2Z!msT=r91*|{c~{YRmon-R4yhjCJW3Yw!UIUf5AWzH>2 zj>mTD%tgC7oC7K~KO=39uK4^EHb+--JVSnbl>CgeIlAKQrp%q$O#q?AbgiB`ouey0 zKbbi#yQ>f`0`E%JB@>eNB1h z=v>Q>n4_b^TF9cz(b>+RqMvi&{FD;qAFjDTF-KQO8KAHo^v5se=*SEOFQaLWE>K6G zq&B~Yjto-;Dd)=eW(w5JmUm?Qf{e1A^K)|hb7lA9u|G$0FP{E>JIlEikNxztIhKk?!@k-?VnJPBDjo0jEe~(nh z^Wif{J>gI~XFJ)er7xZ6Gxa&WIVZgQhBd21G`9L$66r{9|J8VG$4c)JIamR4Uxt88sx3yiJoq9_GoYTigs!sje@X45J}a z4!Xo)ef9BBUoW|5N)m+Yr&jurQudXT$IT`kD<)Ybtr zKSO({L`j>euQePE2UB62GK%g$ z6Uw5bYJ*spS8XV(QmYMPQFgUKEK9I4kV!e#2C^(X+CUaXNE^hwAmxTI9KGBC#zLhH zVNtZSLCgzSZU~EFmK)4Ax#mSqy9?8Tr&OTaZa-UQX$MLen?k1qurG2-7`p@Y zN&wr!rVV0I)RZ8$1x*QJQ_PeA)`cwVZ+gtK-sS~N@wX{nN&xG^mG!qNTG>E0iL@?O z%H~|Cl%rM~jT&08tdFh$Wu0@2P1Z+WShDU4@hM5xIj3OcdSr@2)+477WIc3;KI^PK z;CffKpx#kExLH@-Avy4)R{Q+w!eokz>3$4(xn-7__6-5qVZzI`? z4T~+JfG@sPWttzQh}fGrCycp)l|cE-tAw&LuHtKN+H4q~g<&-z%%+8SbYf#vWe4Ua z&G=;vnl0e6r>3Nu_^8IqdKpcY^~)M6>y~RCy-(ICIqqzV99OkeWu4V*>s+##H7~=| z&o%Ui!k9Y4If_j|GYy!H%GBFB$|ccwCr@IW^-&tUO`p)aVf3RIvk*$^&8AqDGrk461<`CLnJ{XRGz(;EJhOn=l4|Cw8y=Yj$Qg=R2g&qG zW1oKVGC$eT@P!vJq5DKfp+L; zwz;uUG7F^}otg!p#>DP`kbmB+P#~MEz}vQYT0q;|9WB}4yzg9)J4!Omq%zTHTQYMS zZL|HzJ(5MArj$E5glV6q@i)(^(r?jW&(qi`0p*x*IkGnx>muXl4qlW{WVt-MQwMPl zVpfCZi(3unAa*60ZOV(2+LK{?Zo|a#m`1Y+qU?j3j#~2zFPT|3yD%EFDs?M!O~pJx zvJq@UDtl}V_r}v9A7q+G7!kwmuhZX6R3gm8bJ+cV+J)yz|IhvK>19e4QOW!lW?Zy zP>hy&@fnU|IvtK#94>XImcdb2^wpdkbOSbWkmIBxp^l2kF?ZBPZu(Igx#&h^x_xo( z$W2$-$U$4R=y&eSD!FM2p+vu_9;w}``r9!4Ry}kTPTiZqF=qfq;j%+I`Xv`PU5_B| zOJ7;lfZ8)-)h&Bwl5yd#Ec09Ky2yd@%)LJSq%3QGDlKDk7&j?!<(0S57h`od_;jvp zHtaGxQcyh&{g>*YO$^1Z{cT&wbfY5Ln_}C{_<)>pMu_A@ahN5B8#7%Gh4Ad0TY%Zw ztQfPGNnvImLy>0p+=87ON49vgPi`S+=bWO>t|kSZeR7IDyXF*rjjLG|(0J#Tc}*-9 zUdBqn+vutWyO~xHH4Z~H(QK4cSu{SnDx-DJRvfLXri8Myz7nyczACY!X@yevVyITC z2kIzSuB?l$ie;Vc$~Gm_nE0GZ$GfttdYK@an`^RF#aInFys}*wVSS4>u{KORu5AxA&bbYO z#!K5GXq@E(!MA4RI@9sVaWk|NIsUrVK;uWTTlwWQ6`HNGZH30s*jQ*hbB2E!AGsOP z%sS_IW!sBf59=bC8`4lF4FQx}?Q)&f`m1r*v_2YFI#}G3Yg{eMyRl)?Zl!OTv_9N4 z>7BA|liuCfIOX`8Xse5WL&JPekO>awr72mVM^w=T7JoavdGn# zerjs2?5nG+S^eyRS#|Qv^9$SEh~}Ysi`;QkW*F2DW`Uj zlWB!8R6Ba9ISp2nJ0-H-xGtjgpdIR6vO&25n$G{5hEgZCOg*g{U+wj*?1u8CGK0x; z{EhL+n;Bw~UA2eFG)rU(@=*7x{?-&X^R%*^@Es=y4f3L3I@FEfSavcrnQgo_P}$^k^8-7-K8i)7~KUJgRiD5SFbRchCwGJzpF{@x$Fa}4V6_W`u>BViQ_y` ziVzzGF;q^aqqoZN0*kC_$1BE6%D&dw(|a2;ruv&^N$bg(p<`@v*vMIze7P*Z6x@`n z=~kU{6Krr-Q>yXMBr(U8)7F}q=xYlnH$*}?SEh~Ly5XwP+c0=F2H|?0sTjr(`hl&{ zUpLG(dS}vLnMd6a*ti4jfY|7pJ3KaqFzu+#gQ?xN(N|MVC8Mu){B7Y&qj7UD!`R$B zn67a&2B8Gz36e94H|`@lrZ@T-NBG7tIWw2W0P^{OW&+bZMD|?3!r%JLz+wZ#d4g3q z<*dQNSFO=TZz^KkoEkmp$XI%6PCiVw)SZeL{m}-YqtjjnHHOK#wr>_dJC--`NAl?^ zkEu`g6vx!tIF>aA(~gIYzGOUWyK7ICjN4kCIGF|2oJJYFb55#^0dh{Ui~&qfxD3JM zwo*SSVG+i@;aB#PX?fL|TZ?_=?E;kD<>?2MK=z$MPP(jm1SOb#x1fZv?HiOJc{>Ot zn6q9Y8`iR`P=eU@7fKM@PD2yKwCBkB=kGqUVeLBzC0zcFLZCQ?Y7F_;b_vqlhJnjvupyD-PrX%Y(1M!NC{!r z6Y)OmyCWr#gFcD(rk_8v$i04E$tsXl7sWAYZ~U>@g<-15BCKtXm5I{2+fstq_FYO4 z=N*_D-mVu@!sP49lu-HlGbNO1r)GNQqB`ojE!9!pnf9hT)g!lKQr-1Ek?N}JdQ`{E zzPYba-F2Od>Zt8aRJYvjLv=UpFSMR&2cf!Ydj!=@+Xd*{sLj_p7#ex4r><33Jq*pb z>XFlKtIpYmT6HnD%&Je$IA3+nZF{xu*)hG&)w*@n1v50KdS7MKu5(ixOx01-LaI(= zAs&~jPMS7Qb;6D`cjBn7##T=C$r*2FowcLvj4L->n%~(GwZ_pnj#fRjZIS9m8OzPT zwzW~iSvD`(0GjqhbtJRUMZaa%+L)p}{ z?NGeT8jzg$^sR{EY1Wh|J|^vn;$hLKD4upLOEyGK^P+f|v@wc@NkgOY(6lyLSF0u` z8^WyJQGBf$AH~62X#(MXPrI%GT`H z9E`-&TS_w_7MO3SI_tNQ=be@JR;m$cob+LP>sPeFT!}dDK27RpCG=jphFb3>Cto|Z z(|hMO)p}ojTdnugHP(8sOq|?W>wR_2wcbnHUh92w8*II=X^U+PqBhxjA8nhh_t7@m zCO*_^8=Vc!wlRpV-PU^>8g9LJPRp(L%r@P6cVpYF_sOFH?Z=-K^WNG4OJ-##v zY-qsE!YE@+6Cbq+*L!K&aJ>gvnn#~{4^1ns_t1|-^?t^7T<@PVD$VuOj!kp?xG6Wd zX2+=pFXL!cA4J=l>wU;5=62M!=lX3d8}!^@nigH}mD8l_J#*T0y{BoTt`CyaIGcLf zG`mXkuNx!lcA@xNH^7R2rbSf8%O?AU{hCDMuHz|L%ExjhD%xK+WA2wrlt1avseofpwez)3>Y15@_Zq*O4bu2mq#n-Aw z;B9rIdy}N)=^J=2`jNiLZuH}QvjApYMJ9h1{YAzy z4Qth#DE@X`iW0)EUr|Ej?_88{c0G(Pgua_`@vY-xSHc3q0wcl#$9W}bf270%)qxLS z{up>N^A_-d%%2Az#QYTaVCGlBQ%fOGKMFpa z`9<&%%>M*`gn8dFl5`~VT<~<}0C)y-415&xmEfb9?*-3f{sDLv^BdsV%!~$=7+$?G5-{NJoCHY`OJIc10Ww|o(ryKJ{5cd za}2zI`BLzS%(sG1VtyKYGV^c1r!fBqypVanv6587JQrNcyaK$4xd&Xwd>Oc&`3`Uc z^EbhZnO_1gVIBl8Wj<`2BsDTG1utWcf|oO20d8V`5ZuiC5_kpkhu{|GY2zhH{=gD` z*MU!E_bzZN^CjRm<}ZTVnZFM{jrm>h>CF4?CP^!qj|Z#YIc)`>!R{NtXEI+4K8yJq z;ML5pfIr6kA^2?OX}e3(8s>U%2lF~`ka-)plld-ii1|C;7#m4VNXdqhj|+KT;`90H#08De0AIm;C3rjY9pEdO9|M1e`8DuW%;Wc#q^p_dg0Epd z75rJ|jo@pUZvbtXZwKGZd@pzh^H0FH zF#iL5EAym%CFwTinePPO#r!Ss7ny$v{u1-wz+Yw_KS`4A zW}XVZhj~8uUgo9XuQ0Cx-^Y9@_hPBz)vt=0DhABR`65I&w`(3ehd6f=5Yr| z(zlrBf}ddyfS+YffWOUr75F>M4}ibR{5<%3%zpgSAl=Xyb1g~ z^Y!2#F+Tx*f%z5ikC{IN|AhI_110IF%nQIjV?G=FbLOq!UohVR{w4Ew!7nnu1%8Qn z{6UiRE9QCNmzh_Af6aUW_!Z{6!M|bt8Thx%gWy-0eFsa@@0jbruQ7+gzi0j=_;u#{ z!EZ3X4E_W22jDlEr%aKgx0n}#-)0Vi-(kK4{72?{z<*+X0sLp?_rZT*o_vTT{U7s* z;J-3=g8#<64g4h4{wsJK^LU>mjb}a-yc_ee;N6)QgC{VzgZE(W0q@Ct9(XV2E5Q?)Zv*em`~Y|# z=4ZhBGXE4jiTO3~e$4*@@6WvZG>re54**YQo&i3PxfXm7^Qqv2nSHcw;KP{@I1J-|=DFaHFgJpaWbOb@XFdl!gZWDEQOtLO zk7j-vJd^pC;91On2G3@ma5%>Q%+tYhm=}SMVLl5ympKle$9x(1SmrN)k7IrUd_40n z!1I~^1pX*<#Ss|)GfxJez&sngfVmNTB6BDBB<3{uWai7jr!apBypZ`Ra1HY>z_rZ( z1}|cs_z{e)nU4b3GcN)+FbBblnbY7U%r}9TGCvM(Wd1pL8S|gP%b6>W#Q2|i8n~JH zB=8F6)4(mvz2E@zHt?y;JHV~X4};s7e*$i2ej9umGo3J=&O8aclKD9BD&_$A4CW~K zOy*0#XEEOhUd{Xn_+!jJ0iVtMXYd;4su>vnGamyEGM@_WWbOusmeggaj=I6n;GrtDDgZW+Xoy^{2F#c!W3;adq8Q?E5pA7yob1V36<}mmk z<`no|=1+pZ!u&b#ea!cP?`Qrd_^ZrsfFEEU1V6~U*IbPMnSJ1gnCE~WWN@UNI30l&=r zBKX(L{{_FoJmYwb|CyJ9f6E*Ozsh_$_;<{AfnQ^O4*YxOcfhYRkDHJ2Kl5SWKQJ!@ zzsbB7{1)>T@Y~GSfZt*M8u*XQ&w>BM{08{X%pZdP!o1%{G5%+s0sbrVDd4{`pACMO zc`Nwu%sasUV15St9`oDae=_e?jqyM8Jn+AmPXqs(IR*X?^JU=wGJg^L0rPjk|6_g& z{2_DY2^jw~9|o4Z5SA0b9_D3WFLMxF!Mq7PhWRt#O6I%3Rm@L-$1=YH9>@Fvcs%pe z1sMM`*MfIv4uU5z4}kYzz7@PD^V8tHn12hN$m}^0?PgAZZ87knu5^Wdq>gJ2)?^pi3E zXKnx=#@q!yocRLq5zLl;XMz_qw}F>1Zvro6z5(3G{4MY@=C{GinJ3m^{Lg$mxS4r9cm?xy;1=d@ zfCJ3G1)s|7smJ)A`EYO>a|5`Yxd(h2^Ht!}nI8qOWd03!74!eVXE0A|!1$lJ8GIIV z0=$~}DllC?qvav++01_cuVLP8F~nZF0#%={+!Jm&v`&u5<4i19!3 z;ot%0`QQtf8^K$d*MKi%-UPmgc{})G<~zWbFh2zT1oQX7+nC=1f0FqF@TJTXmtp+R zd<6J%<_7Smn9l@%n)&14E10)|w=>@kzLNP#@MoA`0$;`aKKN?pNy{<*XPyK8EOR6H zTIRLj>zD_?*E3%azJd86@QutbfbwZ-eh(u58BmpZPHGUCbwgzsMW`e~I~W@Ryly2j9*7B={cYUxM#t z{y*?nn8&Wb_@DVO@cqoS;IA@&4Ez9dKlnlBPl3P2d?)xJ=I?7&<9}u!_*=}!f}de-0zb>V z4*YHAt>Eu4-va(F^ONB3F~1Ccj`^S9?=w$272|*AIp7~MH-evMUI+dW^QGVyn7;u2 zG4m7PpD@1&{weeSfq%xlPbquMb3XKn`nmU$icRpzbW-!b0^evSD-@b8&_2!5UUZSWh+Z-L)q{yq4g%oV3&{Lefc{4eHZ;D0l(2mgopGVp(y?*)Ir{5<%7%zp-d$UJc+ z#{bOKV5vgxqNSB!5Az1Fm-!lS1@ryjG0ZQ4E1CZXu410H3gds~I`BB=4)A#9i^01w z-wEEG`8n_e=0Ae>U_RswjQ^PzgZE+%gC{ay4BnghHt;^oPlNYm{w;VCGu^AAHduVp3Hn9_(0~{zy~pZAAB(LJK!nI!z4$06m`*&nM5FAfg&y7q=tE= zJ(aiEM;jLXSxj9Up8VF6(QVF6(QVF6(QVF6(QVF6(QVF6(QVF6)*VX?qy zJ#im{EBm`O^oK)z>0oEn*=%F3!53m_ZdipomuYGf%S8FFD}PkG@0CrXKbcB~3f;LU z^YnJ1TaKRa3r0^?L{5sZfY(2#h^^y&O zD50i$0HcxP&sBfzO6$FLrNoM~4v7WYn-VNvDOkHR({ zC80|+;V7{}d1qr|II^K9)mz?15V53_F+cVI3NWE5H;or-48sYhY<9A^o)ZcF2v!^w4FA3ojVcr#ro9WMFqy*TRS2lKr{Yx;_kZig$?LQ!*t*D`r7tdtOAX7?b8dguL!*BTY#PoLsbx` zle_+*6geot0&<@qYC#DW6r1k)7LaSf@9=)omNvR?4qXElagUnPJ>rTzjG-)D;kCiO zXnIXBk?_7k*smVnTR+O5oaRV`XI>00&=fHrz4oRiArtlt@uVjzD1n9zJUvk7{U--+ugxg|oJ&E!}_oXv={8F4m~Qz^txa|;aJ*-WLh zyRwYK34g+sn{w6AJt_jjjzqa{S;q>#w*^w^iC zeC5qaJib69zH(>3l(q ztV6qSeB_<`&xs_gW5M^UU1;A{M)0~5Uw`#{iwmwqWtL{y z)67HTro=OIrpC#D?!{xDA@}03J%n@&U9y|~{avobgZt?W2gPLvrA@crsSEj;D92-e zc=NqCFF?Wk(MYFV z@TN#7XQqS-MDfHsd+g$A>FjYQo?$pX&B0Jhn{R&ga*ICqU}SW(ov$rwYR-G!p(!gc zj@{9=_FLQ(7?ya3>paT0Y*CAKZWW*Az)H337j3uBc#~Y8^Tk!7|L~p{=4k;leN@p_ zQ^&@3J)zvUD`UKpd-<^+6uB6WZA7lc6YjT-r^GTXZ!Y)kwj(cB%0Yo+*7?=z zi_BbMW7bmRaX4lzzWx+8W-U1$r(@Ql?WW`>6<$~5;dQ*(Xq&(3jCR|OzsWFxhQ+1# zr+w*RiHFiVY-9amUYt(}biQ<~h{tR{BH+Qkl7akVN#{{ohOAM>>489<#W;NuHZAjU zI*em!KUP0AQTe%+sG=EuTy zw2dK#wJ+?nKj~f+j7H<}m~Uyg__t3g^Y*5PFXPx6J}1Lwkk6mC8Ln&CPR@bM@=&N7 z%6|9k79Z-U`2hMr>~OS|{iZYO3z>gbOjM|05q?o))9}Po*t~tw`6=*xpy+tY`h0+_ zX3}H2em+ogJPywXN{+{Nn4Ue4Dm@4EY=I z1H&~|8Exk`)CK$V+$yZOK20TIcHRrK7XA8goOj6i>Dpv_cZO^EiCbipo!y8BZDb%A z?e*ct3A~;tif>uzZ+M@Gxa6{L-*sf$bp75k8F0NXEIA(g3}yO`lH;-MqFl6_!}}CV z&5!+Qp&R*8`Y7AG;565Ki_Z_oGvszFtj{ezKZW(VCB#FOI$xWP`orx>Lz8;d&*Cwf zOdDYa!`7yUqqg_ox|Sc?o#Bj>lo2TXH-O``pswv5hD*hA1^2r+sem@f6la6(3JweN?IOB(q-| zHZ&@52t=Z-b-ucIZz2*6Cw;YD!34hNnhh!YXMdWKTu+DLejz!Yp>JzxxG!0~pHww~ z+8*PE`gmV56y}?Hw5C&oTCIKL;qUCVtv@atpMV_CaNVW^|R=I~h*x+-5%JoXvNoKut>kL}RQMY}njUn|jWBkdYp@yDmY`Ol*5 zR^a?+iSa~3w)1P6Dp@qj_0)ERr=2%0IzP(wkY4+UT&t%E=l5^%+jW;b*V0|f&$@_h|5F!FZ1T0oaWOo! zKDe%^qN&W8L_#sU2h?1Przc_ijG}Atm7z3S&r*x!@nLVhMvT>eBNZu_DTH9^I%J7`mXB4v9OqOCg|wKZJ+_y^>8hT&OA zIUf7$NG{|@&apq0PDWzuESm(^%0W7^)~M-vRx z&6anhUU|t4;{2T4cIYL!7mxiptb6hF_vhL|``fj6Y-=*EAC!K4?CL==-P9b=ELI_PNMg%3*7g=a|8z*J9FhxC-R z9qX&Em85FyXFF~r*tSKIHiyQhXH`fQwOhT9)C@?Iz@FNz(&U-}?-5I-io5W;vSy%S z2YAX!9a2s8g%hQLEuN~)7fr4{>7v1>W{jCA)ohg}Y{!Eb2-Dxu;gjPzy`yR&+>e`a zkY`@)1=1`z-k{X9)HC~SZ8~eVj@hQBbEzav8NdV4n>*eL3}AnoE2IGJm(sd&$Jjxn zYfANbNdHZPgVkS0COosQk@xlZ;NZ4HBxwem5N5)O9WVTAsWj%8(|vLpeDL$MOY-)U zww)*Wt1F~=v`@r00k$}TnBp7gN1W1RsdfPQ*`)ZN*fIDL^7Z?LmB=_K!nS|GKH(9<1kJ zy&mghu#RFq7wg__@N2KET3EZ)vjcXYSADZLxzsar8rgiQ_ygh05ftF)~k-?R78N@#K^5+9G+C`sAH- z-&B-UrCtOEP$sy}95=9~B6%15HdlBjp^U`PW~^Yp83SABx3^32^Q^|PnWPWzS^edO zD7R=|+9l7AP^PzUn~Joc{DUWNL)mQasG3W4V{^yYG`3gD{QhIpH#{xXUwDhOlh&V?-}QRYixSk1ycRyfw5_{ z7gYZzU3ar*G4|&f2zn-52e(-VN`o~QNaIfQNrMAH?DJ-6Z1tnCjc0~@+;3bcRgwD) zW&3f<2Y+z;fVXOZ@{jaSfZsN_U5WKY_unSBpPu_XQvZNQ8rXh7rX0)lVoT^{a(}CO zn)K@CaTRwwdduL0D+VMVc7?@PJFqXUcY8tF!{5AVO=~#V)joG=S5n$M&U?p`wS#ZL z2IH=tHkhDx{KSi(r=`FHK`8*cRHMw@E|-xywDC1#kFVM42}oWXa8v}b2nz@c2nz@c zl%oac19wMbp^iY2H*crV)b5jdyZd53sjGWk)F*B1jzxS@vO68}No%{4T|Oz^o!sD) zcEdg^gZ_A*&Ard+Sqa&G00|(~fS+1LF7}K3^(6ZsquzJHsL!Fu;LfywN1iuOQjo7M z@62?HxP=9V+5*%+7P8=^P$HH4;*sPz@0ONU)e>V`uFer?L4vFs9geMG2TSa67CNdpGO)+HaDz--Q~d}W&n%dm(`Y4L4QeC-Na~BlEi4$ zs&tiyyVpObJTfYBDJ&o?V6cE!+OxTJSuN(ABB5Y95|0@|rX^>b_e;e>l{a4t7S1o6H}|d3^Qi-Co%=7B!(S z=JCZm{;*Z55gJjs&*OV)N9chO$r2VA1`CL5_QPQ05fX*CW#7;^^x%c%u$?PIp zVSy200ddWKL>vZ@2w?$X0bv1Q0bv1Q0bv1Q0bv1Q0bv1Q0bzlnEg-Jh7j3#-ZbLDT zzsnsmR})}(gafVgJAiyg2Mk}LZ95{x$rHZAwQeiR%+kp*FaqAfu8 zx}U3lIN&_`jKFI8Gdq?p?L9k1Cuk>am}7$|>`)i)3a`ZH9V78P-*DPVNfyxx3k<0R zsBOTD(!4&bJ$UaLUOZOgv3O5iCBjW}5DqU{^B(3P9KFcxV;12kUFw1txOvaU53}@X z!yS%r^m5sJ;qan3??wmV@WMGSH4C&qEU-&9wk$~!UI^#K%j4v56Kulag>hcIT+TEc zMW8Ntp_~^lk;@zI1calHBFWJE>HVcIn6;hdLiXu4#J^b zt8~(?AJteu>Gra*#1>IY5&l!RXS|fDjl|K zl@8mrN{8)QrNefu(qX$+>7-qw7nwTg7jP^powR>A7L`ugKYC%RllG5Zs4Ch&R4j`n zUwfr)=5{!BzeB%njIE95``Y8sr();DAf6f*PmS-q(l;_H=*mab7OIS&Nf#@y%NC&06|)C+1%~Qiu3vt@viG_-5^B?FB`?gaw2Jgaw2J z>@2XezgcUSHu6*FHU=D+MIFHy8D*?5o(M>2)pj4by$o>ucL77p%_7taWBnbGz|xJQ1}KO>~A zDmBQj!+YsR^fN-s9X_k$^w}h*;g%xaPjP({&j@Ktmcnk|`<`!qX#XmGd^|8-tg(4U zQamHXhbyQ36VC{l*ago%j_7BE&|W!uHp%U9X#Xlj`^N>wWRgJt}q+ z*B%YmU&XaYCzVKq8Pyh`HnmdPU0N305cJ`Wg!N(b7j^E1a7}?g?JyXlBbrDIOFZi@ zZ>h_dpXQZ5GEKfdw1mi<6cRa}9{bXiue>>l#}{ZkGhbHn#glJ)_}45>M1~_CsV@}oa*&=< zgLH(FDf7m!)IICB@1URlP&AmLm&jQxE*u|u&;D~Fwr>G)Bc7F!*s=3`{nhg=I*@&k z61nV$EHcZ^NK4(Ex|uU+S7sq)PcsjVn-b5=nHnbpx)+aqhTMzC_7F-g#$y|iYw>i2 zhq{ka%3HVL(CZvr$j?N%yxCe!qT>??rb5A}Rl(M!%KV2CPtc~ta5J8et>s*>+kldv zP`6!8buXSopSe{PR~L?tlAmPW_5e*~Yu>S-U0P+kCBwGmfFcQ}e2u+{c#^Ii7rXeo z;`SLTcE{Q60)+l(q%-Seq^3wGXQqS-MDfHsdyIR}y0momxD(GX9G~W3sHM#}zk0bv zpL;N}HQLVC7Bw~JJ@3$z6&NRbYzv{qO@U#FXSmLze9IQm>z-|w=D=$jf&Ulkt zpKT*5-E(-)3-h#qnLes$tEppSyPi<)+m*3#$?@1{$i4j74~krj$2KC@;tBWL##4H+ z=5k;=@^Ymd6gXy`U%kG_%oR3fEj1p8W7gv9Phn%$lH+kYW-Z!oN`6w|bwwUt$D56| z`J2vYx9#|w3=?QrTzY@nmmZdQD80ir*2f&1eGf#bDQW3g5s!uK_CVJ>l7akVNxOYG z^s7489<#W;Nuc2wr$bQs5>HFJU$I8HA*p5Yp&59{@gJe4prZnChN>vORs$77!% z_wr*u40JIb+lX9?XQYj5i!TQ}u5~yLEIyvX#(^coQ|99)3(Mtn0wzB@3mZ2T9S=o4 z9OEWs3?YBQ!X&qnG#0L-Z45E2ePO5lN%x{)G#Zb`d`rW{e^Eu5w>Ld}8OPS}IT<#C zeEzh}a9zW8at>scheF*@_Pb}d_)tg92hay%hoi0RH=R*m$o#WnqCyRe@QWIoh9{oF z=Ix8lPl4wHMaNUt=L2LllOEIc^MR7%adZEhD__vbE~lC`ZSe<*?BL_S{$$8IPZ}2 z)3wR=?hM!R6Sv4HJG&7N+Q>jK+UvuO6Sb|)(QrCvNI4O4$z|WZ>&Uk0`n_c`;Cf$J zay<4K%Jdy2$793hQ%A zh=(e5zBV1@joXukCiSYH#bY#?Ho^>stxXR{ZSTKzEkCwBhg`1Rv0I+L!?paR`-gVq zg+iegRop(1(>p08W-?Zz?LN4I`do|qDqQb#OOD53pIdS~wq0Sik19PL+lVrKZmID& z?Q@Hdr?5V%_;?EIqe_h@8L~Y-fk?Eq&Q}-jO+=#Mq_4Ism`H2;DB0hvQ_{G}^>i5S z7n0){`nGl=WzF9iiD|NEK(#%_4fXN9WGKux^=M6J*e1upN5B1@y+eI`0&+aVbG1N&7*bYw`3CeR;#g z52heiSzgpS234vYuW8YQSc^>q>tu}QDg*dZ#+U)8tX>!3DU>zOlChm(H!y14h&ty}lqdM~d#6ng$A?`d^U_}!o9 zR<%m^Kj_(j;9Cl()|Yq75ft)5XKcXf`EJ&ae=l~I&!RlAp5Ozr_Y@DT#~a{{vYq9hobJ4 z?*7B>yWHVkddSApCobsuCnnxIs9f84+zjv8uX~^S3QhktlenG(f<2%XGCh60L7!0! znVvh{uL{BbRK?qX;GcI9vhjF>f1+8-UI`w=LHuv^lJDP5I;!do{x?Y>7*8SPH?KE7 z=m&+|jj_S|bo1*;40;!$%`3e;K^+%dhkQ3h{;2u~JPWDx1RoBCU_4R{RZUDvh#S!7 zH&a5$;vgxmcd!H(0zIhwMd9`)^Pmv@0HcBzxktO%+pGczVmW>$GhK?2Q9rQ zbb5mSjY#P93>Xyg#$61}S3iijcoeJ5XnYrpwQ#ZWzecdCtHA$*j7-Y#TNcjk@3<#X=RZ^!W| z?mBnJg3IrzkvwCCLAuDR28i!uRD4mgL3#;CmnI5_FJ%8x9Sw?O`JxO_Ci5-j4bmU- zb*&OnG3E!;JoEKW(4sPpQlsK(C(1*ft&-&OGxaN6xk89vZ>CM>mI$pW>l+ zm?y&t(hnX__x_C3%i{=waguv;^QRFf+Hdu;BI!l`Phl9Z_FKhDC8;g*G#u#h`mRP& zEc3INU+vGVrjgW+d49~7_UG%bMpAp`bvrozJoZi`;aAmMIMDuS=PpUFGj9kv+CS0b zB55dd3tZd(Qml(InV*LLHQzGJpdXk=zz>h&I4&tZmw(Qr5anQD%lWO7-Zjr5?tjSZ z@KUdJ%X66dRw(L>btL;Y>C%CYFt42D@G9%u(^2LVPZ8~V#NR(6)kkM_e=MR597{RN z^7!Z+^RY61m=~Vvqip6=;J_ZtTkf4k7nr*c+h?#aq@3-wd~}idxiG`lJ9UVUF6;h? zE3J3#cpqJ1z92yFj@drC#yq==qqoe;X>^_W&PYdZa*B^`Fdq)*m&38lKX8-XW@TXE z0m%Fl$ldTw3VO->6UcqYGdlkS@kZ^u zl+1g9+>K$uB<26guUPaE*1l|0|3cK5=Vi*X7JbZoV0Xxe;c~v(HnV6l^O3!=?jTt5 zQH3n}g!!tK24xKJ^9v0vn!^0p9HI(PEc;h!Ytg678^&T>uu1Y7^(>moJYq5Qviv!{ zESkprVf2NWs()B@i+s!<0WZnCSY?Z*GjE3Zt-!qfnHn^M`E>BI%y%`m=riVP!3Fc1 z4_h>o`4w<2|1kLH%pWO0RD$~-C}|Ovuxd}i9-U{7^tR{=<~@t3FK$)#?;(q3GcU1% zXbk(gR!fWKFrTpo^Tm8d5sT(BH=$SCe-U!#G5^Sed0~HUhM(s%pN3>o6vxufv9T5{ zU_Lhu@_F9#wzg;?^L;}cIgi!1=u75JptmNEw_taR7BOGEktj8mmq0RX8sW7cM0=VWet1YqY;NonQuk0QJMYFwT(r~m|uU&pkf>+E4o^= zocW$8%nOd?c!y$MzG8l?HS#?3M({IEsj8L6WMPU`{|(PrgfLdEU^Robv;2HDELz39 z?rx&(?B^DBELzR{HTb6y%Q=wGqOX~cI12eLzx*aWELy{SCHm|0&ZepseZzbcxQ^R8 z@WWc>XTi0dOR!$nG4GA@oy-GX)R{2XZu@H*)Q#;Q9}_|Ax&Ms-IY$e-Xgl+EFa}ej z{IKqjK|7eofonUfU~+ddZ{5{7Kf2+ZUCbBZyrb>hUf-nM%=cY!Q3U&K;b4=#V}9hi zi^7?^<4sCuez20WjuOV0w1@dzYulvNk1_EI$*r#VdsA4 zz0qHfw-Cm4fcbztZhO45I-8Wme9S3lyqjM%=^*n}0prc-Y7(YKwIm4FalX8|Nr#z_ z&g-_v`@wXRjxe8Gz!`7#aV8yQzOkU&9&biXla4XJ39iT6rl;xV^9zwQ2M(i!HB!1Z{Cy=>B1 z<{gVUXxff1$Ghevc(YvgQi^7;!#yZk=R*E+$5A(_Eo&H5i zA{Q~gST>Am!#p`JJElKJ2m{qBmkT5OQ~Zfm&(S*OU*T2PSMR?k=-pzoO9z^l$PBj*W9XI^BsQ;&_GBxn!wt%wa>kJbBFPzLkPe>LoS z%r{oh_sqvaueN{4JAyKqZ!Buq^;o}ig7z}cmm$wRe!rcA94t@OULFkj99L;y2*Pqu z&4ab3>$*=j3EIzmQemea8}^-`1I$z2G-w3ZV{s=1WiihWC%{xSUu!N2I>@|RC#O#H zz9Hxk^XAyx={jxDX+ejXhyUG)pUbZaI>Nlzy9QO_I&J>@f{rqOW`a|vU57o#n776T zMAs+l<_J2@{JrB&{ni0-c!K$?6Hfh>iTFRs{8j9UbsWYG5p;_AxSyT+q#4%ZY32*D z(a?2Wv&(|cFdz0gIM*Sw#|e`0E!#Huxe;FFy6cKKImf&;3UyuAeFMGcna_cfwV$Vr z6qL=pFzWv(mNV#ppbN~uLT7D%_GCd9neQ)d*!d?7{<*}w3krUnf9j4BbeZ{=c-8r* z?jAuXw^VCT->~yf*ik`OnJ>Y03BAADxKhwH=7(NDym9^+d_oY)Gu1BQ{HXKK(4m6l zx{~eci%$Njh4{>2ULqEDvOkyZ7KHLlwV~Kh==@U__W#7Z6kc`y8J;2tWt(cH5zBhL zCnJ7tGMDkI{g$&+&@JXQ1M*MNZO9<7LAIs=`KRwSK`8%J^MLF1+wX=Tlx?bw0@wMc zOr{{Yf0k_pxVEPo;xLT)JghIBf3_?agfdUH_24@HRQ^n;+E2bw97SwH&I9@3|A)AL zv4t)>|4ceBR81xO`<9@7=JhoI@r*K0wH*+y?VmkeP(J3hJ2`nH{6j%<|14Yk=1%^p z0ekW@AG6HKKVz;6Lb<5g>=iCM|6D>oL|LfXdFa*q!EO@;$$hwN1(Be1{&{I{#3bphuaHL!!{}GY4@cbE9lNELfd?qTqj& zi>iJ2s@vWt#`*-ITvTm-vfIu-k^A zP!P&a)w22+_Vw5gCJ4gvQLWHWx1E2g%@o@5uAs8a zHzHB!{Nvgz=yB%H2Kf2NdZE^!RNMkw=bwHv1eNFhrLc(fI% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mDNSWindows/Applications/SystemServiceTest/Tool.mcp b/mDNSWindows/Applications/SystemServiceTest/Tool.mcp deleted file mode 100644 index 59200136c9ec33e43aa7d23997e28360bd5da598..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 398728 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 -#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 deleted file mode 100644 index 3428a50..0000000 --- a/mDNSWindows/Applications/mdnsNSP/NSP.def +++ /dev/null @@ -1,36 +0,0 @@ -; -; 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 deleted file mode 100644 index 96007ece3f0de0b3628107a5859a41336156938b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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/CommonServices.h b/mDNSWindows/CommonServices.h index 2c00724..75eaa2d 100644 --- a/mDNSWindows/CommonServices.h +++ b/mDNSWindows/CommonServices.h @@ -3,8 +3,6 @@ * * @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/DLL.NET/AssemblyInfo.cpp b/mDNSWindows/DLL.NET/AssemblyInfo.cpp new file mode 100755 index 0000000..c32676c --- /dev/null +++ b/mDNSWindows/DLL.NET/AssemblyInfo.cpp @@ -0,0 +1,100 @@ +/* + * 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: AssemblyInfo.cpp,v $ +Revision 1.4 2004/07/26 21:03:00 shersche +enable strong naming for dnssd.NET assembly + +Revision 1.3 2004/07/14 19:54:57 shersche +use version file to get version numbers + +Revision 1.2 2004/07/14 15:42:47 shersche +remove entries for strong authentication + +Revision 1.1 2004/06/26 04:01:22 shersche +Initial revision + + + */ + +#include "stdafx.h" +#include "WinVersRes.h" + +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly:AssemblyTitleAttribute("dnssd.NET")]; +[assembly:AssemblyDescriptionAttribute(".NET wrapper for DNS-SD services")]; +[assembly:AssemblyConfigurationAttribute("")]; +[assembly:AssemblyCompanyAttribute("Apple Computer, Inc.")]; +[assembly:AssemblyProductAttribute("")]; +[assembly:AssemblyCopyrightAttribute("Apple Computer, Inc.")]; +[assembly:AssemblyTrademarkAttribute("")]; +[assembly:AssemblyCultureAttribute("")]; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the value or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly:AssemblyVersionAttribute(MASTER_PROD_VERS_STR2)]; + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly +// signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) +// utility +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project directory. +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly:AssemblyDelaySignAttribute(false)]; +[assembly:AssemblyKeyFileAttribute("dnssd_NET.snk")]; +[assembly:AssemblyKeyNameAttribute("")]; diff --git a/mDNSWindows/DLL.NET/PString.h b/mDNSWindows/DLL.NET/PString.h new file mode 100755 index 0000000..f64b951 --- /dev/null +++ b/mDNSWindows/DLL.NET/PString.h @@ -0,0 +1,86 @@ +/* + * 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: PString.h,v $ +Revision 1.2 2004/07/19 16:08:56 shersche +fix problems in UTF8/Unicode string translations + +Revision 1.1 2004/06/26 04:01:22 shersche +Initial revision + + + */ + +#pragma once + +using namespace System; +using namespace System::Text; + +namespace Apple +{ + __gc class PString + { + public: + + PString(String* string) + { + if (string != NULL) + { + Byte unicodeBytes[] = Encoding::Unicode->GetBytes(string); + Byte utf8Bytes[] = Encoding::Convert(Encoding::Unicode, Encoding::UTF8, unicodeBytes); + m_p = Marshal::AllocHGlobal(utf8Bytes->Length + 1); + Byte __pin * p = &utf8Bytes[0]; + char * hBytes = static_cast(m_p.ToPointer()); + memcpy(hBytes, p, utf8Bytes->Length); + hBytes[utf8Bytes->Length] = '\0'; + } + else + { + m_p = NULL; + } + } + + ~PString() + { + Marshal::FreeHGlobal(m_p); + } + + const char* + c_str() + { + if (m_p != NULL) + { + return static_cast(m_p.ToPointer()); + } + else + { + return NULL; + } + } + + protected: + + IntPtr m_p; + }; +} diff --git a/mDNSWindows/Applications/SystemService/Resource.h b/mDNSWindows/DLL.NET/Stdafx.cpp old mode 100644 new mode 100755 similarity index 76% rename from mDNSWindows/Applications/SystemService/Resource.h rename to mDNSWindows/DLL.NET/Stdafx.cpp index 50064bd..7014ab0 --- a/mDNSWindows/Applications/SystemService/Resource.h +++ b/mDNSWindows/DLL.NET/Stdafx.cpp @@ -1,33 +1,36 @@ -/* - * 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: Resource.h,v $ -Revision 1.1 2004/01/30 02:58:39 bradley -mDNSResponder Windows Service. Provides global Rendezvous support with an IPC interface. - -*/ - -#define IDS_SERVICE_DESCRIPTION 101 +/* + * 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: Stdafx.cpp,v $ +Revision 1.1 2004/06/26 04:01:22 shersche +Initial revision + + + */ + +// stdafx.cpp : source file that includes just the standard includes +// dotNET.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" diff --git a/mDNSWindows/DLL.NET/Stdafx.h b/mDNSWindows/DLL.NET/Stdafx.h new file mode 100755 index 0000000..397a0d1 --- /dev/null +++ b/mDNSWindows/DLL.NET/Stdafx.h @@ -0,0 +1,40 @@ +/* + * 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: Stdafx.h,v $ +Revision 1.1 2004/06/26 04:01:22 shersche +Initial revision + + + */ + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, +// but are changed infrequently + +#pragma once + +#using +#using + diff --git a/mDNSWindows/DLL.NET/dnssd_NET.cpp b/mDNSWindows/DLL.NET/dnssd_NET.cpp new file mode 100755 index 0000000..3aee79c --- /dev/null +++ b/mDNSWindows/DLL.NET/dnssd_NET.cpp @@ -0,0 +1,1276 @@ +/* + * 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_NET.cpp,v $ +Revision 1.9 2004/09/16 18:17:13 shersche +Use background threads, cleanup to parameter names. +Submitted by: prepin@gmail.com + +Revision 1.8 2004/09/13 19:35:58 shersche + Add Apple.DNSSD namespace to MC++ wrapper class + Change all instances of unsigned short to int +Bug #: 3798941, 3798950 + +Revision 1.7 2004/09/11 00:36:40 shersche + Modified .NET shim code to use host byte order for ports in APIs and callbacks +Bug #: 3786226 + +Revision 1.6 2004/09/02 21:20:56 cheshire + Rendezvous DLL.NET crashes on null record + +Revision 1.5 2004/07/27 07:12:56 shersche +make TextRecord an instantiable class object + +Revision 1.4 2004/07/26 06:19:05 shersche +Treat byte arrays of zero-length as null arrays + +Revision 1.3 2004/07/19 16:08:56 shersche +fix problems in UTF8/Unicode string translations + +Revision 1.2 2004/07/19 07:48:34 shersche +fix bug in DNSService.Register when passing in NULL text record, add TextRecord APIs + +Revision 1.1 2004/06/26 04:01:22 shersche +Initial revision + + + */ + +// This is the main DLL file. + +#include "stdafx.h" + +#include "dnssd_NET.h" +#include "DebugServices.h" +#include "PString.h" + + +using namespace System::Net::Sockets; +using namespace System::Diagnostics; +using namespace Apple; +using namespace Apple::DNSSD; + + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[dnssd.NET] " + +// +// ConvertToString +// +static String* +ConvertToString(const char * utf8String) +{ + return __gc new String(utf8String, 0, strlen(utf8String), __gc new UTF8Encoding(true, true)); +} + + +// +// class ServiceRef +// +// ServiceRef serves as the base class for all DNSService operations. +// +// It manages the DNSServiceRef, and implements processing the +// result +// +ServiceRef::ServiceRef(Object * callback) +: + m_bDisposed(false), + m_callback(callback), + m_thread(NULL) +{ + m_impl = new ServiceRefImpl(this); +} + + +ServiceRef::~ServiceRef() +{ +} + + +// +// StartThread +// +// Starts the main processing thread +// +void +ServiceRef::StartThread() +{ + check( m_impl != NULL ); + + m_impl->SetupEvents(); + + m_thread = new Thread(new ThreadStart(this, ProcessingThread)); + m_thread->Name = S"DNSService Thread"; + m_thread->IsBackground = true; + + m_thread->Start(); +} + + +// +// ProcessingThread +// +// The Thread class can only invoke methods in MC++ types. So we +// make a ProcessingThread method that forwards to the impl +// +void +ServiceRef::ProcessingThread() +{ + m_impl->ProcessingThread(); +} + + +// +// Dispose +// +// Calls impl-Dispose(). This ultimately will call DNSServiceRefDeallocate() +// +void +ServiceRef::Dispose() +{ + check(m_impl != NULL); + check(m_bDisposed == false); + + if (!m_bDisposed) + { + m_bDisposed = true; + + // + // Call Dispose. This won't call DNSServiceRefDeallocate() + // necessarily. It depends on what thread this is being + // called in. + // + m_impl->Dispose(); + m_impl = NULL; + + m_thread = NULL; + + GC::SuppressFinalize(this); + } +} + + +// +// EnumerateDomainsDispatch +// +// Dispatch a reply to the delegate. +// +void +ServiceRef::EnumerateDomainsDispatch + ( + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * replyDomain + ) +{ + if ((m_callback != NULL) && (m_impl != NULL)) + { + DNSService::EnumerateDomainsReply * OnEnumerateDomainsReply = static_cast(m_callback); + OnEnumerateDomainsReply(this, flags, interfaceIndex, errorCode, replyDomain); + } +} + + +// +// RegisterDispatch +// +// Dispatch a reply to the delegate. +// +void +ServiceRef::RegisterDispatch + ( + ServiceFlags flags, + ErrorCode errorCode, + String * name, + String * regtype, + String * domain + ) +{ + if ((m_callback != NULL) && (m_impl != NULL)) + { + DNSService::RegisterReply * OnRegisterReply = static_cast(m_callback); + OnRegisterReply(this, flags, errorCode, name, regtype, domain); + } +} + + +// +// BrowseDispatch +// +// Dispatch a reply to the delegate. +// +void +ServiceRef::BrowseDispatch + ( + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * serviceName, + String * regtype, + String * replyDomain + ) +{ + if ((m_callback != NULL) && (m_impl != NULL)) + { + DNSService::BrowseReply * OnBrowseReply = static_cast(m_callback); + OnBrowseReply(this, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain); + } +} + + +// +// ResolveDispatch +// +// Dispatch a reply to the delegate. +// +void +ServiceRef::ResolveDispatch + ( + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * fullname, + String * hosttarget, + int port, + Byte txtRecord[] + ) +{ + if ((m_callback != NULL) && (m_impl != NULL)) + { + DNSService::ResolveReply * OnResolveReply = static_cast(m_callback); + OnResolveReply(this, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtRecord); + } +} + + +// +// RegisterRecordDispatch +// +// Dispatch a reply to the delegate. +// +void +ServiceRef::RegisterRecordDispatch + ( + ServiceFlags flags, + ErrorCode errorCode, + RecordRef * record + ) +{ + if ((m_callback != NULL) && (m_impl != NULL)) + { + DNSService::RegisterRecordReply * OnRegisterRecordReply = static_cast(m_callback); + OnRegisterRecordReply(this, flags, errorCode, record); + } +} + + +// +// QueryRecordDispatch +// +// Dispatch a reply to the delegate. +// +void +ServiceRef::QueryRecordDispatch + ( + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * fullname, + int rrtype, + int rrclass, + Byte rdata[], + int ttl + ) +{ + if ((m_callback != NULL) && (m_impl != NULL)) + { + DNSService::QueryRecordReply * OnQueryRecordReply = static_cast(m_callback); + OnQueryRecordReply(this, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdata, ttl); + } +} + + +// +// ServiceRefImpl::ServiceRefImpl() +// +// Constructs a new ServiceRefImpl. We save the pointer to our enclosing +// class in a gcroot handle. This satisfies the garbage collector as +// the outer class is a managed type +// +ServiceRef::ServiceRefImpl::ServiceRefImpl(ServiceRef * outer) +: + m_socketEvent(NULL), + m_stopEvent(NULL), + m_disposed(false), + m_outer(outer), + m_ref(NULL) +{ + m_threadId = GetCurrentThreadId(); +} + + +// +// ServiceRefImpl::~ServiceRefImpl() +// +// Deallocate all resources associated with the ServiceRefImpl +// +ServiceRef::ServiceRefImpl::~ServiceRefImpl() +{ + if (m_socketEvent != NULL) + { + CloseHandle(m_socketEvent); + m_socketEvent = NULL; + } + + if (m_stopEvent != NULL) + { + CloseHandle(m_stopEvent); + m_stopEvent = NULL; + } + + if (m_ref != NULL) + { + DNSServiceRefDeallocate(m_ref); + m_ref = NULL; + } +} + + +// +// ServiceRefImpl::SetupEvents() +// +// Setup the events necessary to manage multi-threaded dispatch +// of DNSService Events +// +void +ServiceRef::ServiceRefImpl::SetupEvents() +{ + check(m_ref != NULL); + + m_socket = (SOCKET) DNSServiceRefSockFD(m_ref); + check(m_socket != INVALID_SOCKET); + + m_socketEvent = CreateEvent(NULL, 0, 0, NULL); + + if (m_socketEvent == NULL) + { + throw new DNSServiceException(Unknown); + } + + int err = WSAEventSelect(m_socket, m_socketEvent, FD_READ|FD_CLOSE); + + if (err != 0) + { + throw new DNSServiceException(Unknown); + } + + m_stopEvent = CreateEvent(NULL, 0, 0, NULL); + + if (m_stopEvent == NULL) + { + throw new DNSServiceException(Unknown); + } +} + + +// +// ServiceRefImpl::ProcessingThread() +// +// Wait for socket events on the DNSServiceRefSockFD(). Also wait +// for stop events +// +void +ServiceRef::ServiceRefImpl::ProcessingThread() +{ + check( m_socketEvent != NULL ); + check( m_stopEvent != NULL ); + check( m_ref != NULL ); + + HANDLE handles[2]; + + handles[0] = m_socketEvent; + handles[1] = m_stopEvent; + + while (m_disposed == false) + { + int ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + + // + // it's a socket event + // + if (ret == WAIT_OBJECT_0) + { + DNSServiceProcessResult(m_ref); + } + // + // else it's a stop event + // + else if (ret == WAIT_OBJECT_0 + 1) + { + break; + } + else + { + // + // unexpected wait result + // + dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, ret ); + } + } + + delete this; +} + + +// +// ServiceRefImpl::Dispose() +// +// Calls DNSServiceRefDeallocate() +// +void +ServiceRef::ServiceRefImpl::Dispose() +{ + OSStatus err; + BOOL ok; + + check(m_disposed == false); + + m_disposed = true; + + ok = SetEvent(m_stopEvent); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + +exit: + + return; +} + + +// +// ServiceRefImpl::EnumerateDomainsCallback() +// +// This is the callback from dnssd.dll. We pass this up to our outer, managed type +// +void DNSSD_API +ServiceRef::ServiceRefImpl::EnumerateDomainsCallback + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char * replyDomain, + void * context + ) +{ + ServiceRef::ServiceRefImpl * self = static_cast(context); + + check( self != NULL ); + check( self->m_outer != NULL ); + + if (self->m_disposed == false) + { + self->m_outer->EnumerateDomainsDispatch((ServiceFlags) flags, interfaceIndex, (ErrorCode) errorCode, ConvertToString(replyDomain)); + } +} + + +// +// ServiceRefImpl::RegisterCallback() +// +// This is the callback from dnssd.dll. We pass this up to our outer, managed type +// +void DNSSD_API +ServiceRef::ServiceRefImpl::RegisterCallback + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char * name, + const char * regtype, + const char * domain, + void * context + ) +{ + ServiceRef::ServiceRefImpl * self = static_cast(context); + + check( self != NULL ); + check( self->m_outer != NULL ); + + if (self->m_disposed == false) + { + self->m_outer->RegisterDispatch((ServiceFlags) flags, (ErrorCode) errorCode, ConvertToString(name), ConvertToString(regtype), ConvertToString(domain)); + } +} + + +// +// ServiceRefImpl::BrowseCallback() +// +// This is the callback from dnssd.dll. We pass this up to our outer, managed type +// +void DNSSD_API +ServiceRef::ServiceRefImpl::BrowseCallback + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char * serviceName, + const char * regtype, + const char * replyDomain, + void * context + ) +{ + ServiceRef::ServiceRefImpl * self = static_cast(context); + + check( self != NULL ); + check( self->m_outer != NULL ); + + if (self->m_disposed == false) + { + self->m_outer->BrowseDispatch((ServiceFlags) flags, interfaceIndex, (ErrorCode) errorCode, ConvertToString(serviceName), ConvertToString(regtype), ConvertToString(replyDomain)); + } +} + + +// +// ServiceRefImpl::ResolveCallback() +// +// This is the callback from dnssd.dll. We pass this up to our outer, managed type +// +void DNSSD_API +ServiceRef::ServiceRefImpl::ResolveCallback + ( + 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 + ) +{ + ServiceRef::ServiceRefImpl * self = static_cast(context); + + check( self != NULL ); + check( self->m_outer != NULL ); + + if (self->m_disposed == false) + { + Byte txtRecordBytes[]; + + txtRecordBytes = NULL; + + if (txtLen > 0) + { + // + // copy raw memory into managed byte array + // + txtRecordBytes = new Byte[txtLen]; + Byte __pin * p = &txtRecordBytes[0]; + memcpy(p, txtRecord, txtLen); + } + + self->m_outer->ResolveDispatch((ServiceFlags) flags, interfaceIndex, (ErrorCode) errorCode, ConvertToString(fullname), ConvertToString(hosttarget), ntohs(notAnIntPort), txtRecordBytes); + } +} + + +// +// ServiceRefImpl::RegisterRecordCallback() +// +// This is the callback from dnssd.dll. We pass this up to our outer, managed type +// +void DNSSD_API +ServiceRef::ServiceRefImpl::RegisterRecordCallback + ( + DNSServiceRef sdRef, + DNSRecordRef rrRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + void * context + ) +{ + ServiceRef::ServiceRefImpl * self = static_cast(context); + + check( self != NULL ); + check( self->m_outer != NULL ); + + if (self->m_disposed == false) + { + RecordRef * record = NULL; + + if (errorCode == 0) + { + record = new RecordRef; + + record->m_impl->m_ref = rrRef; + } + + self->m_outer->RegisterRecordDispatch((ServiceFlags) flags, (ErrorCode) errorCode, record); + } +} + + +// +// ServiceRefImpl::QueryRecordCallback() +// +// This is the callback from dnssd.dll. We pass this up to our outer, managed type +// +void DNSSD_API +ServiceRef::ServiceRefImpl::QueryRecordCallback + ( + 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 + ) +{ + ServiceRef::ServiceRefImpl * self = static_cast(context); + + check( self != NULL ); + check( self->m_outer != NULL ); + + if (self->m_disposed == false) + { + Byte rdataBytes[]; + + if (rdlen) + { + rdataBytes = new Byte[rdlen]; + Byte __pin * p = &rdataBytes[0]; + memcpy(p, rdata, rdlen); + } + + self->m_outer->QueryRecordDispatch((ServiceFlags) flags, (int) interfaceIndex, (ErrorCode) errorCode, ConvertToString(fullname), rrtype, rrclass, rdataBytes, ttl); + } +} + + +/* + * EnumerateDomains() + * + * This maps to DNSServiceEnumerateDomains(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +ServiceRef* +DNSService::EnumerateDomains + ( + int flags, + int interfaceIndex, + EnumerateDomainsReply * callback + ) +{ + ServiceRef * sdRef = new ServiceRef(callback); + int err; + + err = DNSServiceEnumerateDomains(&sdRef->m_impl->m_ref, flags, interfaceIndex, ServiceRef::ServiceRefImpl::EnumerateDomainsCallback, sdRef->m_impl); + + if (err != 0) + { + throw new DNSServiceException(err); + } + + sdRef->StartThread(); + + return sdRef; +} + + +/* + * Register() + * + * This maps to DNSServiceRegister(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +ServiceRef* +DNSService::Register + ( + int flags, + int interfaceIndex, + String * name, + String * regtype, + String * domain, + String * host, + int port, + Byte txtRecord[], + RegisterReply * callback + ) +{ + ServiceRef * sdRef = new ServiceRef(callback); + PString * pName = new PString(name); + PString * pType = new PString(regtype); + PString * pDomain = new PString(domain); + PString * pHost = new PString(host); + int len = 0; + Byte __pin * p = NULL; + void * v = NULL; + + if ((txtRecord != NULL) && (txtRecord->Length > 0)) + { + len = txtRecord->Length; + p = &txtRecord[0]; + v = (void*) p; + } + + int err = DNSServiceRegister(&sdRef->m_impl->m_ref, flags, interfaceIndex, pName->c_str(), pType->c_str(), pDomain->c_str(), pHost->c_str(), htons(port), len, v, ServiceRef::ServiceRefImpl::RegisterCallback, sdRef->m_impl ); + + if (err != 0) + { + throw new DNSServiceException(err); + } + + sdRef->StartThread(); + + return sdRef; +} + + +/* + * AddRecord() + * + * This maps to DNSServiceAddRecord(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +RecordRef* +DNSService::AddRecord + ( + ServiceRef * sdRef, + int flags, + int rrtype, + Byte rdata[], + int ttl + ) +{ + int len = 0; + Byte __pin * p = NULL; + void * v = NULL; + + if ((rdata != NULL) && (rdata->Length > 0)) + { + len = rdata->Length; + p = &rdata[0]; + v = (void*) p; + } + + RecordRef * record = new RecordRef; + + int err = DNSServiceAddRecord(sdRef->m_impl->m_ref, &record->m_impl->m_ref, flags, rrtype, len, v, ttl); + + if (err != 0) + { + throw new DNSServiceException(err); + } + + return record; +} + + +/* + * UpdateRecord() + * + * This maps to DNSServiceUpdateRecord(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +void +DNSService::UpdateRecord + ( + ServiceRef * sdRef, + RecordRef * record, + int flags, + Byte rdata[], + int ttl + ) +{ + int len = 0; + Byte __pin * p = NULL; + void * v = NULL; + + if ((rdata != NULL) && (rdata->Length > 0)) + { + len = rdata->Length; + p = &rdata[0]; + v = (void*) p; + } + + int err = DNSServiceUpdateRecord(sdRef->m_impl->m_ref, record ? record->m_impl->m_ref : NULL, flags, len, v, ttl); + + if (err != 0) + { + throw new DNSServiceException(err); + } +} + + +/* + * RemoveRecord() + * + * This maps to DNSServiceRemoveRecord(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +void +DNSService::RemoveRecord + ( + ServiceRef * sdRef, + RecordRef * record, + int flags + ) +{ + int err = DNSServiceRemoveRecord(sdRef->m_impl->m_ref, record->m_impl->m_ref, flags); + + if (err != 0) + { + throw new DNSServiceException(err); + } +} + + +/* + * Browse() + * + * This maps to DNSServiceBrowse(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +ServiceRef* +DNSService::Browse + ( + int flags, + int interfaceIndex, + String * regtype, + String * domain, + BrowseReply * callback + ) +{ + ServiceRef * sdRef = new ServiceRef(callback); + PString * pType = new PString(regtype); + PString * pDomain = new PString(domain); + + int err = DNSServiceBrowse(&sdRef->m_impl->m_ref, flags, interfaceIndex, pType->c_str(), pDomain->c_str(),(DNSServiceBrowseReply) ServiceRef::ServiceRefImpl::BrowseCallback, sdRef->m_impl); + + if (err != 0) + { + throw new DNSServiceException(err); + } + + sdRef->StartThread(); + + return sdRef; +} + + +/* + * Resolve() + * + * This maps to DNSServiceResolve(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +ServiceRef* +DNSService::Resolve + ( + int flags, + int interfaceIndex, + String * name, + String * regtype, + String * domain, + ResolveReply * callback + ) +{ + ServiceRef * sdRef = new ServiceRef(callback); + PString * pName = new PString(name); + PString * pType = new PString(regtype); + PString * pDomain = new PString(domain); + + int err = DNSServiceResolve(&sdRef->m_impl->m_ref, flags, interfaceIndex, pName->c_str(), pType->c_str(), pDomain->c_str(),(DNSServiceResolveReply) ServiceRef::ServiceRefImpl::ResolveCallback, sdRef->m_impl); + + if (err != 0) + { + throw new DNSServiceException(err); + } + + sdRef->StartThread(); + + return sdRef; +} + + +/* + * CreateConnection() + * + * This maps to DNSServiceCreateConnection(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +ServiceRef* +DNSService::CreateConnection + ( + RegisterRecordReply * callback + ) +{ + ServiceRef * sdRef = new ServiceRef(callback); + + int err = DNSServiceCreateConnection(&sdRef->m_impl->m_ref); + + if (err != 0) + { + throw new DNSServiceException(err); + } + + sdRef->StartThread(); + + return sdRef; +} + + +/* + * RegisterRecord() + * + * This maps to DNSServiceRegisterRecord(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ + +RecordRef* +DNSService::RegisterRecord + ( + ServiceRef * sdRef, + ServiceFlags flags, + int interfaceIndex, + String * fullname, + int rrtype, + int rrclass, + Byte rdata[], + int ttl + ) +{ + RecordRef * record = new RecordRef; + int len = 0; + Byte __pin * p = NULL; + void * v = NULL; + + PString * pFullname = new PString(fullname); + + if ((rdata != NULL) && (rdata->Length > 0)) + { + len = rdata->Length; + p = &rdata[0]; + v = (void*) p; + } + + int err = DNSServiceRegisterRecord(sdRef->m_impl->m_ref, &record->m_impl->m_ref, flags, interfaceIndex, pFullname->c_str(), rrtype, rrclass, len, v, ttl, (DNSServiceRegisterRecordReply) ServiceRef::ServiceRefImpl::RegisterRecordCallback, sdRef->m_impl); + + if (err != 0) + { + throw new DNSServiceException(err); + } + + return record; +} + +/* + * QueryRecord() + * + * This maps to DNSServiceQueryRecord(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +ServiceRef* +DNSService::QueryRecord + ( + ServiceFlags flags, + int interfaceIndex, + String * fullname, + int rrtype, + int rrclass, + QueryRecordReply * callback + ) +{ + ServiceRef * sdRef = new ServiceRef(callback); + PString * pFullname = new PString(fullname); + + int err = DNSServiceQueryRecord(&sdRef->m_impl->m_ref, flags, interfaceIndex, pFullname->c_str(), rrtype, rrclass, (DNSServiceQueryRecordReply) ServiceRef::ServiceRefImpl::QueryRecordCallback, sdRef->m_impl); + + if (err != 0) + { + throw new DNSServiceException(err); + } + + sdRef->StartThread(); + + return sdRef; +} + + +/* + * ReconfirmRecord() + * + * This maps to DNSServiceReconfirmRecord(). Returns an + * initialized ServiceRef on success, throws an exception + * on failure. + */ +void +DNSService::ReconfirmRecord + ( + ServiceFlags flags, + int interfaceIndex, + String * fullname, + int rrtype, + int rrclass, + Byte rdata[] + ) +{ + int len = 0; + Byte __pin * p = NULL; + void * v = NULL; + + PString * pFullname = new PString(fullname); + + if ((rdata != NULL) && (rdata->Length > 0)) + { + len = rdata->Length; + p = &rdata[0]; + v = (void*) p; + } + + DNSServiceReconfirmRecord(flags, interfaceIndex, pFullname->c_str(), rrtype, rrclass, len, v); +} + + +void +TextRecord::SetValue + ( + String * key, + Byte value[] /* may be NULL */ + ) +{ + PString * pKey = new PString(key); + int len = 0; + Byte __pin * p = NULL; + void * v = NULL; + DNSServiceErrorType err; + + if (value && (value->Length > 0)) + { + len = value->Length; + p = &value[0]; + v = (void*) p; + } + + err = TXTRecordSetValue(&m_impl->m_ref, pKey->c_str(), len, v); + + if (err != 0) + { + throw new DNSServiceException(err); + } +} + + +void +TextRecord::RemoveValue + ( + String * key + ) +{ + PString * pKey = new PString(key); + DNSServiceErrorType err; + + err = TXTRecordRemoveValue(&m_impl->m_ref, pKey->c_str()); + + if (err != 0) + { + throw new DNSServiceException(err); + } +} + + +int +TextRecord::GetLength + ( + ) +{ + return TXTRecordGetLength(&m_impl->m_ref); +} + + +Byte +TextRecord::GetBytes + ( + ) __gc[] +{ + const void * noGCBytes = NULL; + Byte gcBytes[] = NULL; + + noGCBytes = TXTRecordGetBytesPtr(&m_impl->m_ref); + int len = GetLength(); + + if (noGCBytes && len) + { + gcBytes = new Byte[len]; + Byte __pin * p = &gcBytes[0]; + memcpy(p, noGCBytes, len); + } + + return gcBytes; +} + + +bool +TextRecord::ContainsKey + ( + Byte txtRecord[], + String * key + ) +{ + PString * pKey = new PString(key); + Byte __pin * p = &txtRecord[0]; + + return (TXTRecordContainsKey(txtRecord->Length, p, pKey->c_str()) > 0) ? true : false; +} + + +Byte +TextRecord::GetValueBytes + ( + Byte txtRecord[], + String * key + ) __gc[] +{ + uint8_t valueLen; + Byte ret[] = NULL; + PString * pKey = new PString(key); + Byte __pin * p1 = &txtRecord[0]; + const void * v; + + v = TXTRecordGetValuePtr(txtRecord->Length, p1, pKey->c_str(), &valueLen); + + if (v != NULL) + { + ret = new Byte[valueLen]; + Byte __pin * p2 = &ret[0]; + + memcpy(p2, v, valueLen); + } + + return ret; +} + + +int +TextRecord::GetCount + ( + Byte txtRecord[] + ) +{ + Byte __pin * p = &txtRecord[0]; + + return TXTRecordGetCount(txtRecord->Length, p); +} + + +Byte +TextRecord::GetItemAtIndex + ( + Byte txtRecord[], + int index, + [Out] String ** key + ) __gc[] +{ + char keyBuf[255]; + uint8_t keyBufLen = 255; + uint8_t valueLen; + void * value; + Byte ret[] = NULL; + DNSServiceErrorType err; + Byte __pin * p1 = &txtRecord[0]; + + + err = TXTRecordGetItemAtIndex(txtRecord->Length, p1, index, keyBufLen, keyBuf, &valueLen, (const void**) &value); + + if (err != 0) + { + throw new DNSServiceException(err); + } + + *key = ConvertToString(keyBuf); + + if (valueLen) + { + ret = new Byte[valueLen]; + Byte __pin * p2 = &ret[0]; + + memcpy(p2, value, valueLen); + } + + return ret; +} + + +// +// DNSServiceException::DNSServiceException() +// +// Constructs an exception with an error code +// +DNSServiceException::DNSServiceException + ( + int _err + ) +: + err(_err) +{ +} + + +// +// This version of the constructor is useful for instances in which +// an inner exception is thrown, caught, and then a new exception +// is thrown in it's place +// +DNSServiceException::DNSServiceException + ( + String * message, + System::Exception * innerException + ) +{ +} diff --git a/mDNSWindows/DLL.NET/dnssd_NET.h b/mDNSWindows/DLL.NET/dnssd_NET.h new file mode 100755 index 0000000..565286f --- /dev/null +++ b/mDNSWindows/DLL.NET/dnssd_NET.h @@ -0,0 +1,1424 @@ +/* + * 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@ + + * + * NOTE: + * + * These .Net APIs are a work in progress, currently being discussed and refined. + * If you plan to build an application based on these APIs, you may wish to + * statically link this code into your application, or otherwise distribute + * the DLL so that it installs into the same folder as your application + * (not into any common system area where it might interfere with other + * applications using a future completed version of these APIs). + * If you plan to do this, please be sure to inform us by sending email + * to rendezvous@apple.com to let us know. + * You may want to discuss what you're doing on the Rendezvous mailing + * list to see if others in similar positions have any suggestions for you: + * + * + * + + Change History (most recent first): + +$Log: dnssd_NET.h,v $ +Revision 1.6 2004/09/20 22:47:06 cheshire +Add cautionary comment + +Revision 1.5 2004/09/16 18:16:27 shersche +Cleanup to parameter names +Submitted by: prepin@gmail.com + +Revision 1.4 2004/09/13 19:35:57 shersche + Add Apple.DNSSD namespace to MC++ wrapper class + Change all instances of unsigned short to int +Bug #: 3798941, 3798950 + +Revision 1.3 2004/07/27 07:12:10 shersche +make TextRecord an instantiable class object + +Revision 1.2 2004/07/19 07:48:34 shersche +fix bug in DNSService.Register when passing in NULL text record, add TextRecord APIs + +Revision 1.1 2004/06/26 04:01:22 shersche +Initial revision + + + */ + +#pragma once + +#include +#include +#include +#include + +using namespace System; +using namespace System::Net; +using namespace System::Runtime::InteropServices; +using namespace System::Threading; +using namespace System::Collections; + + +namespace Apple +{ + namespace DNSSD + { + public __gc class ServiceRef; + + public __value enum ServiceFlags : int + { + MoreComing = 1, + /* 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, + * and then update their UI. + * When MoreComing is not set, 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. + */ + + Add = 2, + Default = 4, + /* Flags for domain enumeration and browse/query reply callbacks. + * "Default" applies only to enumeration and is only valid in + * conjuction with "Add". An enumeration callback with the "Add" + * flag NOT set indicates a "Remove", i.e. the domain is no longer + * valid. + */ + + NoAutoRename = 8, + /* Flag for specifying renaming behavior on name conflict when registering + * non-shared records. By default, name conflicts are automatically handled + * 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 + * (ie the default name is not used.) + */ + + Shared = 16, + Unique = 32, + /* Flag for registering individual records on a connected + * DNSServiceRef. Shared indicates that there may be multiple records + * with this name on the network (e.g. PTR records). Unique indicates that + the + * record's name is to be unique on the network (e.g. SRV records). + */ + + BrowseDomains = 64, + RegistrationDomains = 128, + /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomain + s. + * BrowseDomains enumerates domains recommended for browsing, RegistrationDo + mains + * enumerates domains recommended for registration. + */ + }; + + + public __value enum ErrorCode : int + { + NoError = 0, + Unknown = -65537, + NoSuchName = -65538, + NoMemory = -65539, + BadParam = -65540, + BadReference = -65541, + BadState = -65542, + BadFlags = -65543, + Unsupported = -65544, + AlreadyRegistered = -65547, + NameConflict = -65548, + Invalid = -65549, + Incompatible = -65551, + BadinterfaceIndex = -65552 + + /* + * mDNS Error codes are in the range + * FFFE FF00 (-65792) to FFFE FFFF (-65537) + */ + }; + + public __gc class DNSServiceException + : + public Exception + { + public: + + DNSServiceException + ( + int err + ); + + DNSServiceException + ( + String * message, + System::Exception * innerException + ); + + int err; + }; + + + /* + * class RecordRef + * + * This is a thin MC++ class facade on top of a DNSRecordRef + */ + public __gc class RecordRef + { + public: + + RecordRef() + { + m_impl = new RecordRefImpl; + m_impl->m_ref = NULL; + } + + ~RecordRef() + { + delete m_impl; + } + + __nogc class RecordRefImpl + { + public: + + DNSRecordRef m_ref; + }; + + RecordRefImpl * m_impl; + }; + + + /* + * class ServiceRef + * + * This is a thin MC++ class facade on top of a DNSServiceRef + */ + public __gc class ServiceRef : public IDisposable + { + public: + + ServiceRef(Object * callback); + + ~ServiceRef(); + + /* + * This does an underlying DNSServiceRefDeallocate(). After + * calling Dispose, the ServiceRef is no longer usable. + */ + void + Dispose(); + + /* + * Internal - Dispatch an EnumerateDomains callback + */ + void + EnumerateDomainsDispatch + ( + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * replyDomain + ); + + /* + * Internal - Dispatch a Register callback + */ + void + RegisterDispatch + ( + ServiceFlags flags, + ErrorCode errorCode, + String * name, + String * regtype, + String * domain + ); + + /* + * Internal - Dispatch a Browse callback + */ + void + BrowseDispatch + ( + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * serviceName, + String * regtype, + String * replyDomain + ); + + /* + * Internal - Dispatch a Resolve callback + */ + void + ResolveDispatch + ( + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * fullname, + String * hosttarget, + int port, + Byte txtRecord[] + ); + + /* + * Internal - Dispatch a RegisterRecord callback + */ + void + RegisterRecordDispatch + ( + ServiceFlags flags, + ErrorCode errorCode, + RecordRef * record + ); + + /* + * Internal - Dispatch a QueryRecord callback + */ + void + QueryRecordDispatch + ( + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * fullname, + int rrtype, + int rrclass, + Byte rdata[], + int ttl + ); + + /* + * Internal - A non managed class to wrap a DNSServiceRef + */ + __nogc class ServiceRefImpl + { + public: + + ServiceRefImpl + ( + ServiceRef * outer + ); + + ~ServiceRefImpl(); + + /* + * Sets up events for threaded operation + */ + void + SetupEvents(); + + /* + * Main processing thread + */ + void + ProcessingThread(); + + /* + * Calls DNSServiceRefDeallocate() + */ + void + Dispose(); + + /* + * Called from dnssd.dll + */ + static void DNSSD_API + EnumerateDomainsCallback + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char * replyDomain, + void * context + ); + + static void DNSSD_API + RegisterCallback + ( + DNSServiceRef ref, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char * name, + const char * regtype, + const char * domain, + void * context + ); + + static void DNSSD_API + BrowseCallback + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char * serviceName, + const char * regtype, + const char * replyDomain, + void * context + ); + + static void DNSSD_API + ResolveCallback + ( + 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 + ); + + static void DNSSD_API + RegisterRecordCallback + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + void * context + ); + + static void DNSSD_API + QueryRecordCallback + ( + 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 + ); + + SOCKET m_socket; + HANDLE m_socketEvent; + HANDLE m_stopEvent; + DWORD m_threadId; + bool m_disposed; + DNSServiceRef m_ref; + gcroot m_outer; + }; + + void + StartThread(); + + void + ProcessingThread(); + + bool m_bDisposed; + Object * m_callback; + Thread * m_thread; + ServiceRefImpl * m_impl; + }; + + /********************************************************************************************* + * + * TXT Record Construction Functions + * + *********************************************************************************************/ + + /* + * A typical calling sequence for TXT record construction is something like: + * + * DNSService.TextRecord tr = new DNSService.TextRecord(1024); + * tr.SetValue(); + * tr.SetValue(); + * tr.SetValue(); + * ... + * DNSServiceRegister( ... tr.GetLength(), tr.GetBytes() ... ); + */ + + + /* TextRecord + * + * Opaque internal data type. + * Note: Represents a DNS-SD TXT record. + */ + + + /* TextRecord::TextRecord() + * + * Creates a new empty TextRecord . + * + */ + + public __gc class TextRecord + { + public: + + TextRecord() + { + m_impl = new TextRecordImpl(); + TXTRecordCreate(&m_impl->m_ref, 0, NULL); + } + + ~TextRecord() + { + TXTRecordDeallocate(&m_impl->m_ref); + delete m_impl; + } + + __nogc class TextRecordImpl + { + public: + + TXTRecordRef m_ref; + }; + + TextRecordImpl * m_impl; + + + /* SetValue() + * + * Adds a key (optionally with value) to a TextRecord. If the "key" already + * exists in the TextRecord, then the current value will be replaced with + * the new value. + * Keys may exist 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 TXT record) + * - Present with non-empty value ("key=value" appears in TXT record) + * For more details refer to "Data Syntax for DNS-SD TXT Records" in + * + * + * key: A null-terminated string which only contains printable ASCII + * values (0x20-0x7E), excluding '=' (0x3D). Keys should be + * 14 characters or less (not counting the terminating null). + * + * value: Any binary value. For values that represent + * textual data, UTF-8 is STRONGLY recommended. + * For values that represent textual data, valueSize + * should NOT include the terminating null (if any) + * at the end of the string. + * If NULL, then "key" will be added with no value. + * If non-NULL but valueSize is zero, then "key=" will be + * added with empty value. + * + * exceptions: Throws kDNSServiceErr_Invalid if the "key" string contains + * illegal characters. + * Throws kDNSServiceErr_NoMemory if adding this key would + * exceed the available storage. + */ + + void + SetValue + ( + String * key, + Byte value[] /* may be NULL */ + ); + + + /* RemoveValue() + * + * Removes a key from a TextRecord. The "key" must be an + * ASCII string which exists in the TextRecord. + * + * key: A key name which exists in the TextRecord. + * + * exceptions: Throws kDNSServiceErr_NoSuchKey if the "key" does not + * exist in the TextRecord. + * + */ + + void + RemoveValue + ( + String * key + ); + + + /* GetLength() + * + * Allows you to determine the length of the raw bytes within a TextRecord. + * + * return value : Returns the size of the raw bytes inside a TextRecord + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + * Returns 0 if the TextRecord is empty. + * + */ + + int + GetLength + ( + ); + + + /* GetBytes() + * + * Allows you to retrieve a pointer to the raw bytes within a TextRecord. + * + * return value: Returns a pointer to the bytes inside the TextRecord + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + * + */ + + Byte + GetBytes + ( + ) __gc[]; + + + /********************************************************************************************* + * + * TXT Record Parsing Functions + * + *********************************************************************************************/ + + /* + * A typical calling sequence for TXT record parsing is something like: + * + * Receive TXT record data in DNSServiceResolve() callback + * if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something + * val1ptr = DNSService.TextService.GetValue(txtRecord, "key1", &len1); + * val2ptr = DNSService.TextService.GetValue(txtRecord, "key2", &len2); + * ... + * return; + * + */ + + /* ContainsKey() + * + * Allows you to determine if a given TXT Record contains a specified key. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * return value: Returns 1 if the TXT Record contains the specified key. + * Otherwise, it returns 0. + * + */ + + static public bool + ContainsKey + ( + Byte txtRecord[], + String * key + ); + + + /* GetValueBytes() + * + * Allows you to retrieve the value for a given key from a TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * return value: Returns NULL if the key does not exist in this TXT record, + * or exists with no value (to differentiate between + * these two cases use ContainsKey()). + * Returns byte array + * if the key exists with empty or non-empty value. + * For empty value, length of byte array will be zero. + * For non-empty value, it will be the length of value data. + */ + + static public Byte + GetValueBytes + ( + Byte txtRecord[], + String * key + ) __gc[]; + + + /* GetCount() + * + * Returns the number of keys stored in the TXT Record. The count + * can be used with TXTRecordGetItemAtIndex() to iterate through the keys. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * return value: Returns the total number of keys in the TXT Record. + * + */ + + static public int + GetCount + ( + Byte txtRecord[] + ); + + + /* GetItemAtIndex() + * + * Allows you to retrieve a key name and value pointer, given an index into + * a TXT Record. Legal index values range from zero to TXTRecordGetCount()-1. + * It's also possible to iterate through keys in a TXT record by simply + * calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero + * and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid. + * + * On return: + * For keys with no value, *value is set to NULL and *valueLen is zero. + * For keys with empty value, *value is non-NULL and *valueLen is zero. + * For keys with non-empty value, *value is non-NULL and *valueLen is non-zero. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * index: An index into the TXT Record. + * + * key: A string buffer used to store the key name. + * On return, the buffer contains a string + * giving the key name. DNS-SD TXT keys are usually + * 14 characters or less. + * + * return value: Record bytes that holds the value data. + * + * exceptions: Throws kDNSServiceErr_Invalid if index is greater than + * GetCount()-1. + */ + + static public Byte + GetItemAtIndex + ( + Byte txtRecord[], + int index, + [Out] String ** key + ) __gc[]; + }; + + + public __abstract __gc class DNSService + { + public: + + /********************************************************************************************* + * + * Domain Enumeration + * + *********************************************************************************************/ + + /* DNSServiceEnumerateDomains() + * + * Asynchronously enumerate domains available for browsing and registration. + * Currently, the only domain returned is "local.", but other domains will be returned in future. + * + * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains + * are to be found. + * + * + * EnumerateDomainsReply Delegate + * + * This Delegate is invoked upon a reply from an EnumerateDomains call. + * + * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains(). + * + * flags: Possible values are: + * MoreComing + * Add + * Default + * + * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given + * interface is determined via the if_nametoindex() family of calls.) + * + * errorCode: Will be NoError (0) on success, otherwise indicates + * the failure that occurred (other parameters are undefined if errorCode is nonzero). + * + * replyDomain: The name of the domain. + * + */ + + __delegate void + EnumerateDomainsReply + ( + ServiceRef * sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * replyDomain + ); + + /* DNSServiceEnumerateDomains() Parameters: + * + * + * flags: Possible values are: + * BrowseDomains to enumerate domains recommended for browsing. + * RegistrationDomains to enumerate domains recommended + * for registration. + * + * interfaceIndex: If non-zero, specifies the interface on which to look for domains. + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to enumerate domains on + * all interfaces. + * + * callback: The delegate to be called when a domain is found or the call asynchronously + * fails. + * + * + * return value: Returns initialize ServiceRef on succeses (any subsequent, asynchronous + * errors are delivered to the delegate), otherwise throws an exception indicating + * the error that occurred (the callback is not invoked and the ServiceRef + * is not initialized.) + */ + + static public ServiceRef* + EnumerateDomains + ( + int flags, + int interfaceIndex, + EnumerateDomainsReply * callback + ); + + /********************************************************************************************* + * + * Service Registration + * + *********************************************************************************************/ + + /* Register a service that is discovered via Browse() and Resolve() calls. + * + * RegisterReply() Callback Parameters: + * + * sdRef: The ServiceRef initialized by Register(). + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts, if the + * NoAutoRename flag was passed to the + * callout.) Other parameters are undefined if errorCode is nonzero. + * + * name: The service name registered (if the application did not specify a name in + * DNSServiceRegister(), this indicates what name was automatically chosen). + * + * regtype: The type of service registered, as it was passed to the callout. + * + * domain: The domain on which the service was registered (if the application did not + * specify a domain in Register(), this indicates the default domain + * on which the service was registered). + * + */ + + __delegate void + RegisterReply + ( + ServiceRef * sdRef, + ServiceFlags flags, + ErrorCode errorCode, + String * name, + String * regtype, + String * domain + ); + + /* Register() Parameters: + * + * flags: Indicates the renaming behavior on name conflict (most applications + * will pass 0). See flag definitions above for details. + * + * interfaceIndex: If non-zero, specifies the interface on which to register the service + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to register on all + * available interfaces. Pass -1 to register a service only on the local + * machine (service will not be visible to remote hosts.) + * + * name: If non-NULL, specifies the service name to be registered. + * Most applications will not specify a name, in which case the + * computer name is used (this name is communicated to the client via + * the callback). + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * New service types should be registered at htp://www.dns-sd.org/ServiceTypes.html. + * + * domain: If non-NULL, specifies the domain on which to advertise the service. + * Most applications will not specify a domain, instead automatically + * registering in the default domain(s). + * + * host: If non-NULL, specifies the SRV target host name. Most applications + * will not specify a host, instead automatically using the machine's + * default host name(s). Note that specifying a non-NULL host does NOT + * create an address record for that host - the application is responsible + * for ensuring that the appropriate address record exists, or creating it + * via DNSServiceRegisterRecord(). + * + * port: The port, in host byte order, on which the service accepts connections. + * Pass 0 for a "placeholder" service (i.e. a service that will not be discovered + * by browsing, but will cause a name conflict if another client tries to + * register that same name). Most clients will not use placeholder services. + * + * txtRecord: The txt record rdata. May be NULL. Note that a non-NULL txtRecord + * MUST be a properly formatted DNS TXT record, i.e. + * ... + * + * callback: The delegate 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(). + * + * return value: Returns initialize ServiceRef (any subsequent, asynchronous + * errors are delivered to the callback), otherwise throws an exception indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + * + */ + static public ServiceRef* + Register + ( + int flags, + int interfaceIndex, + String * name, + String * regtype, + String * domain, + String * host, + int port, + Byte txtRecord[], + RegisterReply * callback + ); + + /* AddRecord() + * + * Add a record to a registered service. The name of the record will be the same as the + * registered service's name. + * The record can later be updated or deregistered by passing the RecordRef initialized + * by this function to UpdateRecord() or RemoveRecord(). + * + * + * Parameters; + * + * sdRef: A ServiceRef initialized by Register(). + * + * RecordRef: A pointer to an uninitialized RecordRef. Upon succesfull completion of this + * call, this ref may be passed to UpdateRecord() or RemoveRecord(). + * If the above ServiceRef is disposed, RecordRef is also + * invalidated and may not be used further. + * + * flags: Currently ignored, reserved for future use. + * + * rrtype: The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h. + * + * rdata: The raw rdata to be contained in the added resource record. + * + * ttl: The time to live of the resource record, in seconds. + * + * return value: Returns initialized RecordRef, otherwise throws + * an exception indicating the error that occurred (the RecordRef is not initialized). + */ + + static public RecordRef* + AddRecord + ( + ServiceRef * sref, + int flags, + int rrtype, + Byte rdata[], + int ttl + ); + + /* UpdateRecord + * + * Update a registered resource record. The record must either be: + * - The primary txt record of a service registered via Register() + * - A record added to a registered service via AddRecord() + * - An individual record registered by RegisterRecord() + * + * + * Parameters: + * + * sdRef: A ServiceRef that was initialized by Register() + * or CreateConnection(). + * + * RecordRef: A RecordRef initialized by AddRecord, or NULL to update the + * service's primary txt record. + * + * flags: Currently ignored, reserved for future use. + * + * rdata: The new rdata to be contained in the updated resource record. + * + * ttl: The time to live of the updated resource record, in seconds. + * + * return value: No return value on success, otherwise throws an exception + * indicating the error that occurred. + */ + static public void + UpdateRecord + ( + ServiceRef * sref, + RecordRef * record, + int flags, + Byte rdata[], + int ttl + ); + + /* RemoveRecord + * + * Remove a record previously added to a service record set via AddRecord(), or deregister + * an record registered individually via RegisterRecord(). + * + * Parameters: + * + * sdRef: A ServiceRef initialized by Register() (if the + * record being removed was registered via AddRecord()) or by + * CreateConnection() (if the record being removed was registered via + * RegisterRecord()). + * + * recordRef: A RecordRef initialized by a successful call to AddRecord() + * or RegisterRecord(). + * + * flags: Currently ignored, reserved for future use. + * + * return value: Nothing on success, otherwise throws an + * exception indicating the error that occurred. + */ + + static public void + RemoveRecord + ( + ServiceRef * sref, + RecordRef * record, + int flags + ); + + /********************************************************************************************* + * + * Service Discovery + * + *********************************************************************************************/ + + /* Browse for instances of a service. + * + * + * BrowseReply() Parameters: + * + * sdRef: The DNSServiceRef initialized by Browse(). + * + * flags: Possible values are MoreComing and Add. + * See flag definitions for details. + * + * interfaceIndex: The interface on which the service is advertised. This index should + * be passed to Resolve() when resolving the service. + * + * errorCode: Will be NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * serviceName: The service name discovered. + * + * regtype: The service type, as passed in to Browse(). + * + * domain: The domain on which the service was discovered (if the application did not + * specify a domain in Browse(), this indicates the domain on which the + * service was discovered.) + * + */ + + __delegate void + BrowseReply + ( + ServiceRef * sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * name, + String * type, + String * domain + ); + + /* DNSServiceBrowse() Parameters: + * + * sdRef: A pointer to an uninitialized ServiceRef. Call ServiceRef.Dispose() + * to terminate the browse. + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface on which to browse for services + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to browse on all available + * interfaces. Pass -1 to only browse for services provided on the local host. + * + * regtype: The service type being browsed for followed by the protocol, separated by a + * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * + * domain: If non-NULL, specifies the domain on which to browse for services. + * Most applications will not specify a domain, instead browsing on the + * default domain(s). + * + * callback: The delegate to be called when an instance of the service being browsed for + * is found, or if the call asynchronously fails. + * + * return value: Returns initialized ServiceRef on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise throws an exception indicating + * the error that occurred (the callback is not invoked and the ServiceRef + * is not initialized.) + */ + + static public ServiceRef* + Browse + ( + int flags, + int interfaceIndex, + String * regtype, + String * domain, + BrowseReply * callback + ); + + /* ResolveReply() Parameters: + * + * Resolve a service name discovered via Browse() to a target host name, port number, and + * txt record. + * + * Note: Applications should NOT use Resolve() solely for txt record monitoring - use + * QueryRecord() 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 + * ServiceRef.Dispose(). + * + * Note: Resolve() 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, QueryRecord() should be used. + * + * ResolveReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by Resolve(). + * + * flags: Currently unused, reserved for future use. + * + * interfaceIndex: The interface on which the service was resolved. + * + * errorCode: Will be NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * fullname: The full service domain name, in the form ... + * (Any literal dots (".") are escaped with a backslash ("\."), and literal + * backslashes are escaped with a second backslash ("\\"), e.g. a web server + * named "Dr. Pepper" would have the fullname "Dr\.\032Pepper._http._tcp.local."). + * This is the appropriate format to pass to standard system DNS APIs such as + * res_query(), or to the special-purpose functions included in this API that + * take fullname parameters. + * + * hosttarget: The target hostname of the machine providing the service. This name can + * be passed to functions like gethostbyname() to identify the host's IP address. + * + * port: The port, in host byte order, on which connections are accepted for this service. + * + * txtRecord: The service's primary txt record, in standard txt record format. + * + */ + + __delegate void + ResolveReply + ( + ServiceRef * sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * fullName, + String * hostName, + int port, + Byte txtRecord[] + ); + + /* Resolve() Parameters + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: The interface on which to resolve the service. The client should + * pass the interface on which the servicename was discovered, i.e. + * the interfaceIndex passed to the DNSServiceBrowseReply callback, + * or 0 to resolve the named service on all available interfaces. + * + * name: The servicename to be resolved. + * + * regtype: The service type being resolved followed by the protocol, separated by a + * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * + * domain: The domain on which the service is registered, i.e. the domain passed + * to the DNSServiceBrowseReply callback. + * + * callback: The delegate to be called when a result is found, or if the call + * asynchronously fails. + * + * + * return value: Returns initialized ServiceRef on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise throws an exception indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + + static public ServiceRef* + Resolve + ( + int flags, + int interfaceIndex, + String * name, + String * regtype, + String * domain, + ResolveReply * callback + ); + + /********************************************************************************************* + * + * Special Purpose Calls (most applications will not use these) + * + *********************************************************************************************/ + + /* CreateConnection/RegisterRecord + * + * Register an individual resource record on a connected ServiceRef. + * + * Note that name conflicts occurring for records registered via this call must be handled + * by the client in the callback. + * + * + * RecordReply() parameters: + * + * sdRef: The connected ServiceRef initialized by + * CreateConnection(). + * + * RecordRef: The RecordRef initialized by RegisterRecord(). If the above + * ServiceRef.Dispose is called, this RecordRef is + * invalidated, and may not be used further. + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts.) + * Other parameters are undefined if errorCode is nonzero. + * + */ + + __delegate void + RegisterRecordReply + ( + ServiceRef * sdRef, + ServiceFlags flags, + ErrorCode errorCode, + RecordRef * record + ); + + /* CreateConnection() + * + * Create a connection to the daemon allowing efficient registration of + * multiple individual records. + * + * + * Parameters: + * + * callback: The delegate to be called when a result is found, or if the call + * asynchronously fails (e.g. because of a name conflict.) + * + * return value: Returns initialize ServiceRef on success, otherwise throws + * an exception indicating the specific failure that occurred (in which + * case the ServiceRef is not initialized). + */ + + static public ServiceRef* + CreateConnection + ( + RegisterRecordReply * callback + ); + + + /* RegisterRecord() Parameters: + * + * sdRef: A ServiceRef initialized by CreateConnection(). + * + * RecordRef: A pointer to an uninitialized RecordRef. Upon succesfull completion of this + * call, this ref may be passed to UpdateRecord() or RemoveRecord(). + * (To deregister ALL records registered on a single connected ServiceRef + * and deallocate each of their corresponding RecordRefs, call + * ServiceRef.Dispose()). + * + * flags: Possible values are Shared or Unique + * (see flag type definitions for details). + * + * interfaceIndex: If non-zero, specifies the interface on which to register the record + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the record to be registered on all interfaces. + * Passing -1 causes the record to only be visible on the local host. + * + * fullname: The full domain name of the resource record. + * + * rrtype: The numerical type of the resource record (e.g. PTR, SRV, etc), as defined + * in nameser.h. + * + * rrclass: The class of the resource record, as defined in nameser.h (usually 1 for the + * Internet class). + * + * 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. + * + * + * return value: Returns initialize RecordRef on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise throws an exception indicating + * the error that occurred (the callback is never invoked and the RecordRef is + * not initialized.) + */ + static public RecordRef* + RegisterRecord + ( + ServiceRef * sdRef, + ServiceFlags flags, + int interfaceIndex, + String * fullname, + int rrtype, + int rrclass, + Byte rdata[], + int ttl + ); + + + /* DNSServiceQueryRecord + * + * Query for an arbitrary DNS record. + * + * + * QueryRecordReply() Delegate Parameters: + * + * sdRef: The ServiceRef initialized by QueryRecord(). + * + * flags: Possible values are MoreComing and + * Add. The Add flag is NOT set for PTR records + * with a ttl of 0, i.e. "Remove" events. + * + * interfaceIndex: The interface on which the query was resolved (the index for a given + * interface is determined via the if_nametoindex() family of calls). + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * errorCode is nonzero. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. + * + * rrclass: The class of the resource record, as defined in nameser.h (usually 1). + * + * rdata: The raw rdata of the resource record. + * + * ttl: The resource record's time to live, in seconds. + * + */ + + __delegate void + QueryRecordReply + ( + ServiceRef * sdRef, + ServiceFlags flags, + int interfaceIndex, + ErrorCode errorCode, + String * fullName, + int rrtype, + int rrclass, + Byte rdata[], + int ttl + ); + + /* QueryRecord() Parameters: + * + * flags: Pass LongLivedQuery to create a "long-lived" unicast + * query in a non-local domain. Without setting this flag, unicast queries + * will be one-shot - that is, only answers available at the time of the call + * will be returned. By setting this flag, answers (including Add and Remove + * events) that become available after the initial call is made will generate + * callbacks. This flag has no effect on link-local multicast queries. + * + * interfaceIndex: If non-zero, specifies the interface on which to issue the query + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the name to be queried for on all + * interfaces. Passing -1 causes the name to be queried for only on the + * local host. + * + * fullname: The full domain name of the resource record to be queried for. + * + * rrtype: The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc) + * as defined in nameser.h. + * + * rrclass: The class of the resource record, as defined in nameser.h + * (usually 1 for the Internet class). + * + * callback: The delegate to be called when a result is found, or if the call + * asynchronously fails. + * + * + * return value: Returns initialized ServiceRef on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise throws an exception indicating + * the error that occurred (the callback is never invoked and the ServiceRef + * is not initialized.) + */ + + static public ServiceRef* + QueryRecord + ( + ServiceFlags flags, + int interfaceIndex, + String * fullname, + int rrtype, + int rrclass, + QueryRecordReply * callback + ); + + /* ReconfirmRecord + * + * Instruct the daemon to verify the validity of a resource record that appears to + * be out of date (e.g. because tcp connection to a service's target failed.) + * Causes the record to be flushed from the daemon's cache (as well as all other + * daemons' caches on the network) if the record is determined to be invalid. + * + * Parameters: + * + * flags: Currently unused, reserved for future use. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. + * + * rrclass: The class of the resource record, as defined in nameser.h (usually 1). + * + * rdata: The raw rdata of the resource record. + * + */ + static public void + ReconfirmRecord + ( + ServiceFlags flags, + int interfaceIndex, + String * fullname, + int rrtype, + int rrclass, + Byte rdata[] + ); + }; + } +} diff --git a/mDNSWindows/DLL.NET/dnssd_NET.ico b/mDNSWindows/DLL.NET/dnssd_NET.ico new file mode 100755 index 0000000000000000000000000000000000000000..3a5525fd794f7a7c5c8e6187f470ea3af38cd2b6 GIT binary patch literal 1078 zcmeHHJr05}7=1t!Hp3A*8IHkVf+j?-!eHY14Gtcw1Eb*_9>Bq^zETJ@GKj{_2j4$w zo9}xCh!8{T3=X##Skq>ikMjsvB|y%crWBM2iW(4pI}c%z6%lW!=~4v77#3{z!dmB1 z__&l)-{KUYR+|8|;wB^R|9ET$J@(@=#rd^=)qs85?vAy(PSF5CyNkus435LVkZ$rj zNw|JG-P7^hF<(;#o*Vk}5R#e|^13tBbQkeF?djULtvqyxd3<{9 literal 0 HcmV?d00001 diff --git a/mDNSWindows/DLL.NET/dnssd_NET.rc b/mDNSWindows/DLL.NET/dnssd_NET.rc new file mode 100755 index 0000000..f3c3dd4 --- /dev/null +++ b/mDNSWindows/DLL.NET/dnssd_NET.rc @@ -0,0 +1,113 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "WinVersRes.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 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +1 ICON "dnssd_NET.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" + "\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION MASTER_PROD_VERS + PRODUCTVERSION MASTER_PROD_VERS + FILEFLAGSMASK 0x17L +#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", "dnssd.NET Dynamic Link Library" + VALUE "FileVersion", MASTER_PROD_VERS_STR + VALUE "InternalName", "dnssd.NET" + VALUE "LegalCopyright", "Copyright (C) 2004" + VALUE "OriginalFilename", "dnssd.NET.dll" + VALUE "ProductName", MASTER_PROD_NAME + VALUE "ProductVersion", MASTER_PROD_VERS_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/mDNSWindows/DLL.NET/dnssd_NET.vcproj b/mDNSWindows/DLL.NET/dnssd_NET.vcproj new file mode 100755 index 0000000..f2145d9 --- /dev/null +++ b/mDNSWindows/DLL.NET/dnssd_NET.vcproj @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/DLL.NET/resource.h b/mDNSWindows/DLL.NET/resource.h new file mode 100755 index 0000000..29338aa --- /dev/null +++ b/mDNSWindows/DLL.NET/resource.h @@ -0,0 +1,3 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by dnssd_NET.rc diff --git a/mDNSWindows/DLL/dll.aps b/mDNSWindows/DLL/dll.aps new file mode 100644 index 0000000000000000000000000000000000000000..31b4787e30c720c038d88ca0ba13b5332374ac8b GIT binary patch literal 18692 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 literal 0 HcmV?d00001 diff --git a/mDNSWindows/DLL/dll.rc b/mDNSWindows/DLL/dll.rc new file mode 100644 index 0000000..70b4839 --- /dev/null +++ b/mDNSWindows/DLL/dll.rc @@ -0,0 +1,102 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "WinVersRes.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" + "#include ""WinVersRes.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION MASTER_PROD_VERS + PRODUCTVERSION MASTER_PROD_VERS + FILEFLAGSMASK 0x17L +#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", "dnssd Dynamic Link Library" + VALUE "FileVersion", MASTER_PROD_VERS_STR + VALUE "InternalName", "dnssd" + VALUE "LegalCopyright", "Copyright (C) 2004" + VALUE "OriginalFilename", "dnssd.dll" + VALUE "ProductName", MASTER_PROD_NAME + VALUE "ProductVersion", MASTER_PROD_VERS_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/mDNSWindows/Applications/DLL/dllmain.c b/mDNSWindows/DLL/dllmain.c similarity index 91% rename from mDNSWindows/Applications/DLL/dllmain.c rename to mDNSWindows/DLL/dllmain.c index db9906e..a15aedf 100644 --- a/mDNSWindows/Applications/DLL/dllmain.c +++ b/mDNSWindows/DLL/dllmain.c @@ -3,8 +3,6 @@ * * @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 @@ -25,12 +23,15 @@ Change History (most recent first): $Log: dllmain.c,v $ +Revision 1.1 2004/06/18 03:55:11 rpantos +Move DLL up to main level; additional integration from Scott. + Revision 1.1 2004/02/21 04:16:50 bradley DLL wrapper for DNS-SD API. */ -#include "DNSSD.h" +#include BOOL APIENTRY DllMain( HANDLE inModule, DWORD inReason, LPVOID inReserved ) { diff --git a/mDNSWindows/Applications/DLL/dnssd.def b/mDNSWindows/DLL/dnssd.def similarity index 79% rename from mDNSWindows/Applications/DLL/dnssd.def rename to mDNSWindows/DLL/dnssd.def index 587489e..6249046 100644 --- a/mDNSWindows/Applications/DLL/dnssd.def +++ b/mDNSWindows/DLL/dnssd.def @@ -23,6 +23,12 @@ ; Change History (most recent first): ; ; $Log: dnssd.def,v $ +; Revision 1.2 2004/07/19 07:43:59 shersche +; export TXTRecord APIs +; +; Revision 1.1 2004/06/18 03:55:11 rpantos +; Move DLL up to main level; additional integration from Scott. +; ; 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. ; @@ -32,14 +38,9 @@ ; ; -LIBRARY DNSSD +LIBRARY dnssd EXPORTS - DNSServiceInitialize - DNSServiceFinalize - DNSServiceCheckVersion - DNSServiceCopyProperty - DNSServiceReleaseProperty DNSServiceRefSockFD DNSServiceProcessResult DNSServiceRefDeallocate @@ -55,3 +56,13 @@ EXPORTS DNSServiceRegisterRecord DNSServiceQueryRecord DNSServiceReconfirmRecord + TXTRecordCreate + TXTRecordDeallocate + TXTRecordSetValue + TXTRecordRemoveValue + TXTRecordContainsKey + TXTRecordGetCount + TXTRecordGetLength + TXTRecordGetBytesPtr + TXTRecordGetValuePtr + TXTRecordGetItemAtIndex diff --git a/mDNSWindows/DLL/dnssd.vcproj b/mDNSWindows/DLL/dnssd.vcproj new file mode 100644 index 0000000..01e412b --- /dev/null +++ b/mDNSWindows/DLL/dnssd.vcproj @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/DLL/resource.h b/mDNSWindows/DLL/resource.h new file mode 100644 index 0000000..bdea251 --- /dev/null +++ b/mDNSWindows/DLL/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by dll.rc +// +#define IDS_PROJNAME 100 +#define IDR_WMDMLOGGER 101 +#define IDS_LOG_SEV_INFO 201 +#define IDS_LOG_SEV_WARN 202 +#define IDS_LOG_SEV_ERROR 203 +#define IDS_LOG_DATETIME 204 +#define IDS_LOG_SRCNAME 205 +#define IDS_DEF_LOGFILE 301 +#define IDS_DEF_MAXSIZE 302 +#define IDS_DEF_SHRINKTOSIZE 303 +#define IDS_DEF_LOGENABLED 304 +#define IDS_MUTEX_TIMEOUT 401 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/mDNSWindows/DNSSD.c b/mDNSWindows/DNSSD.c deleted file mode 100644 index 24bc560..0000000 --- a/mDNSWindows/DNSSD.c +++ /dev/null @@ -1,1720 +0,0 @@ -/* - * 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 deleted file mode 100644 index 11cd6e3..0000000 --- a/mDNSWindows/DNSSD.h +++ /dev/null @@ -1,1689 +0,0 @@ -/* - * 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 deleted file mode 100644 index c20a97e..0000000 --- a/mDNSWindows/DNSSDDirect.c +++ /dev/null @@ -1,1863 +0,0 @@ -/* - * 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 deleted file mode 100644 index be7acb0..0000000 --- a/mDNSWindows/DNSSDDirect.h +++ /dev/null @@ -1,285 +0,0 @@ -/* - * 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/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.sln b/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.sln similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.sln rename to mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.sln diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj b/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj similarity index 98% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj rename to mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj index 5d72e30..c1a2424 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj +++ b/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj @@ -50,7 +50,7 @@ Name="VCLinkerTool" AdditionalOptions="/MACHINE:I386 /IGNORE:4089" AdditionalDependencies="ws2_32.lib" - OutputFile="Rendezvous Browser Debug.exe" + OutputFile="DNSServiceBrowser Debug.exe" LinkIncremental="1" SuppressStartupBanner="TRUE" IgnoreDefaultLibraryNames="wsock32.lib" @@ -122,7 +122,7 @@ Name="VCLinkerTool" AdditionalOptions="/MACHINE:I386 /IGNORE:4089" AdditionalDependencies="ws2_32.lib" - OutputFile="Rendezvous Browser.exe" + OutputFile="DNSServiceBrowser.exe" LinkIncremental="1" SuppressStartupBanner="TRUE" IgnoreDefaultLibraryNames="wsock32.lib" diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.sln b/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.sln similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.sln rename to mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.sln diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj b/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj similarity index 98% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj rename to mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj index f3b4e9a..3c8cb1c 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj +++ b/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj @@ -50,7 +50,7 @@ Name="VCLinkerTool" AdditionalOptions="/MACHINE:I386 /IGNORE:4089" AdditionalDependencies="ws2_32.lib" - OutputFile="Rendezvous Browser Debug.exe" + OutputFile="DNSServiceBrowser Debug.exe" LinkIncremental="1" SuppressStartupBanner="TRUE" IgnoreDefaultLibraryNames="wsock32.lib" @@ -128,7 +128,7 @@ Name="VCLinkerTool" AdditionalOptions="/MACHINE:I386 /IGNORE:4089" AdditionalDependencies="ws2_32.lib" - OutputFile="Rendezvous Browser.exe" + OutputFile="DNSServiceBrowser.exe" LinkIncremental="1" SuppressStartupBanner="TRUE" IgnoreDefaultLibraryNames="wsock32.lib" diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.ico b/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.ico similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.ico rename to mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.ico diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc b/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc similarity index 95% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc rename to mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc index cf2f292..3943139 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc +++ b/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc @@ -77,7 +77,7 @@ 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" +CAPTION "DNSServiceBrowser" MENU IDR_CHOOSER_DIALOG_MENU FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN @@ -111,11 +111,11 @@ END IDD_ABOUT_DIALOG DIALOGEX 0, 0, 244, 73 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION -CAPTION "About Rendezvous Browser" +CAPTION "About DNSServiceBrowser" FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN ICON IDR_MAIN_ICON,IDC_ABOUT_APP_ICON,12,12,20,20 - LTEXT "Rendezvous Browser",IDC_ABOUT_APP_NAME_TEXT,44,11,192, + LTEXT "DNSServiceBrowser",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.", @@ -163,12 +163,12 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Apple Computer, Inc." - VALUE "FileDescription", "Rendezvous Browser for Windows" + VALUE "FileDescription", "DNSServiceBrowser for Windows" VALUE "FileVersion", "1, 0, 0, 1" - VALUE "InternalName", "Rendezvous Browser for Windows" + VALUE "InternalName", "DNSServiceBrowser for Windows" VALUE "LegalCopyright", "Copyright (C) 2002-2004 Apple Computer, Inc." - VALUE "OriginalFilename", "RendezvousBrowser.exe" - VALUE "ProductName", "Rendezvous Browser for Windows" + VALUE "OriginalFilename", "DNSServiceBrowser.exe" + VALUE "ProductName", "DNSServiceBrowser for Windows" VALUE "ProductVersion", "1, 0, 0, 1" END END @@ -194,7 +194,7 @@ BEGIN END POPUP "Help" BEGIN - MENUITEM "About Rendezvous Browser...", ID_HELP_ABOUT + MENUITEM "About DNSServiceBrowser...", ID_HELP_ABOUT END END @@ -285,7 +285,7 @@ END STRINGTABLE BEGIN - IDS_ABOUTBOX "&About Rendezvous Browser" + IDS_ABOUTBOX "&About DNSServiceBrowser" IDS_CHOOSER_DOMAIN_COLUMN_NAME "Domains" IDP_SOCKETS_INIT_FAILED "Windows sockets initialization failed." IDS_CHOOSER_SERVICE_COLUMN_TYPE "Services" diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc2 b/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc2 similarity index 86% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc2 rename to mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc2 index 9c76766..ce71791 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Application.rc2 +++ b/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc2 @@ -23,17 +23,23 @@ Change History (most recent first): $Log: Application.rc2,v $ +Revision 1.2 2004/07/13 21:24:26 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:35 rpantos +Move up one level + 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. +Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder. Revision 1.2 2003/08/20 07:06:34 bradley Update to APSL 2.0. Updated change history to match other mDNSResponder files. Revision 1.1 2002/09/20 06:12:48 bradley -Rendezvous Browser for Windows +DNSServiceBrowser for Windows */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Resource.h b/mDNSWindows/DNSServiceBrowser/Windows/Resources/Resource.h similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Resources/Resource.h rename to mDNSWindows/DNSServiceBrowser/Windows/Resources/Resource.h diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp b/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp similarity index 93% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp index 50142c2..5984f33 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,17 @@ Change History (most recent first): $Log: AboutDialog.cpp,v $ +Revision 1.2 2004/07/13 21:24:26 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. +Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder. Revision 1.4 2003/08/12 19:56:28 cheshire Update to APSL 2.0 @@ -41,7 +45,7 @@ Revision 1.2 2002/09/21 20:44:55 zarzycki Added APSL info Revision 1.1 2002/09/20 06:12:49 bradley -Rendezvous Browser for Windows +DNSServiceBrowser for Windows */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.h b/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.h similarity index 92% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.h rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.h index ee97731..8263432 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/AboutDialog.h +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.h @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,17 @@ Change History (most recent first): $Log: AboutDialog.h,v $ +Revision 1.2 2004/07/13 21:24:26 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. +Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder. Revision 1.4 2003/08/12 19:56:28 cheshire Update to APSL 2.0 @@ -41,7 +45,7 @@ Revision 1.2 2002/09/21 20:44:55 zarzycki Added APSL info Revision 1.1 2002/09/20 06:12:50 bradley -Rendezvous Browser for Windows +DNSServiceBrowser for Windows */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.cpp b/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.cpp similarity index 94% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.cpp rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.cpp index 1fb0723..8d1dcfd 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.cpp +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,17 @@ Change History (most recent first): $Log: Application.cpp,v $ +Revision 1.2 2004/07/13 21:24:26 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. +Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder. Revision 1.5 2003/08/12 19:56:28 cheshire Update to APSL 2.0 @@ -44,7 +48,7 @@ Revision 1.2 2002/09/20 08:37:34 bradley Increased the DNS record cache from the default of 64 to 512 entries for larger networks. Revision 1.1 2002/09/20 06:12:51 bradley -Rendezvous Browser for Windows +DNSServiceBrowser for Windows */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.h b/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.h similarity index 92% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.h rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.h index 30705d3..498dd07 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/Application.h +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.h @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,17 @@ Change History (most recent first): $Log: Application.h,v $ +Revision 1.2 2004/07/13 21:24:26 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. +Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder. Revision 1.4 2003/08/12 19:56:28 cheshire Update to APSL 2.0 @@ -41,7 +45,7 @@ Revision 1.2 2002/09/21 20:44:55 zarzycki Added APSL info Revision 1.1 2002/09/20 06:12:51 bradley -Rendezvous Browser for Windows +DNSServiceBrowser for Windows */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp b/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp similarity index 99% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp index 162acda..0435f61 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,12 @@ Change History (most recent first): $Log: ChooserDialog.cpp,v $ +Revision 1.2 2004/07/13 21:24:26 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. @@ -56,7 +60,7 @@ from Andrew van der Stock for the addition of an _rfb._tcp service type for a VN 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. +Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder. Revision 1.7 2003/08/20 06:45:56 bradley Updated for IP address changes in DNSServices. Added support for browsing for Xserve RAID. @@ -78,7 +82,7 @@ Make sure each resolved item matches the selected service type to handle resolve been queued up on the Windows Message Loop. Reduce column to fit when scrollbar is present. Revision 1.1 2002/09/20 06:12:52 bradley -Rendezvous Browser for Windows +DNSServiceBrowser for Windows */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.h b/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.h similarity index 95% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.h rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.h index a98e5b6..8bda96c 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/ChooserDialog.h +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,12 @@ Change History (most recent first): $Log: ChooserDialog.h,v $ +Revision 1.2 2004/07/13 21:24:26 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. @@ -32,7 +36,7 @@ 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. +Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder. Revision 1.4 2003/08/12 19:56:28 cheshire Update to APSL 2.0 @@ -44,7 +48,7 @@ Revision 1.2 2002/09/21 20:44:55 zarzycki Added APSL info Revision 1.1 2002/09/20 06:12:52 bradley -Rendezvous Browser for Windows +DNSServiceBrowser for Windows */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp b/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp similarity index 98% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp index b35e76b..c3869e7 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: LoginDialog.cpp,v $ +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.h b/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.h similarity index 96% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.h rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.h index 2b31e6a..caf641a 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/LoginDialog.h +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: LoginDialog.h,v $ +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.cpp b/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.cpp similarity index 86% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.cpp rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.cpp index f1932eb..1ccc754 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.cpp +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,17 @@ Change History (most recent first): $Log: StdAfx.cpp,v $ +Revision 1.2 2004/07/13 21:24:26 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. +Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder. Revision 1.4 2003/08/12 19:56:28 cheshire Update to APSL 2.0 @@ -41,7 +45,7 @@ Revision 1.2 2002/09/21 20:44:55 zarzycki Added APSL info Revision 1.1 2002/09/20 06:12:53 bradley -Rendezvous Browser for Windows +DNSServiceBrowser for Windows */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.h b/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.h similarity index 91% rename from mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.h rename to mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.h index 35fee48..cb8031e 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/Windows/Sources/StdAfx.h +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,12 @@ Change History (most recent first): $Log: StdAfx.h,v $ +Revision 1.2 2004/07/13 21:24:26 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:36 rpantos +Move up one level + 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. @@ -32,7 +36,7 @@ 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. +Moved DNSServiceBrowser for non-Windows CE into Windows sub-folder. Revision 1.4 2003/08/12 19:56:28 cheshire Update to APSL 2.0 @@ -44,7 +48,7 @@ Revision 1.2 2002/09/21 20:44:56 zarzycki Added APSL info Revision 1.1 2002/09/20 06:12:53 bradley -Rendezvous Browser for Windows +DNSServiceBrowser for Windows */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcc b/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcc similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcc rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcc diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcp b/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcp similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcp rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcp diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcw b/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcw similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Application.vcw rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcw diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.ico b/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.ico similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.ico rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.ico diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.rc b/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc similarity index 96% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.rc rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc index 71af68d..c153326 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.rc +++ b/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc @@ -79,7 +79,7 @@ IDR_MAINFRAME ICON DISCARDABLE "Application.ico" IDD_APPLICATION_DIALOG DIALOG DISCARDABLE 0, 0, 139, 153 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION EXSTYLE WS_EX_APPWINDOW | 0x80000000L -CAPTION "Rendezvous Browser" +CAPTION "DNSServiceBrowser" FONT 8, "System" BEGIN CONTROL "List1",IDC_BROWSE_LIST,"SysListView32",LVS_REPORT | @@ -113,14 +113,14 @@ BEGIN BEGIN VALUE "Comments", "\0" VALUE "CompanyName", "Apple Computer, Inc.\0" - VALUE "FileDescription", "Rendezvous Browser for Windows CE\0" + VALUE "FileDescription", "DNSServiceBrowser for Windows CE\0" VALUE "FileVersion", "1, 0, 0, 1\0" VALUE "InternalName", "Application\0" VALUE "LegalCopyright", "Copyright (C) 2003-2004 Apple Computer, Inc.\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "Application.exe\0" VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Rendezvous Browser for Windows CE\0" + VALUE "ProductName", "DNSServiceBrowser for Windows CE\0" VALUE "ProductVersion", "1, 0, 0, 1\0" VALUE "SpecialBuild", "\0" END diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.rc2 b/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc2 similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/Application.rc2 rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc2 diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/newres.h b/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/newres.h similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/newres.h rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/newres.h diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/resource.h b/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/resource.h similarity index 100% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Resources/resource.h rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/resource.h diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.cpp b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.cpp similarity index 94% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.cpp rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.cpp index 9bcde04..39bb529 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.cpp +++ b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,17 @@ Change History (most recent first): $Log: Application.cpp,v $ +Revision 1.2 2004/07/13 21:24:27 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:37 rpantos +Move up one level + 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. +DNSServiceBrowser for HTTP services for Windows CE/PocketPC. */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.h b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.h similarity index 92% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.h rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.h index 5e5b507..08409b1 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/Application.h +++ b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.h @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,17 @@ Change History (most recent first): $Log: Application.h,v $ +Revision 1.2 2004/07/13 21:24:27 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:37 rpantos +Move up one level + 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. +DNSServiceBrowser for HTTP services for Windows CE/PocketPC. */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp similarity index 98% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp index 63208aa..2bdd05b 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp +++ b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,12 @@ Change History (most recent first): $Log: BrowserDialog.cpp,v $ +Revision 1.2 2004/07/13 21:24:27 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:37 rpantos +Move up one level + 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. @@ -39,7 +43,7 @@ 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. +DNSServiceBrowser for HTTP services for Windows CE/PocketPC. */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h similarity index 94% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h index e2f56c9..e0a7723 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h +++ b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,12 @@ Change History (most recent first): $Log: BrowserDialog.h,v $ +Revision 1.2 2004/07/13 21:24:27 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:37 rpantos +Move up one level + 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. @@ -36,7 +40,7 @@ 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. +DNSServiceBrowser for HTTP services for Windows CE/PocketPC. */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp similarity index 86% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp index 33ea0a3..919256e 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp +++ b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,17 @@ Change History (most recent first): $Log: StdAfx.cpp,v $ +Revision 1.2 2004/07/13 21:24:27 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:37 rpantos +Move up one level + 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. +DNSServiceBrowser for HTTP services for Windows CE/PocketPC. */ diff --git a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h similarity index 91% rename from mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h rename to mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h index e3be196..f598c26 100644 --- a/mDNSWindows/Applications/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h +++ b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h @@ -3,8 +3,6 @@ * * @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 @@ -25,11 +23,17 @@ Change History (most recent first): $Log: StdAfx.h,v $ +Revision 1.2 2004/07/13 21:24:27 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:04:37 rpantos +Move up one level + 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. +DNSServiceBrowser for HTTP services for Windows CE/PocketPC. */ diff --git a/mDNSWindows/Applications/DNSServiceTest/Tool.c b/mDNSWindows/DNSServiceTest/Tool.c similarity index 95% rename from mDNSWindows/Applications/DNSServiceTest/Tool.c rename to mDNSWindows/DNSServiceTest/Tool.c index 46e8e0c..258363e 100644 --- a/mDNSWindows/Applications/DNSServiceTest/Tool.c +++ b/mDNSWindows/DNSServiceTest/Tool.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,15 @@ Change History (most recent first): $Log: Tool.c,v $ +Revision 1.3 2004/09/16 01:58:25 cheshire +Fix compiler warnings + +Revision 1.2 2004/07/13 21:24:28 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:07:54 rpantos +Move up one level + Revision 1.12 2004/04/09 21:03:15 bradley Changed port numbers to use network byte order for consistency with other platforms. @@ -44,7 +51,7 @@ Revision 1.7 2003/08/20 07:06:34 bradley Update to APSL 2.0. Updated change history to match other mDNSResponder files. Revision 1.6 2003/08/20 06:50:55 bradley -Updated to latest internal version of the Rendezvous for Windows code: Re-did everything to support +Updated to latest internal version of the mDNSCore code: Re-did everything to support the latest DNSServices APIs (proxies, record updates, etc.); Added support for testing the platform neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDiscovery API. @@ -214,14 +221,14 @@ struct PresetData static const PresetData gPresets[] = { - /* 01 */ { 2, { "rendezvous", "-bbd" } }, - /* 02 */ { 4, { "rendezvous", "-bs", "_airport._tcp", "local." } }, - /* 03 */ { 4, { "rendezvous", "-bs", "_xserveraid._tcp", "local." } }, - /* 04 */ { 3, { "rendezvous", "-rdb", "apple.com" } }, - /* 05 */ { 7, { "rendezvous", "-rs", "My Fake AirPort", "_airport._tcp", "local.", "1234", "My Fake Info" } }, - /* 06 */ { 7, { "rendezvous", "-rs", "My Fake Xserve RAID", "_xserveraid._tcp", "local.", "1234", "My Fake Info" } }, - /* 07 */ { 7, { "rendezvous", "-rs", "My Fake Web Server", "_http._tcp", "local.", "8080", "index.html" } }, - /* 08 */ { 9, { "rendezvous", "-rps", "www.apple.com", "17.254.0.91", "Apple Web Server", "_http._tcp", "local.", "80", "index.html" } }, + /* 01 */ { 2, { "DNSServiceTest", "-bbd" } }, + /* 02 */ { 4, { "DNSServiceTest", "-bs", "_airport._tcp", "local." } }, + /* 03 */ { 4, { "DNSServiceTest", "-bs", "_xserveraid._tcp", "local." } }, + /* 04 */ { 3, { "DNSServiceTest", "-rdb", "apple.com" } }, + /* 05 */ { 7, { "DNSServiceTest", "-rs", "My Fake AirPort", "_airport._tcp", "local.", "1234", "My Fake Info" } }, + /* 06 */ { 7, { "DNSServiceTest", "-rs", "My Fake Xserve RAID", "_xserveraid._tcp", "local.", "1234", "My Fake Info" } }, + /* 07 */ { 7, { "DNSServiceTest", "-rs", "My Fake Web Server", "_http._tcp", "local.", "8080", "index.html" } }, + /* 08 */ { 9, { "DNSServiceTest", "-rps", "www.apple.com", "17.254.0.91", "Apple Web Server", "_http._tcp", "local.", "80", "index.html" } }, }; const int gPresetsCount = sizeof( gPresets ) / sizeof( gPresets[ 0 ] ); @@ -241,7 +248,7 @@ int main( int argc, char* argv[] ) // Set up DNS Services and install a Console Control Handler to handle things like control-c signals. err = DNSServicesInitialize( kDNSFlagAdvertise, 0 ); - require_noerr_string( err, exit, "could not initialize Rendezvous" ); + require_noerr_string( err, exit, "could not initialize DNSServiceTest" ); #if( __MACH__ ) signal( SIGINT, SigIntHandler ); @@ -265,7 +272,7 @@ exit: static void Usage( void ) { fprintf( stderr, "\n" ); - fprintf( stderr, "rendezvous - Rendezvous Tool 1.0d1\n" ); + fprintf( stderr, "DNSServiceTest - DNS-SD Test Tool 1.0d1\n" ); fprintf( stderr, "\n" ); fprintf( stderr, " -bbd 'b'rowse for 'b'rowsing 'd'omains\n" ); fprintf( stderr, " -brd 'b'rowse for 'r'egistration 'd'omains\n" ); @@ -285,13 +292,13 @@ static void Usage( void ) fprintf( stderr, " -h[elp] 'h'elp\n" ); fprintf( stderr, "\n" ); - fprintf( stderr, " -1 Preset 1 (browse for browsing domains) rendezvous -bbd\n" ); - fprintf( stderr, " -2 Preset 2 (browse for AirPort) rendezvous -bs \"_airport._tcp\" \"local.\"\n" ); - fprintf( stderr, " -3 Preset 3 (browse for Xserve RAID) rendezvous -bs \"_xserveraid._tcp\" \"local.\"\n" ); - fprintf( stderr, " -4 Preset 4 (register apple.com domain) rendezvous -rdb \"apple.com\"\n" ); - fprintf( stderr, " -5 Preset 5 (register fake AirPort) rendezvous -rs \"My Fake AirPort\" \"_airport._tcp\" \"local.\" 1234 \"My Fake Info\"\n" ); - fprintf( stderr, " -6 Preset 6 (register fake Xserve RAID) rendezvous -rs \"My Fake Xserve RAID\" \"_xserveraid._tcp\" \"local.\" 1234 \"My Fake Info\"\n" ); - fprintf( stderr, " -7 Preset 7 (register fake web server) rendezvous -rs \"My Fake Web Server\" \"_http._tcp\" \"local.\" 8080 \"index.html\"\n" ); + fprintf( stderr, " -1 Preset 1 (browse for browsing domains) DNSServiceTest -bbd\n" ); + fprintf( stderr, " -2 Preset 2 (browse for AirPort) DNSServiceTest -bs \"_airport._tcp\" \"local.\"\n" ); + fprintf( stderr, " -3 Preset 3 (browse for Xserve RAID) DNSServiceTest -bs \"_xserveraid._tcp\" \"local.\"\n" ); + fprintf( stderr, " -4 Preset 4 (register apple.com domain) DNSServiceTest -rdb \"apple.com\"\n" ); + fprintf( stderr, " -5 Preset 5 (register fake AirPort) DNSServiceTest -rs \"My Fake AirPort\" \"_airport._tcp\" \"local.\" 1234 \"My Fake Info\"\n" ); + fprintf( stderr, " -6 Preset 6 (register fake Xserve RAID) DNSServiceTest -rs \"My Fake Xserve RAID\" \"_xserveraid._tcp\" \"local.\" 1234 \"My Fake Info\"\n" ); + fprintf( stderr, " -7 Preset 7 (register fake web server) DNSServiceTest -rs \"My Fake Web Server\" \"_http._tcp\" \"local.\" 8080 \"index.html\"\n" ); fprintf( stderr, "\n" ); } @@ -734,28 +741,28 @@ static void BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inS break; case kDNSBrowserEventTypeAddDomain: - fprintf( stdout, "domain \"%s\" added on interface 0x%08X (%s)\n", + fprintf( stdout, "domain \"%s\" added on interface 0x%p (%s)\n", inEvent->data.addDomain.domain, (int) inEvent->data.addDomain.interfaceID, IPv4ToString( inEvent->data.addDomain.interfaceIP.u.ipv4.addr, ifIP ) ); break; case kDNSBrowserEventTypeAddDefaultDomain: - fprintf( stdout, "default domain \"%s\" added on interface 0x%08X (%s)\n", + fprintf( stdout, "default domain \"%s\" added on interface 0x%p (%s)\n", inEvent->data.addDefaultDomain.domain, (int) inEvent->data.addDefaultDomain.interfaceID, IPv4ToString( inEvent->data.addDefaultDomain.interfaceIP.u.ipv4.addr, ifIP ) ); break; case kDNSBrowserEventTypeRemoveDomain: - fprintf( stdout, "domain \"%s\" removed on interface 0x%08X (%s)\n", + fprintf( stdout, "domain \"%s\" removed on interface 0x%p (%s)\n", inEvent->data.removeDomain.domain, (int) inEvent->data.removeDomain.interfaceID, IPv4ToString( inEvent->data.removeDomain.interfaceIP.u.ipv4.addr, ifIP ) ); break; case kDNSBrowserEventTypeAddService: - fprintf( stdout, "service \"%s.%s%s\" added on interface 0x%08X (%s)\n", + fprintf( stdout, "service \"%s.%s%s\" added on interface 0x%p (%s)\n", inEvent->data.addService.name, inEvent->data.addService.type, inEvent->data.addService.domain, @@ -764,7 +771,7 @@ static void BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inS break; case kDNSBrowserEventTypeRemoveService: - fprintf( stdout, "service \"%s.%s%s\" removed on interface 0x%08X (%s)\n", + fprintf( stdout, "service \"%s.%s%s\" removed on interface 0x%p (%s)\n", inEvent->data.removeService.name, inEvent->data.removeService.type, inEvent->data.removeService.domain, @@ -778,7 +785,7 @@ static void BrowserCallBack( void *inContext, DNSBrowserRef inRef, DNSStatus inS const uint8_t * end; int i; - fprintf( stdout, "resolved \"%s.%s%s\" to \"%s\" (%s:%u) on interface 0x%08X (%s)%s\n", + fprintf( stdout, "resolved \"%s.%s%s\" to \"%s\" (%s:%u) on interface 0x%p (%s)%s\n", inEvent->data.resolved->name, inEvent->data.resolved->type, inEvent->data.resolved->domain, @@ -841,7 +848,7 @@ static void ResolverCallBack( void *inContext, DNSResolverRef inRef, DNSStatus i const uint8_t * end; int i; - fprintf( stdout, "resolved \"%s.%s%s\" to \"%s\" (%s:%u) on interface 0x%08X (%s)%s\n", + fprintf( stdout, "resolved \"%s.%s%s\" to \"%s\" (%s:%u) on interface 0x%p (%s)%s\n", inEvent->data.resolved.name, inEvent->data.resolved.type, inEvent->data.resolved.domain, diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindows.h b/mDNSWindows/DNSServiceTest/ToolPrefixWindows.h similarity index 95% rename from mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindows.h rename to mDNSWindows/DNSServiceTest/ToolPrefixWindows.h index 901df49..6469748 100644 --- a/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindows.h +++ b/mDNSWindows/DNSServiceTest/ToolPrefixWindows.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: ToolPrefixWindows.h,v $ +Revision 1.1 2004/06/18 04:07:54 rpantos +Move up one level + Revision 1.3 2004/01/30 03:04:32 bradley Updated for latest changes to mDNSWindows. diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindowsDebug.h b/mDNSWindows/DNSServiceTest/ToolPrefixWindowsDebug.h similarity index 95% rename from mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindowsDebug.h rename to mDNSWindows/DNSServiceTest/ToolPrefixWindowsDebug.h index 5858503..7b73d2e 100644 --- a/mDNSWindows/Applications/DNSServiceTest/ToolPrefixWindowsDebug.h +++ b/mDNSWindows/DNSServiceTest/ToolPrefixWindowsDebug.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: ToolPrefixWindowsDebug.h,v $ +Revision 1.1 2004/06/18 04:07:54 rpantos +Move up one level + Revision 1.3 2004/01/30 03:04:32 bradley Updated for latest changes to mDNSWindows. diff --git a/mDNSWindows/DNSServiceTest/ToolWin32.mcp b/mDNSWindows/DNSServiceTest/ToolWin32.mcp new file mode 100644 index 0000000000000000000000000000000000000000..d82908187345ba50f8548396924784023a3dddb9 GIT binary patch literal 127071 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 X$h diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.sln b/mDNSWindows/DNSServiceTest/ToolWin32VS2003.sln similarity index 100% rename from mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.sln rename to mDNSWindows/DNSServiceTest/ToolWin32VS2003.sln diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.vcproj b/mDNSWindows/DNSServiceTest/ToolWin32VS2003.vcproj similarity index 99% rename from mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.vcproj rename to mDNSWindows/DNSServiceTest/ToolWin32VS2003.vcproj index c0ed0ee..7393437 100644 --- a/mDNSWindows/Applications/DNSServiceTest/ToolWin32VS2003.vcproj +++ b/mDNSWindows/DNSServiceTest/ToolWin32VS2003.vcproj @@ -131,7 +131,7 @@ diff --git a/mDNSWindows/DNSServices/DNSServiceDiscovery.c b/mDNSWindows/DNSServices/DNSServiceDiscovery.c index e8d5e2e..94e2d02 100644 --- a/mDNSWindows/DNSServices/DNSServiceDiscovery.c +++ b/mDNSWindows/DNSServices/DNSServiceDiscovery.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,12 @@ Change History (most recent first): $Log: DNSServiceDiscovery.c,v $ +Revision 1.8 2004/09/17 01:08:58 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + Revision 1.7 2004/05/08 12:24:48 bradley Removed trailing character from zero value to fix compile error. @@ -38,7 +42,7 @@ Updated to support full Unicode display. Added support for all services on www.d 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. +Best solution is just to combine mDNSEmbeddedAPI.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. @@ -74,7 +78,7 @@ Platform-neutral DNSServices-based emulation layer for the Mac OS X DNSServiceDi #endif -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "DNSServices.h" #include "DNSServiceDiscovery.h" diff --git a/mDNSWindows/DNSServices/DNSServiceDiscovery.h b/mDNSWindows/DNSServices/DNSServiceDiscovery.h index eae6d47..084b0b8 100644 --- a/mDNSWindows/DNSServices/DNSServiceDiscovery.h +++ b/mDNSWindows/DNSServices/DNSServiceDiscovery.h @@ -3,8 +3,6 @@ * * @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/DNSServices/DNSServices.c b/mDNSWindows/DNSServices/DNSServices.c index 79a3258..a8aedb3 100755 --- a/mDNSWindows/DNSServices/DNSServices.c +++ b/mDNSWindows/DNSServices/DNSServices.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,26 @@ Change History (most recent first): $Log: DNSServices.c,v $ +Revision 1.31 2004/10/19 21:33:23 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.30 2004/09/17 01:08:58 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.29 2004/09/17 00:31:53 cheshire +For consistency with ipv6, renamed rdata field 'ip' to 'ipv4' + +Revision 1.28 2004/09/16 01:58:25 cheshire +Fix compiler warnings + +Revision 1.27 2004/07/13 21:24:28 rpantos +Fix for . + Revision 1.26 2004/06/05 00:04:27 cheshire : wide-area domains should be returned in reg. domain enumeration @@ -49,10 +67,10 @@ Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 2 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. +Best solution is just to combine mDNSEmbeddedAPI.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 +Move AssignDomainName macro to mDNSEmbeddedAPI.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. @@ -61,7 +79,7 @@ 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 +Updated to latest internal version of the mDNSCore code: Added support for interface specific registrations; Added support for no-such-service registrations; Added support for host name registrations; Added support for host proxy and service proxy registrations; Added support for registration record updates (e.g. TXT record updates); Added support for using either a single C @@ -138,7 +156,7 @@ DNS Services for Windows #include #endif -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "DNSServices.h" @@ -871,7 +889,7 @@ DNSStatus MakeDomainNameFromDNSNameString( &type, inType ); MakeDomainNameFromDNSNameString( &domain, inDomain ); - err = mDNS_StartBrowse( gMDNSPtr, &inRef->serviceBrowseQuestion, &type, &domain, mDNSInterface_Any, + err = mDNS_StartBrowse( gMDNSPtr, &inRef->serviceBrowseQuestion, &type, &domain, mDNSInterface_Any, mDNSfalse, DNSBrowserPrivateCallBack, inRef ); require_noerr( err, exit ); @@ -1123,11 +1141,11 @@ mDNSlocal void break; case kDNSResolverEventTypeRelease: - verbosedebugf( DEBUG_NAME "private resolver callback: release (ref=0x%08X)", inRef ); + verbosedebugf( DEBUG_NAME "private resolver callback: release (ref=0x%p)", inRef ); break; default: - verbosedebugf( DEBUG_NAME "private resolver callback: unknown event (ref=0x%08X, event=%ld)", inRef, inEvent->type ); + verbosedebugf( DEBUG_NAME "private resolver callback: unknown event (ref=0x%p, event=%ld)", inRef, inEvent->type ); break; } @@ -2417,7 +2435,7 @@ DNSStatus mDNS_snprintf( buffer, sizeof( buffer ), "%d.%d.%d.%d.in-addr.arpa.", ip.b[ 3 ], ip.b[ 2 ], ip.b[ 1 ], ip.b[ 0 ] ); MakeDomainNameFromDNSNameString( &object->RR_PTR.resrec.name, buffer ); - object->RR_A.resrec.rdata->u.ip = ip; + object->RR_A.resrec.rdata->u.ipv4 = ip; AssignDomainName( object->RR_PTR.resrec.rdata->u.name, object->RR_A.resrec.name ); // Add the object to the list. diff --git a/mDNSWindows/DNSServices/DNSServices.h b/mDNSWindows/DNSServices/DNSServices.h index bbf48b1..8e16a8c 100755 --- a/mDNSWindows/DNSServices/DNSServices.h +++ b/mDNSWindows/DNSServices/DNSServices.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: DNSServices.h,v $ +Revision 1.11 2004/07/13 21:24:28 rpantos +Fix for . + 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. @@ -32,7 +33,7 @@ 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 +Updated to latest internal version of the mDNSCore code: Added support for interface specific registrations; Added support for no-such-service registrations; Added support for host name registrations; Added support for host proxy and service proxy registrations; Added support for registration record updates (e.g. TXT record updates); Added support for using either a single C @@ -1322,7 +1323,7 @@ DNSStatus //--------------------------------------------------------------------------------------------------------------------------- /*! @function DNSNoSuchServiceRegistrationCreate - @abstract Creates a registration object and publish the registration to assert non-existance of a particular service. + @abstract Creates a registration object and publish the registration to assert non-existence of a particular service. @param inFlags Flags to control the registration process. diff --git a/mDNSWindows/DebugServices.c b/mDNSWindows/DebugServices.c index 7742b59..7562c2e 100644 --- a/mDNSWindows/DebugServices.c +++ b/mDNSWindows/DebugServices.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,12 @@ Change History (most recent first): $Log: DebugServices.c,v $ +Revision 1.5 2004/09/17 01:08:57 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + Revision 1.4 2004/04/15 08:59:08 bradley Removed deprecated debug and log levels and replaced them with modern equivalents. @@ -82,10 +86,10 @@ Debugging support for various platforms. #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 MDNS_DEBUGMSGS is defined (even if defined 0), it is aware of mDNS and it is probably safe to include mDNSEmbeddedAPI.h. #if( defined( MDNS_DEBUGMSGS ) ) - #include "mDNSClientAPI.h" + #include "mDNSEmbeddedAPI.h" #endif #if 0 @@ -1178,8 +1182,8 @@ static pascal void // 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. +// Changed types to standard C types since mDNSEmbeddedAPI.h may not be available. +// Conditionalized mDNS stuff so it can be used with or with mDNSEmbeddedAPI.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. @@ -1391,7 +1395,7 @@ DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char default: F.precision = 0; break; } #else - F.precision = 0; // mDNSClientAPI.h not included so no mDNSAddr support + F.precision = 0; // mDNSEmbeddedAPI.h not included so no mDNSAddr support #endif } else if (F.altForm == 2) diff --git a/mDNSWindows/DebugServices.h b/mDNSWindows/DebugServices.h index fd914ee..e687406 100644 --- a/mDNSWindows/DebugServices.h +++ b/mDNSWindows/DebugServices.h @@ -3,8 +3,6 @@ * * @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/Installer.vct b/mDNSWindows/Installer.vct deleted file mode 100644 index c31317d785551748d31e184eb5b2c6e6330568f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40028 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

QQoOuHy#!M_?7)m1Ppl<{ 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 a8`m#iyUJzHny56S%2bP5qco}b^Zx)F5s_8^ diff --git a/mDNSWindows/Installer/Main.ism b/mDNSWindows/Installer/Main.ism new file mode 100644 index 0000000000000000000000000000000000000000..c430d40b181fea53399446cfeca72269dcf155d7 GIT binary patch literal 195584 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

n}hsO~0UTS`Wc8qaD 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 la)!z6lJQCugp+pDn-hv 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 VT60_CF#bQ+@weTaGriDv{y*Lex~%{J literal 0 HcmV?d00001 diff --git a/mDNSWindows/Applications/SystemService/Service.rc b/mDNSWindows/Java/jdns_sd.rc similarity index 51% rename from mDNSWindows/Applications/SystemService/Service.rc rename to mDNSWindows/Java/jdns_sd.rc index 833a410..42511a3 100644 --- a/mDNSWindows/Applications/SystemService/Service.rc +++ b/mDNSWindows/Java/jdns_sd.rc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,29 +21,26 @@ * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): - -$Log: Service.rc,v $ -Revision 1.1 2004/01/30 02:58:39 bradley -mDNSResponder Windows Service. Provides global Rendezvous support with an IPC interface. -*/ +$Log: jdns_sd.rc,v $ +Revision 1.3 2004/10/19 03:41:42 shersche + Include "afxres.h" to resource script so it gets compiled correctly +Bug #: 3843396 -#ifdef RC_INVOKED - #ifndef _INC_WINDOWS - #define _INC_WINDOWS - #include "winres.h" - #endif -#endif +Revision 1.2 2004/06/26 21:27:38 rpantos +Update to use WinVersRes.h -#include "Resource.h" +Revision 1.1 2004/06/26 20:06:51 rpantos +Update to use WinVersRes.h -///////////////////////////////////////////////////////////////////////////// -// -// Messages -// -LANGUAGE 0x9,0x1 -1 11 EventLogMessages.bin +*/ + +#ifndef JDNS_SD_RC +#define JDNS_SD_RC + +#include "afxres.h" +#include "../WinVersRes.h" ///////////////////////////////////////////////////////////////////////////// // @@ -51,16 +48,16 @@ LANGUAGE 0x9,0x1 // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,1 - PRODUCTVERSION 1,0,0,1 - FILEFLAGSMASK 0x3fL + FILEVERSION MASTER_PROD_VERS + PRODUCTVERSION MASTER_PROD_VERS + FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L - FILETYPE 0x1L + FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" @@ -68,13 +65,13 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Apple Computer, Inc." - VALUE "FileDescription", "mDNSResponder" - VALUE "FileVersion", "1, 0, 0, 1" - VALUE "InternalName", "mDNSResponder" - VALUE "LegalCopyright", "Copyright (C) 2003-2004 Apple Computer, Inc." - VALUE "OriginalFilename", "mDNSResponder.exe" - VALUE "ProductName", "mDNSResponder" - VALUE "ProductVersion", "1, 0, 0, 1" + VALUE "FileDescription", MASTER_PROD_NAME " support for Java" + VALUE "FileVersion", MASTER_PROD_VERS_STR + VALUE "InternalName", "jdns_sd" + VALUE "LegalCopyright", "Copyright (C) 2004" + VALUE "OriginalFilename", "jdns_sd.dll" + VALUE "ProductName", MASTER_PROD_NAME + VALUE "ProductVersion", MASTER_PROD_VERS_STR END END BLOCK "VarFileInfo" @@ -83,12 +80,4 @@ BEGIN END END -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDS_SERVICE_DESCRIPTION "Rendezvous enables hardware devices and software services to automatically configure themselves on the network and advertise their presence, so that users can discover and use those services without any unnecessary manual setup or administration." -END +#endif // JDNS_SD_RC diff --git a/mDNSWindows/Applications/Java/makefile b/mDNSWindows/Java/makefile old mode 100755 new mode 100644 similarity index 87% rename from mDNSWindows/Applications/Java/makefile rename to mDNSWindows/Java/makefile index 3ed3f9f..d787410 --- a/mDNSWindows/Applications/Java/makefile +++ b/mDNSWindows/Java/makefile @@ -20,6 +20,15 @@ # @APPLE_LICENSE_HEADER_END@ # # $Log: makefile,v $ +# Revision 1.3 2004/11/23 08:13:07 shersche +# Link to the iphlpapi.lib for GetAdaptersInfo +# +# Revision 1.2 2004/06/26 20:07:06 rpantos +# Update to use WinVersRes.h +# +# Revision 1.1 2004/06/18 04:12:05 rpantos +# Move up one level. Integration changes for Scott. +# # Revision 1.2 2004/05/01 00:31:41 rpantos # Change line endings for CVS. # @@ -38,17 +47,18 @@ # 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 +# The default location of the JDK is \javasdk. You can override this on the # command line (e.g. 'nmake JDK=\j2dk1.4.2_03'). ############################################################################ -COREDIR = ..\..\..\mDNSCore -SHAREDDIR = ..\..\..\mDNSShared +COREDIR = ..\..\mDNSCore +SHAREDDIR = ..\..\mDNSShared -JDK = d:\javasdk +JDK = \javasdk CC = cl +RC = rc LD = ld CP = copy RM = del /Q @@ -56,7 +66,7 @@ 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..\.. \ +CFLAGS_COMMON = -LD -DAUTO_CALLBACKS=0 -I. -I..\.. \ -I$(COREDIR) -I$(SHAREDDIR) -I$(JDK)\include -I$(JDK)\include\win32 # Set up diverging paths for debug vs. prod builds @@ -116,9 +126,9 @@ JARCONTENTS = $(OBJDIR)\com\apple\dnssd\DNSSDService.class \ $(BUILDDIR)\dns_sd.jar: $(JARCONTENTS) $(JAR) -cf $@ -C $(OBJDIR) com -$(BUILDDIR)\jdns_sd.dll: $(JAVASRC)\JNISupport.c $(OBJDIR)\DNSSD.java.h +$(BUILDDIR)\jdns_sd.dll: $(JAVASRC)\JNISupport.c $(OBJDIR)\DNSSD.java.h $(OBJDIR)\jdns_sd.RES $(CC) -Fe$@ $(JAVASRC)\JNISupport.c $(CFLAGS) -I$(OBJDIR) \ - $(LIBDIR)\DNSSD.lib $(JDK)\lib\jvm.lib + $(LIBDIR)\DNSSD.lib $(JDK)\lib\jvm.lib ws2_32.lib iphlpapi.lib $(OBJDIR)\jdns_sd.RES .SUFFIXES : .java {$(JAVASRC)}.java{$(OBJDIR)\com\apple\dnssd}.class: @@ -132,3 +142,6 @@ $(OBJDIR)\DNSSD.java.h: $(OBJDIR)\com\apple\dnssd\DNSSD.class com.apple.dnssd.AppleQuery \ com.apple.dnssd.AppleService +$(OBJDIR)\jdns_sd.RES: jdns_sd.rc + $(RC) /fo $@ $? + diff --git a/mDNSWindows/NSPTool/NSPTool.aps b/mDNSWindows/NSPTool/NSPTool.aps new file mode 100644 index 0000000000000000000000000000000000000000..313f3068be307b091c0a6b3a1dd5f2283676d674 GIT binary patch literal 34296 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 HG*|s!hkI>$ literal 0 HcmV?d00001 diff --git a/mDNSWindows/Applications/NSPTool/Tool.c b/mDNSWindows/NSPTool/NSPTool.c similarity index 94% rename from mDNSWindows/Applications/NSPTool/Tool.c rename to mDNSWindows/NSPTool/NSPTool.c index bee72f8..3648655 100644 --- a/mDNSWindows/Applications/NSPTool/Tool.c +++ b/mDNSWindows/NSPTool/NSPTool.c @@ -3,8 +3,6 @@ * * @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 @@ -24,7 +22,18 @@ Change History (most recent first): -$Log: Tool.c,v $ +$Log: NSPTool.c,v $ +Revision 1.3 2004/08/26 04:46:49 shersche +Add -q switch for silent operation + +Revision 1.2 2004/06/23 16:39:14 shersche +Fix extraneous warnings regarding implict casts + +Submitted by: Scott Herscher (sherscher@apple.com) + +Revision 1.1 2004/06/18 04:14:26 rpantos +Move up one level. + Revision 1.1 2004/01/30 03:02:58 bradley NameSpace Provider Tool for installing, removing, list, etc. NameSpace Providers. @@ -43,7 +52,7 @@ NameSpace Provider Tool for installing, removing, list, etc. NameSpace Providers // Prototypes //=========================================================================================================================== -int main( int argc, char *argv[] ); +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 ); @@ -56,6 +65,8 @@ DEBUG_LOCAL WCHAR * CharToWCharString( const char *inCharString, WCHAR *outWCha DEBUG_LOCAL char * GUIDtoString( const GUID *inGUID, char *outString ); DEBUG_LOCAL OSStatus StringToGUID( const char *inCharString, GUID *outGUID ); +DEBUG_LOCAL BOOL gToolQuietMode = FALSE; + //=========================================================================================================================== // main //=========================================================================================================================== @@ -101,6 +112,7 @@ DEBUG_LOCAL void Usage( void ) fprintf( stderr, " -list - Lists Name Space Providers\n" ); fprintf( stderr, " -reorder - Reorders Name Space Providers\n" ); + fprintf( stderr, " -q - Enable quiet mode\n" ); fprintf( stderr, " -h[elp] - Help\n" ); fprintf( stderr, "\n" ); } @@ -206,6 +218,10 @@ DEBUG_LOCAL int ProcessArgs( int argc, char* argv[] ) err = ReorderNameSpaces(); require_noerr( err, exit ); } + else if( strcmp( argv[ i ], "-q" ) == 0 ) + { + gToolQuietMode = TRUE; + } else if( ( strcmp( argv[ i ], "-help" ) == 0 ) || ( strcmp( argv[ i ], "-h" ) == 0 ) ) { @@ -270,7 +286,10 @@ OSStatus InstallNSP( const char *inName, const char *inGUID, const char *inPath WSACleanup(); require_noerr( err, exit ); - fprintf( stderr, "Installed NSP \"%s\" (%s) at %s\n", inName, inGUID, inPath ); + if (!gToolQuietMode) + { + fprintf( stderr, "Installed NSP \"%s\" (%s) at %s\n", inName, inGUID, inPath ); + } exit: if( err != kNoErr ) @@ -304,7 +323,10 @@ DEBUG_LOCAL OSStatus RemoveNSP( const char *inGUID ) WSACleanup(); require_noerr( err, exit ); - fprintf( stderr, "Removed NSP %s\n", inGUID ); + if (!gToolQuietMode) + { + fprintf( stderr, "Removed NSP %s\n", inGUID ); + } exit: if( err != kNoErr ) @@ -338,7 +360,10 @@ DEBUG_LOCAL OSStatus EnableNSP( const char *inGUID, BOOL inEnable ) WSACleanup(); require_noerr( err, exit ); - fprintf( stderr, "Removed NSP %s\n", inGUID ); + if (!gToolQuietMode) + { + fprintf( stderr, "Removed NSP %s\n", inGUID ); + } exit: if( err != kNoErr ) @@ -465,11 +490,11 @@ DEBUG_LOCAL OSStatus ReorderNameSpaces( void ) // Uninstall it then re-install it to move it to the end. - size = strlen( array[ i ].lpszIdentifier ); + size = (DWORD) strlen( array[ i ].lpszIdentifier ); require_action( size < sizeof_array( name ), exit, err = kSizeErr ); CharToWCharString( array[ i ].lpszIdentifier, name ); - size = strlen( "%SystemRoot%\\System32\\mswsock.dll" ); + size = (DWORD) strlen( "%SystemRoot%\\System32\\mswsock.dll" ); require_action( size < sizeof_array( path ), exit, err = kSizeErr ); CharToWCharString( "%SystemRoot%\\System32\\mswsock.dll", path ); diff --git a/mDNSWindows/NSPTool/NSPTool.rc b/mDNSWindows/NSPTool/NSPTool.rc new file mode 100644 index 0000000..b75f621 --- /dev/null +++ b/mDNSWindows/NSPTool/NSPTool.rc @@ -0,0 +1,102 @@ +// 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 + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,5 + PRODUCTVERSION 1,0,0,5 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Apple Computer, Inc." + VALUE "FileDescription", "NSPTool Application" + VALUE "FileVersion", "1, 0, 0, 5" + VALUE "InternalName", "NSPTool" + VALUE "LegalCopyright", "Copyright (C) 2004" + VALUE "OriginalFilename", "NSPTool.exe" + VALUE "ProductName", " NSPTool Application" + VALUE "ProductVersion", "1, 0, 0, 5" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/mDNSWindows/Applications/DLL/dnssd.vcproj b/mDNSWindows/NSPTool/NSPTool.vcproj similarity index 50% rename from mDNSWindows/Applications/DLL/dnssd.vcproj rename to mDNSWindows/NSPTool/NSPTool.vcproj index ad7c1d1..89ca1fb 100644 --- a/mDNSWindows/Applications/DLL/dnssd.vcproj +++ b/mDNSWindows/NSPTool/NSPTool.vcproj @@ -1,156 +1,143 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/Applications/NSPTool/Prefix.h b/mDNSWindows/NSPTool/Prefix.h similarity index 94% rename from mDNSWindows/Applications/NSPTool/Prefix.h rename to mDNSWindows/NSPTool/Prefix.h index c7f01c5..af06d92 100644 --- a/mDNSWindows/Applications/NSPTool/Prefix.h +++ b/mDNSWindows/NSPTool/Prefix.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: Prefix.h,v $ +Revision 1.1 2004/06/18 04:14:26 rpantos +Move up one level. + Revision 1.1 2004/01/30 03:02:58 bradley NameSpace Provider Tool for installing, removing, list, etc. NameSpace Providers. diff --git a/mDNSWindows/NSPTool/resource.h b/mDNSWindows/NSPTool/resource.h new file mode 100644 index 0000000..78c1d91 --- /dev/null +++ b/mDNSWindows/NSPTool/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by NSPTool.rc +// +#define IDS_PROJNAME 100 +#define IDR_WMDMLOGGER 101 +#define IDS_LOG_SEV_INFO 201 +#define IDS_LOG_SEV_WARN 202 +#define IDS_LOG_SEV_ERROR 203 +#define IDS_LOG_DATETIME 204 +#define IDS_LOG_SRCNAME 205 +#define IDS_DEF_LOGFILE 301 +#define IDS_DEF_MAXSIZE 302 +#define IDS_DEF_SHRINKTOSIZE 303 +#define IDS_DEF_LOGENABLED 304 +#define IDS_MUTEX_TIMEOUT 401 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/mDNSWindows/README.txt b/mDNSWindows/README.txt index 5a7b375..8d8ab4b 100644 --- a/mDNSWindows/README.txt +++ b/mDNSWindows/README.txt @@ -54,8 +54,8 @@ 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 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: +Tool.c to make DNSServiceTest.exe, a small Windows command-line tool to do all +the standard DNS-SD stuff on Windows. It has the following features: - Browse for browsing and/or registration domains. - Browse for services. @@ -67,11 +67,11 @@ For example, if you have a Windows machine running a Web server, then you can make it advertise that it is offering HTTP on port 80 with the following command: -rendezvous -rs "Windows Web Server" "_http._tcp." "local." 80 "" +DNSServiceTest -rs "Windows Web Server" "_http._tcp." "local." 80 "" To search for AFP servers, use this: -rendezvous -bs "_afpovertcp._tcp." "local." +DNSServiceTest -bs "_afpovertcp._tcp." "local." You can also do multiple things at once (e.g. register a service and browse for it so one instance of the app can be used for testing). @@ -79,7 +79,7 @@ Multiple instances can also be run on the same machine to discover each other. There is a -help command to show all the commands, their parameters, and some examples of using it. -RendezvousBrowser contains the source code for a graphical browser application +DNSServiceBrowser 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 deleted file mode 100644 index 540d4ef..0000000 --- a/mDNSWindows/RMxClient.c +++ /dev/null @@ -1,1302 +0,0 @@ -/* - * 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 deleted file mode 100644 index 798f9e9..0000000 --- a/mDNSWindows/RMxClient.h +++ /dev/null @@ -1,307 +0,0 @@ -/* - * 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 deleted file mode 100644 index 686ee93..0000000 --- a/mDNSWindows/RMxCommon.c +++ /dev/null @@ -1,1501 +0,0 @@ -/* - * 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 deleted file mode 100644 index a28da74..0000000 --- a/mDNSWindows/RMxCommon.h +++ /dev/null @@ -1,662 +0,0 @@ -/* - * 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 deleted file mode 100644 index 3c28089..0000000 --- a/mDNSWindows/RMxServer.c +++ /dev/null @@ -1,1656 +0,0 @@ -/* - * 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 deleted file mode 100644 index 4b0fb3a..0000000 --- a/mDNSWindows/RMxServer.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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/Applications/SystemService/EventLogMessages.bin b/mDNSWindows/SystemService/EventLogMessages.bin similarity index 100% rename from mDNSWindows/Applications/SystemService/EventLogMessages.bin rename to mDNSWindows/SystemService/EventLogMessages.bin diff --git a/mDNSWindows/SystemService/Firewall.cpp b/mDNSWindows/SystemService/Firewall.cpp new file mode 100755 index 0000000..15eb7f8 --- /dev/null +++ b/mDNSWindows/SystemService/Firewall.cpp @@ -0,0 +1,330 @@ +/* + * 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: Firewall.cpp,v $ +Revision 1.2 2004/09/15 09:39:53 shersche +Retry the method INetFwPolicy::get_CurrentProfile on error + +Revision 1.1 2004/09/13 07:32:31 shersche +Wrapper for Windows Firewall API code + + +*/ + +#define _WIN32_DCOM + +#include "Firewall.h" +#include +#include +#include +#include +#include + + +static const int kMaxTries = 30; +static const int kRetrySleepPeriod = 1 * 1000; // 1 second + + +static OSStatus +mDNSFirewallInitialize(OUT INetFwProfile ** fwProfile) +{ + INetFwMgr * fwMgr = NULL; + INetFwPolicy * fwPolicy = NULL; + int numRetries = 0; + HRESULT err = kNoErr; + + _ASSERT(fwProfile != NULL); + + *fwProfile = NULL; + + // Use COM to get a reference to the firewall settings manager. This + // call will fail on anything other than XP SP2 + + err = CoCreateInstance( __uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&fwMgr ); + require(SUCCEEDED(err), exit); + + // Use the reference to get the local firewall policy + + err = fwMgr->get_LocalPolicy(&fwPolicy); + require(SUCCEEDED(err), exit); + + // Use the reference to get the extant profile. Empirical evidence + // suggests that there is the potential for a race condition when a system + // service whose startup type is automatic calls this method. + // This is true even when the service declares itself to be dependent + // on the firewall service. Re-trying the method will succeed within + // a few seconds. + + do + { + err = fwPolicy->get_CurrentProfile(fwProfile); + + if (err) + { + Sleep(kRetrySleepPeriod); + } + } + while (err && (numRetries++ < kMaxTries)); + + require(SUCCEEDED(err), exit); + + err = kNoErr; + +exit: + + // Release temporary COM objects + + if (fwPolicy != NULL) + { + fwPolicy->Release(); + } + + if (fwMgr != NULL) + { + fwMgr->Release(); + } + + return err; +} + + +static void +mDNSFirewallCleanup + ( + IN INetFwProfile * fwProfile + ) +{ + // Call Release on the COM reference. + + if (fwProfile != NULL) + { + fwProfile->Release(); + } +} + + +static OSStatus +mDNSFirewallAppIsEnabled + ( + IN INetFwProfile * fwProfile, + IN const wchar_t * fwProcessImageFileName, + OUT BOOL * fwAppEnabled + ) +{ + BSTR fwBstrProcessImageFileName = NULL; + VARIANT_BOOL fwEnabled; + INetFwAuthorizedApplication * fwApp = NULL; + INetFwAuthorizedApplications* fwApps = NULL; + OSStatus err = kNoErr; + + _ASSERT(fwProfile != NULL); + _ASSERT(fwProcessImageFileName != NULL); + _ASSERT(fwAppEnabled != NULL); + + *fwAppEnabled = FALSE; + + // Get the list of authorized applications + + err = fwProfile->get_AuthorizedApplications(&fwApps); + require(SUCCEEDED(err), exit); + + fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName); + require_action(SysStringLen(fwBstrProcessImageFileName) > 0, exit, err = kNoMemoryErr); + + // Look for us + + err = fwApps->Item(fwBstrProcessImageFileName, &fwApp); + + if (SUCCEEDED(err)) + { + // It's listed, but is it enabled? + + err = fwApp->get_Enabled(&fwEnabled); + require(SUCCEEDED(err), exit); + + if (fwEnabled != VARIANT_FALSE) + { + // Yes, it's enabled + + *fwAppEnabled = TRUE; + } + } + + err = kNoErr; + +exit: + + // Deallocate the BSTR + + SysFreeString(fwBstrProcessImageFileName); + + // Release the COM objects + + if (fwApp != NULL) + { + fwApp->Release(); + } + + if (fwApps != NULL) + { + fwApps->Release(); + } + + return err; +} + + +static OSStatus +mDNSFirewallAddApp + ( + IN INetFwProfile * fwProfile, + IN const wchar_t * fwProcessImageFileName, + IN const wchar_t * fwName + ) +{ + BOOL fwAppEnabled; + BSTR fwBstrName = NULL; + BSTR fwBstrProcessImageFileName = NULL; + INetFwAuthorizedApplication * fwApp = NULL; + INetFwAuthorizedApplications* fwApps = NULL; + OSStatus err = S_OK; + + _ASSERT(fwProfile != NULL); + _ASSERT(fwProcessImageFileName != NULL); + _ASSERT(fwName != NULL); + + // First check to see if the application is already authorized. + err = mDNSFirewallAppIsEnabled( fwProfile, fwProcessImageFileName, &fwAppEnabled ); + require_noerr(err, exit); + + // Only add the application if it isn't enabled + + if (!fwAppEnabled) + { + // Get the list of authorized applications + + err = fwProfile->get_AuthorizedApplications(&fwApps); + require(SUCCEEDED(err), exit); + + // Create an instance of an authorized application. + + err = CoCreateInstance( __uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&fwApp ); + require(SUCCEEDED(err), exit); + + fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName); + require_action(SysStringLen(fwBstrProcessImageFileName) > 0, exit, err = kNoMemoryErr); + + // Set the executable file name + + err = fwApp->put_ProcessImageFileName(fwBstrProcessImageFileName); + require(SUCCEEDED(err), exit); + + fwBstrName = SysAllocString(fwName); + require_action(SysStringLen(fwBstrName) > 0, exit, err = kNoMemoryErr); + + // Set the friendly name + + err = fwApp->put_Name(fwBstrName); + require(SUCCEEDED(err), exit); + + // Now add the application + + err = fwApps->Add(fwApp); + require(SUCCEEDED(err), exit); + } + + err = kNoErr; + +exit: + + // Deallocate the BSTR objects + + SysFreeString(fwBstrName); + SysFreeString(fwBstrProcessImageFileName); + + // Release the COM objects + + if (fwApp != NULL) + { + fwApp->Release(); + } + + if (fwApps != NULL) + { + fwApps->Release(); + } + + return err; +} + + +OSStatus +mDNSAddToFirewall + ( + LPWSTR executable, + LPWSTR name + ) +{ + INetFwProfile * fwProfile = NULL; + HRESULT comInit = E_FAIL; + OSStatus err = kNoErr; + + // Initialize COM. + + comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE ); + + // Ignore this case. RPC_E_CHANGED_MODE means that COM has already been + // initialized with a different mode. + + if (comInit != RPC_E_CHANGED_MODE) + { + err = comInit; + require(SUCCEEDED(err), exit); + } + + // Connect to the firewall + + err = mDNSFirewallInitialize(&fwProfile); + require_noerr(err, exit); + + // Add us to the list of exempt programs + + err = mDNSFirewallAddApp( fwProfile, executable, name ); + require_noerr(err, exit); + +exit: + + // Disconnect from the firewall + + mDNSFirewallCleanup(fwProfile); + + // De-initialize COM + + if (SUCCEEDED(comInit)) + { + CoUninitialize(); + } + + return err; +} diff --git a/mDNSWindows/SystemService/Firewall.h b/mDNSWindows/SystemService/Firewall.h new file mode 100755 index 0000000..ac2dfad --- /dev/null +++ b/mDNSWindows/SystemService/Firewall.h @@ -0,0 +1,59 @@ +/* + * 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: Firewall.h,v $ +Revision 1.1 2004/09/13 07:32:31 shersche +Wrapper for Windows Firewall API code + + +*/ + +#ifndef _Firewall_h +#define _Firewall_h + + +#include "CommonServices.h" +#include "DebugServices.h" + + +#if defined(__cplusplus) +extern "C" +{ +#endif + + +OSStatus +mDNSAddToFirewall + ( + LPWSTR executable, + LPWSTR name + ); + + +#if defined(__cplusplus) +} +#endif + + +#endif diff --git a/mDNSWindows/Applications/SystemService/Prefix.h b/mDNSWindows/SystemService/Prefix.h similarity index 95% rename from mDNSWindows/Applications/SystemService/Prefix.h rename to mDNSWindows/SystemService/Prefix.h index 844b96e..59d18e8 100644 --- a/mDNSWindows/Applications/SystemService/Prefix.h +++ b/mDNSWindows/SystemService/Prefix.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: Prefix.h,v $ +Revision 1.1 2004/06/18 04:16:41 rpantos +Move up one level. + 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. diff --git a/mDNSWindows/SystemService/Service.aps b/mDNSWindows/SystemService/Service.aps new file mode 100644 index 0000000000000000000000000000000000000000..41f25562d4a630c130f960887f21e59e4da28ead GIT binary patch literal 3388 zcmbtW&u`mQ9RHeiV`Yq0li-HrjUHBsW=`8}P>@h+yQ?9!D?80rDN@L6x3!kUk)5pc z!ih6~0e=Dq4jkc*IPpJlLE?bKffFKpzVF3}9XFi@iR|~@_kDkUzTfZjBO+4qjK}i9 zwD8-(Y8B5OJt3Zt$I4`Xd1*4C;0BSu-3pTPAoicPdWP+}VLFKWL70fulc*oWXKBkj zPlsX83zHX-AGRiACGlAi)E4dhB-Cp(vvWsK?obk@@hI`b$|*}(hS&B?w`u8S+b})d zwGJAVV^dYr=o(SGcHp&b^J$~)nx1p$f~#4pADY;&(wq5x-Pzx-*~VmhOVhw`v&djK zT&F&jhk>{9``*#MnTfOZTSS@5XHg$s3x}1a*KV4w2Ory(z3WhwZj)jedfRK%8kSD? z)~jo@Mv9Q-_pite|Ng{N#OP;1&tp`52z~Pw{$Fz5h0*c20SGNyuOAwKPMDpiZd^#+ zoKhdsBQejv79}spGhIAq^ZXEf4Td5v_fZ!_+RChU{sRQLGSjzP2T6RAbb9J;)D6?t ze&i={8XphUW|WRPUDX?of+$uiw%JfOG;Ooh^dGf4!*1``Z$*8-I|{=4(;|Rxk4rwU zPknxkoyYIOT+ZbozeED^OCT>H{|K6k_*6=^R?tVzXUHse=F0~}1#|^0dd=3&I&CnU zWz=>Yw?Uh7;hGKih_>XydTcwcNe|@e$n@Zf5V0Lpftcxa$1~{zx!5!72lSzwFK%b- zD|GepmIiLKmY?s)!lv$&+Ieojd>()y*Qx6QE?T`pq+(}0UcrOl@XPn)>Xj`}n&eR% zZCf^v6HqAVic)f+ELH#GC|8YM&4=e1&3Bpl?VCMbxJgXp5*z zAvE{tm}2mJE_gKLMgzt?KEs>nQ+gY+I(T|Cpbq%Y;Y|mtP#CCzIuN?lDc1;=LfCpA z5wiuKDuUO*QK(F98{kQ?$36`p5y3`Z@b$=m4G$PF$3V~np^>ZgMovdlP}7`jRan(> zZ^o7AV_}=Uk-qg2LpK-6Wj4R0b=a)K)`^f}GzP$dJwKsSXe1GPk*3l;P}hLL29VUy z0_&667HF@6Tb&PoI?D<$IHZS-qQ`%y)0T0X=Bvm-xaQ#U5wDztEg2=;QGRuuIJ3`0Z>5kq71#xICdLpH?ui$uK_4S?FmeIL6uCVHdNa-x97%z92p*Q; zUgh4FX!{tu_@86X2c1s`uJIA1+2Rmhgy3dHxSy`Td949gXEU-@x)>oi8IJ^ zT(Q7HN^|@pXy;fYx+$zB0vE=rk6lKJvF2z;@Lq)mj)UXuL1$n1#b@rUlI1y= Fix a race condition between the socket thread and the main processing thread that resulted in the socket thread accessing a previously deleted Win32EventSource object. +Bug #: 3838237 + +Revision 1.19 2004/10/12 17:59:55 shersche + Disable routing table modifications when Nortel VPN adapter is active +Bug #: 3718122 + +Revision 1.18 2004/10/11 21:57:50 shersche + The SharedAccess service dependency causes a circular dependency on Windows Server 2003. Only add the SharedAccess service dependency if running on XP. All other platforms do not manipulate the firewall and thus are not dependent on it. +Bug #: 3832450 + +Revision 1.17 2004/09/17 01:08:58 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.16 2004/09/16 18:49:34 shersche +Remove the XP SP2 check before attempting to manage the firewall. There is a race condition in the SP2 updater such that upon first reboot after the upgrade, mDNSResponder might not know that it is running under SP2 yet. This necessitates a second reboot before the firewall is managed. Removing the check will cause mDNSResponder to try and manage the firewall everytime it boots up, if and only if it hasn't managed the firewall a previous time. + +Revision 1.15 2004/09/15 17:13:33 shersche +Change Firewall name from "Apple mDNSResponder" to "Rendezvous" + +Revision 1.14 2004/09/15 09:37:25 shersche +Add SharedAccess to dependency list, call CheckFirewall after sending status back to SCM + +Revision 1.13 2004/09/13 07:35:10 shersche + Add mDNSResponder to Windows Firewall application list if SP2 is detected and app hasn't been added before +Bug #: 3762235 + +Revision 1.12 2004/09/11 21:18:32 shersche + Add route to ARP everything when a 169.254.x.x address is selected +Bug #: 3779502 + +Revision 1.11 2004/09/11 05:39:19 shersche + Detect power managment state changes, calling mDNSCoreMachineSleep(m, true) on sleep, and mDNSCoreMachineSleep(m, false) on resume +Bug #: 3780203 + +Revision 1.10 2004/08/16 21:45:24 shersche +Use the full pathname of executable when calling CreateService() +Submitted by: prepin@zetron.com + +Revision 1.9 2004/08/11 01:59:41 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init() + +Revision 1.8 2004/08/05 05:40:05 shersche + Only invoke SetConsoleCtrlHandler when running directly from command line. + Invoke udsserver_handle_configchange() when the computer description changes +Bug #: 3751481, 3751566 + +Revision 1.7 2004/07/26 05:35:07 shersche +ignore non-enet interfaces when setting up link-local routing + +Revision 1.6 2004/07/20 06:48:26 shersche + Allow registry entries to dictate whether to manage link local routing +Bug #: 3718122 + +Revision 1.5 2004/07/09 19:08:07 shersche + ServiceSetupEventLogging() errors are handled gracefully +Bug #: 3713762 + +Revision 1.4 2004/06/24 20:58:15 shersche +Fix compiler error in Release build +Submitted by: herscher + +Revision 1.3 2004/06/24 15:28:53 shersche +Automatically setup routes to link-local addresses upon interface list change events. +Submitted by: herscher + +Revision 1.2 2004/06/23 16:56:00 shersche + locked call to udsserver_idle(). +Bug #: 3697326 +Submitted by: herscher + +Revision 1.1 2004/06/18 04:16:41 rpantos +Move up one level. + +Revision 1.1 2004/01/30 02:58:39 bradley +mDNSResponder Windows Service. Provides global Rendezvous support with an IPC interface. + +*/ + +#include +#include + + +#include "CommonServices.h" +#include "DebugServices.h" + +#include "uds_daemon.h" +#include "GenLinkedList.h" + +#include "Resource.h" + +#include "mDNSEmbeddedAPI.h" +#include "mDNSWin32.h" + +#include "Firewall.h" + +#if( !TARGET_OS_WINDOWS_CE ) + #include + #include + #include + #include + #include +#endif + +#if 0 +#pragma mark == Constants == +#endif + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define DEBUG_NAME "[Server] " +#define kServiceName "Apple mDNSResponder" +#define kServiceFirewallName L"Rendezvous" +#define kServiceDependencies "Tcpip\0winmgmt\0\0" +#define kServiceManageLLRouting "ManageLLRouting" +#define kServiceCacheEntryCount "CacheEntryCount" +#define kServiceManageFirewall "ManageFirewall" +#define kDNSServiceCacheEntryCountDefault 512 + +#define RR_CACHE_SIZE 500 +static CacheRecord gRRCache[RR_CACHE_SIZE]; +#if 0 +#pragma mark == Structures == +#endif + +//=========================================================================================================================== +// Structures +//=========================================================================================================================== +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef EventSourceFlags + + @abstract Session flags. + + @constant EventSourceFlagsNone No flags. + @constant EventSourceFlagsThreadDone Thread is no longer active. + @constant EventSourceFlagsNoClose Do not close the session when the thread exits. + @constant EventSourceFinalized Finalize has been called for this session +*/ + +typedef uint32_t EventSourceFlags; + +#define EventSourceFlagsNone 0 +#define EventSourceFlagsThreadDone ( 1 << 2 ) +#define EventSourceFlagsNoClose ( 1 << 3 ) +#define EventSourceFinalized ( 1 << 4 ) + + +typedef struct Win32EventSource +{ + EventSourceFlags flags; + HANDLE threadHandle; + unsigned threadID; + HANDLE socketEvent; + HANDLE closeEvent; + udsEventCallback callback; + void * context; + DWORD waitCount; + HANDLE waitList[2]; + SOCKET sock; + struct Win32EventSource * next; +} Win32EventSource; + + +#if 0 +#pragma mark == Prototypes == +#endif + +//=========================================================================================================================== +// Prototypes +//=========================================================================================================================== + +int __cdecl 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 SetServiceParameters(); +static OSStatus GetServiceParameters(); +static OSStatus CheckFirewall(); +static OSStatus SetServiceInfo( 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 DWORD WINAPI ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext ); + +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[] ); +static mStatus EventSourceFinalize(Win32EventSource * source); +static void EventSourceLock(); +static void EventSourceUnlock(); +static mDNSs32 udsIdle(mDNS * const inMDNS, mDNSs32 interval); +static void CoreCallback(mDNS * const inMDNS, mStatus result); +static void HostDescriptionChanged(mDNS * const inMDNS); +static OSStatus GetRouteDestination(DWORD * ifIndex, DWORD * address); +static bool HaveLLRoute(PMIB_IPFORWARDROW rowExtant); +static OSStatus SetLLRoute(); + +#define kLLNetworkAddr "169.254.0.0" +#define kLLNetworkAddrMask "255.255.0.0" + + +#include "mDNSEmbeddedAPI.h" + +#if 0 +#pragma mark == Globals == +#endif + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== +#define gMDNSRecord mDNSStorage +DEBUG_LOCAL mDNS_PlatformSupport gPlatformStorage; +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. +DEBUG_LOCAL bool gServiceManageLLRouting = true; +DEBUG_LOCAL int gWaitCount = 0; +DEBUG_LOCAL HANDLE * gWaitList = NULL; +DEBUG_LOCAL HANDLE gStopEvent = NULL; +DEBUG_LOCAL CRITICAL_SECTION gEventSourceLock; +DEBUG_LOCAL GenLinkedList gEventSources; + + +#if 0 +#pragma mark - +#endif + +//=========================================================================================================================== +// main +//=========================================================================================================================== + +int __cdecl main( int argc, char *argv[] ) +{ + OSStatus err; + BOOL ok; + BOOL start; + int i; + + debug_initialize( kDebugOutputTypeMetaConsole ); + debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose ); + + // 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, fullPath, NULL, NULL, kServiceDependencies, + NULL, NULL ); + err = translate_errno( service, (OSStatus) GetLastError(), kDuplicateErr ); + require_noerr( err, exit ); + + err = SetServiceParameters(); + check_noerr( err ); + + if( inDescription ) + { + err = SetServiceInfo( 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 ); +} + + + +//=========================================================================================================================== +// SetServiceParameters +//=========================================================================================================================== + +static OSStatus SetServiceParameters() +{ + DWORD value; + DWORD valueLen = sizeof(DWORD); + DWORD type; + const char * s; + OSStatus err; + HKEY key; + + key = NULL; + + // + // Add/Open Parameters section under service entry in registry + // + s = "SYSTEM\\CurrentControlSet\\Services\\" kServiceName "\\Parameters"; + err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key ); + require_noerr( err, exit ); + + // + // If the value isn't already there, then we create it + // + err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen); + + if (err != ERROR_SUCCESS) + { + value = 1; + + err = RegSetValueEx( key, kServiceManageLLRouting, 0, REG_DWORD, (const LPBYTE) &value, sizeof(DWORD) ); + require_noerr( err, exit ); + } + +exit: + + if ( key ) + { + RegCloseKey( key ); + } + + return( err ); +} + + + +//=========================================================================================================================== +// GetServiceParameters +//=========================================================================================================================== + +static OSStatus GetServiceParameters() +{ + DWORD value; + DWORD valueLen; + DWORD type; + const char * s; + OSStatus err; + HKEY key; + + key = NULL; + + // + // Add/Open Parameters section under service entry in registry + // + s = "SYSTEM\\CurrentControlSet\\Services\\" kServiceName "\\Parameters"; + err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key ); + require_noerr( err, exit ); + + valueLen = sizeof(DWORD); + err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen); + if (err == ERROR_SUCCESS) + { + gServiceManageLLRouting = (value) ? true : false; + } + + valueLen = sizeof(DWORD); + err = RegQueryValueEx(key, kServiceCacheEntryCount, 0, &type, (LPBYTE) &value, &valueLen); + if (err == ERROR_SUCCESS) + { + gServiceCacheEntryCount = value; + } + +exit: + + if ( key ) + { + RegCloseKey( key ); + } + + return( err ); +} + + +//=========================================================================================================================== +// CheckFirewall +//=========================================================================================================================== + +static OSStatus CheckFirewall() +{ + DWORD value; + DWORD valueLen; + DWORD type; + const char * s; + HKEY key = NULL; + OSStatus err = kUnknownErr; + + // Check to see if we've managed the firewall. + // This package might have been installed, then + // the OS was upgraded to SP2 or above. If that's + // the case, then we need to manipulate the firewall + // so networking works correctly. + + s = "SYSTEM\\CurrentControlSet\\Services\\" kServiceName "\\Parameters"; + err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key ); + require_noerr( err, exit ); + + valueLen = sizeof(DWORD); + err = RegQueryValueEx(key, kServiceManageFirewall, 0, &type, (LPBYTE) &value, &valueLen); + + if ((err != ERROR_SUCCESS) || (value == 0)) + { + wchar_t fullPath[ MAX_PATH ]; + DWORD size; + + // Get a full path to the executable + + size = GetModuleFileNameW( NULL, fullPath, sizeof( fullPath ) ); + err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr ); + require_noerr( err, exit ); + + err = mDNSAddToFirewall(fullPath, kServiceFirewallName); + require_noerr( err, exit ); + + value = 1; + err = RegSetValueEx( key, kServiceManageFirewall, 0, REG_DWORD, (const LPBYTE) &value, sizeof( DWORD ) ); + require_noerr( err, exit ); + } + +exit: + + if ( key ) + { + RegCloseKey( key ); + } + + return( err ); +} + + + +//=========================================================================================================================== +// SetServiceInfo +//=========================================================================================================================== + +static OSStatus SetServiceInfo( SC_HANDLE inSCM, const char *inServiceName, const char *inDescription ) +{ + OSStatus err; + SC_LOCK lock; + SC_HANDLE service; + SERVICE_DESCRIPTION description; + SERVICE_FAILURE_ACTIONS actions; + SC_ACTION action; + 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|SERVICE_START ); + 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 ); + + actions.dwResetPeriod = INFINITE; + actions.lpRebootMsg = NULL; + actions.lpCommand = NULL; + actions.cActions = 1; + actions.lpsaActions = &action; + action.Delay = 500; + action.Type = SC_ACTION_RESTART; + + ok = ChangeServiceConfig2( service, SERVICE_CONFIG_FAILURE_ACTIONS, &actions ); + 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; + BOOL ok; + + initialized = FALSE; + + // 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 ); + + 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(); + check_noerr( err ); + + err = GetServiceParameters(); + check_noerr( err ); + + // 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|SERVICE_ACCEPT_POWEREVENT; + gServiceStatus.dwWin32ExitCode = NO_ERROR; + gServiceStatus.dwServiceSpecificExitCode = NO_ERROR; + gServiceStatus.dwCheckPoint = 0; + gServiceStatus.dwWaitHint = 0; + + gServiceStatusHandle = RegisterServiceCtrlHandlerEx( argv[ 0 ], ServiceControlHandler, NULL ); + 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 = SetServiceInfo( 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 DWORD WINAPI ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext ) +{ + BOOL setStatus; + BOOL ok; + + DEBUG_UNUSED( inEventData ); + DEBUG_UNUSED( inContext ); + + setStatus = TRUE; + switch( inControl ) + { + case SERVICE_CONTROL_STOP: + dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: SERVICE_CONTROL_STOP\n" ); + + ServiceStop(); + setStatus = FALSE; + break; + + case SERVICE_CONTROL_POWEREVENT: + + if (inEventType == PBT_APMSUSPEND) + { + mDNSCoreMachineSleep(&gMDNSRecord, TRUE); + } + else if (inEventType == PBT_APMRESUMESUSPEND) + { + mDNSCoreMachineSleep(&gMDNSRecord, 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 ); + } + + return NO_ERROR; +} + +//=========================================================================================================================== +// 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 ); + + err = CheckFirewall(); + check_noerr( err ); + + // 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; + + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + memset( &gMDNSRecord, 0, sizeof gMDNSRecord); + memset( &gPlatformStorage, 0, sizeof gPlatformStorage); + + gPlatformStorage.idleThreadCallback = udsIdle; + gPlatformStorage.hostDescriptionChangedCallback = HostDescriptionChanged; + + InitializeCriticalSection(&gEventSourceLock); + + gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + err = translate_errno( gStopEvent, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + err = mDNS_Init( &gMDNSRecord, &gPlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, CoreCallback, mDNS_Init_NoInitCallbackContext); + require_noerr( err, exit); + + err = udsserver_init(); + require_noerr( err, exit); + + // + // set a route to link local addresses (169.254.0.0) + // + if (gServiceManageLLRouting == true) + { + SetLLRoute(); + } + +exit: + if( err != kNoErr ) + { + ServiceSpecificFinalize( argc, argv ); + } + return( err ); +} + +//=========================================================================================================================== +// ServiceSpecificRun +//=========================================================================================================================== + +static OSStatus ServiceSpecificRun( int argc, char *argv[] ) +{ + DWORD result; + + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + // Main event loop. Process connection requests and state changes (i.e. quit). + while( (result = WaitForSingleObject(gStopEvent, INFINITE)) != WAIT_OBJECT_0 ) + { + // Unexpected wait result. + dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result ); + } + + return kNoErr; +} + +//=========================================================================================================================== +// ServiceSpecificStop +//=========================================================================================================================== + +static OSStatus ServiceSpecificStop( void ) +{ + OSStatus err; + BOOL ok; + + ok = SetEvent(gStopEvent); + err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); +exit: + return( err ); +} + +//=========================================================================================================================== +// ServiceSpecificFinalize +//=========================================================================================================================== + +static void ServiceSpecificFinalize( int argc, char *argv[] ) +{ + DEBUG_UNUSED( argc ); + DEBUG_UNUSED( argv ); + + // + // clean up any open sessions + // + while (gEventSources.Head) + { + EventSourceFinalize((Win32EventSource*) gEventSources.Head); + } + // + // give a chance for the udsserver code to clean up + // + udsserver_exit(); + + // + // and finally close down the mDNSCore + // + mDNS_Close(&gMDNSRecord); + + // + // clean up the event sources mutex...no one should be using it now + // + DeleteCriticalSection(&gEventSourceLock); +} + + +static void +CoreCallback(mDNS * const inMDNS, mStatus status) +{ + DEBUG_UNUSED( inMDNS ); + + if (status == mStatus_ConfigChanged) + { + if (gServiceManageLLRouting == true) + { + SetLLRoute(); + } + } +} + + +static mDNSs32 +udsIdle(mDNS * const inMDNS, mDNSs32 interval) +{ + DEBUG_UNUSED( inMDNS ); + + // + // rdar://problem/3697326 + // + // udsserver_idle wasn't being locked. This resulted + // in multiple threads contesting for the all_requests + // data structure in uds_daemon.c + // + mDNSPlatformLock(&gMDNSRecord); + + interval = udsserver_idle(interval); + + mDNSPlatformUnlock(&gMDNSRecord); + + return interval; +} + + +static void +HostDescriptionChanged(mDNS * const inMDNS) +{ + DEBUG_UNUSED( inMDNS ); + + udsserver_handle_configchange(); +} + + +mDNSlocal unsigned WINAPI +udsSocketThread(LPVOID inParam) +{ + Win32EventSource * source = (Win32EventSource*) inParam; + DWORD threadID = GetCurrentThreadId(); + DWORD waitCount; + HANDLE waitList[2]; + bool safeToClose; + bool done; + bool locked = false; + mStatus err = 0; + + waitCount = source->waitCount; + waitList[0] = source->waitList[0]; + waitList[1] = source->waitList[1]; + done = (bool) (source->flags & EventSourceFinalized); + + while (!done) + { + DWORD result; + + result = WaitForMultipleObjects(waitCount, waitList, FALSE, INFINITE); + + mDNSPlatformLock(&gMDNSRecord); + locked = true; + + // + // + // Look up the source by the thread id. This will ensure that the + // source is still extant. It could already have been deleted + // by the processing thread. + // + + EventSourceLock(); + + for (source = gEventSources.Head; source; source = source->next) + { + if (source->threadID == threadID) + { + break; + } + } + + EventSourceUnlock(); + + if (source == NULL) + { + goto exit; + } + + // + // socket event + // + if (result == WAIT_OBJECT_0) + { + source->callback(source->context); + } + // + // close event + // + else if (result == WAIT_OBJECT_0 + 1) + { + // + // this is a bit of a hack. we want to clean up the internal data structures + // so we'll go in here and it will clean up for us + // + shutdown(source->sock, 2); + source->callback(source->context); + + break; + } + else + { + // Unexpected wait result. + dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result ); + goto exit; + } + + done = (bool) (source->flags & EventSourceFinalized); + + mDNSPlatformUnlock(&gMDNSRecord); + locked = false; + } + + EventSourceLock(); + source->flags |= EventSourceFlagsThreadDone; + safeToClose = !( source->flags & EventSourceFlagsNoClose ); + EventSourceUnlock(); + + if( safeToClose ) + { + EventSourceFinalize( source ); + } + +exit: + + if ( locked ) + { + mDNSPlatformUnlock(&gMDNSRecord); + } + + _endthreadex_compat( (unsigned) err ); + return( (unsigned) err ); +} + + +mStatus +udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context) +{ + Win32EventSource * newSource; + DWORD result; + mStatus err; + + newSource = malloc(sizeof(Win32EventSource)); + require_action( newSource, exit, err = mStatus_NoMemoryErr ); + memset(newSource, 0, sizeof(Win32EventSource)); + + newSource->flags = 0; + newSource->sock = (SOCKET) fd; + newSource->callback = callback; + newSource->context = context; + + newSource->socketEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + err = translate_errno( newSource->socketEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + newSource->closeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + err = translate_errno( newSource->closeEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + err = WSAEventSelect(newSource->sock, newSource->socketEvent, FD_ACCEPT|FD_READ|FD_CLOSE); + err = translate_errno( err == 0, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + + newSource->waitCount = 0; + newSource->waitList[ newSource->waitCount++ ] = newSource->socketEvent; + newSource->waitList[ newSource->waitCount++ ] = newSource->closeEvent; + + // + // lock the list + // + EventSourceLock(); + + // add the event source to the end of the list, while checking + // to see if the list needs to be initialized + // + if ( gEventSources.LinkOffset == 0) + { + InitLinkedList( &gEventSources, offsetof( Win32EventSource, next)); + } + + AddToTail( &gEventSources, newSource); + + // + // no longer using the list + // + EventSourceUnlock(); + + // 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. + newSource->threadHandle = (HANDLE) _beginthreadex_compat( NULL, 0, udsSocketThread, newSource, CREATE_SUSPENDED, &newSource->threadID ); + err = translate_errno( newSource->threadHandle, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + result = ResumeThread( newSource->threadHandle ); + err = translate_errno( result != (DWORD) -1, errno_compat(), kNoResourcesErr ); + require_noerr( err, exit ); + +exit: + + if (err && newSource) + { + EventSourceFinalize(newSource); + } + + return err; +} + + +mStatus +udsSupportRemoveFDFromEventLoop( SocketRef fd) +{ + Win32EventSource * source; + mStatus err = mStatus_NoError; + + // + // find the event source + // + EventSourceLock(); + + for (source = gEventSources.Head; source; source = source->next) + { + if (source->sock == (SOCKET) fd) + { + break; + } + } + + // + // if we found him, finalize him + // + if (source != NULL) + { + EventSourceFinalize(source); + } + + // + // done with the list + // + EventSourceUnlock(); + + return err; +} + + +mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) + { + (void)m; + (void)delay; + // No-op, for now + } + + +static mStatus +EventSourceFinalize(Win32EventSource * source) +{ + OSStatus err; + bool locked; + Win32EventSource * inserted; + bool sameThread; + bool deferClose; + BOOL ok; + DWORD threadID; + DWORD result; + + check( source ); + + // Find the session in the list. + + EventSourceLock(); + locked = true; + + for( inserted = (Win32EventSource*) gEventSources.Head; inserted; inserted = inserted->next ) + { + if( inserted == source ) + { + break; + } + } + require_action( inserted, exit, err = kNotFoundErr ); + + // + // note that we've had finalize called + // + source->flags |= EventSourceFinalized; + + // 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 = source->threadHandle && ( threadID == source->threadID ); + if( sameThread && !( source->flags & EventSourceFlagsThreadDone ) ) + { + source->flags &= ~EventSourceFlagsNoClose; + 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 && ( source->flags & EventSourceFlagsThreadDone ) && !( source->flags & EventSourceFlagsNoClose ) ) + { + deferClose = true; + } + + // Signal a close so the thread exits. + + if( source->closeEvent ) + { + ok = SetEvent( source->closeEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + } + if( deferClose ) + { + err = kNoErr; + goto exit; + } + + source->flags |= EventSourceFlagsNoClose; + + // Remove the session from the list. + RemoveFromList(&gEventSources, source); + + EventSourceUnlock(); + locked = false; + + // Wait for the thread to exit. Give up after 3 seconds to handle a hung thread. + + if( source->threadHandle && ( threadID != source->threadID ) ) + { + result = WaitForSingleObject( source->threadHandle, 3 * 1000 ); + check_translated_errno( result == WAIT_OBJECT_0, (OSStatus) GetLastError(), result ); + } + + // Release the thread. + + if( source->threadHandle ) + { + ok = CloseHandle( source->threadHandle ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + source->threadHandle = NULL; + } + + // Release the socket event. + + if( source->socketEvent ) + { + ok = CloseHandle( source->socketEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + source->socketEvent = NULL; + } + + // Release the close event. + + if( source->closeEvent ) + { + ok = CloseHandle( source->closeEvent ); + check_translated_errno( ok, errno_compat(), kUnknownErr ); + source->closeEvent = NULL; + } + + // Release the memory used by the object. + free ( source ); + + err = kNoErr; + + dlog( kDebugLevelNotice, DEBUG_NAME "session closed\n" ); + +exit: + + if( locked ) + { + EventSourceUnlock(); + } + + return( err ); +} + + +static void +EventSourceLock() +{ + EnterCriticalSection(&gEventSourceLock); +} + + +static void +EventSourceUnlock() +{ + LeaveCriticalSection(&gEventSourceLock); +} + + +//=========================================================================================================================== +// HaveLLRoute +//=========================================================================================================================== + +static bool +HaveLLRoute(PMIB_IPFORWARDROW rowExtant) +{ + PMIB_IPFORWARDTABLE pIpForwardTable = NULL; + DWORD dwSize = 0; + BOOL bOrder = FALSE; + OSStatus err; + bool found = false; + unsigned long int i; + + // + // Find out how big our buffer needs to be. + // + err = GetIpForwardTable(NULL, &dwSize, bOrder); + require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr ); + + // + // Allocate the memory for the table + // + pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize ); + require_action( pIpForwardTable, exit, err = kNoMemoryErr ); + + // + // Now get the table. + // + err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder); + require_noerr( err, exit ); + + // + // Search for the row in the table we want. + // + for ( i = 0; i < pIpForwardTable->dwNumEntries; i++) + { + if (pIpForwardTable->table[i].dwForwardDest == inet_addr(kLLNetworkAddr)) + { + memcpy( rowExtant, &(pIpForwardTable->table[i]), sizeof(*rowExtant) ); + found = true; + break; + } + } + +exit: + + if ( pIpForwardTable != NULL ) + { + free(pIpForwardTable); + } + + return found; +} + + +//=========================================================================================================================== +// SetLLRoute +//=========================================================================================================================== + +static OSStatus +SetLLRoute() +{ + DWORD ifIndex; + MIB_IPFORWARDROW rowExtant; + bool addRoute; + MIB_IPFORWARDROW row; + OSStatus err; + + ZeroMemory(&row, sizeof(row)); + + err = GetRouteDestination(&ifIndex, &row.dwForwardNextHop); + require_noerr( err, exit ); + row.dwForwardDest = inet_addr(kLLNetworkAddr); + row.dwForwardIfIndex = ifIndex; + row.dwForwardMask = inet_addr(kLLNetworkAddrMask); + row.dwForwardType = 3; + row.dwForwardProto = MIB_IPPROTO_NETMGMT; + row.dwForwardAge = 0; + row.dwForwardPolicy = 0; + row.dwForwardMetric1 = 30; + row.dwForwardMetric2 = (DWORD) - 1; + row.dwForwardMetric3 = (DWORD) - 1; + row.dwForwardMetric4 = (DWORD) - 1; + row.dwForwardMetric5 = (DWORD) - 1; + + addRoute = true; + + // + // check to make sure we don't already have a route + // + if (HaveLLRoute(&rowExtant)) + { + // + // set the age to 0 so that we can do a memcmp. + // + rowExtant.dwForwardAge = 0; + + // + // check to see if this route is the same as our route + // + if (memcmp(&row, &rowExtant, sizeof(row)) != 0) + { + // + // if it isn't then delete this entry + // + DeleteIpForwardEntry(&rowExtant); + } + else + { + // + // else it is, so we don't want to create another route + // + addRoute = false; + } + } + + if (addRoute && row.dwForwardNextHop) + { + err = CreateIpForwardEntry(&row); + + require_noerr( err, exit ); + } + + // + // see if this address is a link local address + // + if ((row.dwForwardNextHop & 0xFFFF) == row.dwForwardDest) + { + // + // if so, set up a route to ARP everything + // + row.dwForwardDest = 0; + row.dwForwardIfIndex = ifIndex; + row.dwForwardMask = 0; + row.dwForwardType = 3; + row.dwForwardProto = MIB_IPPROTO_NETMGMT; + row.dwForwardAge = 0; + row.dwForwardPolicy = 0; + row.dwForwardMetric1 = 1; + row.dwForwardMetric2 = (DWORD) - 1; + row.dwForwardMetric3 = (DWORD) - 1; + row.dwForwardMetric4 = (DWORD) - 1; + row.dwForwardMetric5 = (DWORD) - 1; + + err = CreateIpForwardEntry(&row); + + require_noerr( err, exit ); + } +exit: + + return ( err ); +} + + +//=========================================================================================================================== +// GetRouteDestination +//=========================================================================================================================== + +static OSStatus +GetRouteDestination(DWORD * ifIndex, DWORD * address) +{ + struct in_addr ia; + IP_ADAPTER_INFO * pAdapterInfo = NULL; + IP_ADAPTER_INFO * pAdapter = NULL; + ULONG bufLen; + OSStatus err; + + // + // GetBestInterface will fail if there is no default gateway + // configured. If that happens, we will just take the first + // interface in the list. MSDN support says there is no surefire + // way to manually determine what the best interface might + // be for a particular network address. + // + ia.s_addr = inet_addr(kLLNetworkAddr); + err = GetBestInterface(*(IPAddr*) &ia, ifIndex); + + if (err) + { + *ifIndex = 0; + } + + // + // Make an initial call to GetAdaptersInfo to get + // the necessary size into the bufLen variable + // + err = GetAdaptersInfo( NULL, &bufLen); + require_action( err == ERROR_BUFFER_OVERFLOW, exit, err = kUnknownErr ); + + pAdapterInfo = (IP_ADAPTER_INFO*) malloc( bufLen ); + require_action( pAdapterInfo, exit, err = kNoMemoryErr ); + + err = GetAdaptersInfo( pAdapterInfo, &bufLen); + require_noerr( err, exit ); + + pAdapter = pAdapterInfo; + err = kUnknownErr; + + // + // + // Look for the Nortel VPN virtual interface. This interface + // is identified by it's unique MAC address: 44-45-53-54-42-00 + // + // If the interface is active (i.e., has a non-zero IP Address), + // then we want to disable routing table modifications. + + while (pAdapter) + { + if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && + (pAdapter->AddressLength == 6) && + (pAdapter->Address[0] == 0x44) && + (pAdapter->Address[1] == 0x45) && + (pAdapter->Address[2] == 0x53) && + (pAdapter->Address[3] == 0x54) && + (pAdapter->Address[4] == 0x42) && + (pAdapter->Address[5] == 0x00) && + (inet_addr( pAdapter->IpAddressList.IpAddress.String ) != 0)) + { + goto exit; + } + + pAdapter = pAdapter->Next; + } + + pAdapter = pAdapterInfo; + err = kUnknownErr; + + while (pAdapter) + { + // + // if we don't have an interface selected, choose the first one + // + if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && (!(*ifIndex) || (pAdapter->Index == (*ifIndex)))) + { + *address = inet_addr( pAdapter->IpAddressList.IpAddress.String ); + *ifIndex = pAdapter->Index; + err = kNoErr; + break; + } + + pAdapter = pAdapter->Next; + } + +exit: + + if ( pAdapterInfo != NULL ) + { + free( pAdapterInfo ); + } + + return( err ); +} + diff --git a/mDNSWindows/SystemService/Service.mcp b/mDNSWindows/SystemService/Service.mcp new file mode 100644 index 0000000000000000000000000000000000000000..ca1556669f00fe404e3383dc0a7055f8f6d7a876 GIT binary patch literal 126095 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/SystemService/resource.h b/mDNSWindows/SystemService/resource.h new file mode 100644 index 0000000..d968af9 --- /dev/null +++ b/mDNSWindows/SystemService/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Service.rc +// + +#define IDS_SERVICE_DESCRIPTION 100 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/mDNSWindows/SystemService/resrc1.h b/mDNSWindows/SystemService/resrc1.h new file mode 100644 index 0000000..54a6524 --- /dev/null +++ b/mDNSWindows/SystemService/resrc1.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Service.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/mDNSWindows/Applications/SystemServiceTest/Prefix.h b/mDNSWindows/SystemServiceTest/Prefix.h similarity index 94% rename from mDNSWindows/Applications/SystemServiceTest/Prefix.h rename to mDNSWindows/SystemServiceTest/Prefix.h index 68e705b..53fb893 100644 --- a/mDNSWindows/Applications/SystemServiceTest/Prefix.h +++ b/mDNSWindows/SystemServiceTest/Prefix.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,9 @@ Change History (most recent first): $Log: Prefix.h,v $ +Revision 1.1 2004/06/18 04:17:43 rpantos +Move up one level. + Revision 1.1 2004/01/30 02:58:57 bradley Test tool for the mDNSResponder Windows service. diff --git a/mDNSWindows/Applications/SystemServiceTest/Tool.c b/mDNSWindows/SystemServiceTest/Tool.c similarity index 99% rename from mDNSWindows/Applications/SystemServiceTest/Tool.c rename to mDNSWindows/SystemServiceTest/Tool.c index 5ab16d9..f4d675a 100644 --- a/mDNSWindows/Applications/SystemServiceTest/Tool.c +++ b/mDNSWindows/SystemServiceTest/Tool.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,12 @@ Change History (most recent first): $Log: Tool.c,v $ +Revision 1.2 2004/07/13 21:24:28 rpantos +Fix for . + +Revision 1.1 2004/06/18 04:17:43 rpantos +Move up one level. + Revision 1.3 2004/04/09 21:03:15 bradley Changed port numbers to use network byte order for consistency with other platforms. @@ -176,7 +180,7 @@ int main( int argc, char *argv[] ) static void Usage( void ) { fprintf( stderr, "\n" ); - fprintf( stderr, "Rendezvous Service Test 1.0d1\n" ); + fprintf( stderr, "DNSServiceTest 1.0d1\n" ); fprintf( stderr, "\n" ); fprintf( stderr, " -server Set Remote Server\n" ); fprintf( stderr, " -cv Check Version\n" ); diff --git a/mDNSWindows/Applications/mdnsNSP/mdnsNSP.mcp b/mDNSWindows/SystemServiceTest/Tool.mcp similarity index 50% rename from mDNSWindows/Applications/mdnsNSP/mdnsNSP.mcp rename to mDNSWindows/SystemServiceTest/Tool.mcp index 96007ece3f0de0b3628107a5859a41336156938b..2f38283556de75bcc00a25ccd1fbb8f14bd0392a 100644 GIT binary patch literal 210145 zcmeHw3t${o)&H5@yqmTu6f6&A6CMq;-SmN2V#S0cZBr7uHldB!&vvufHk)pC*WFDC zNG0-BKt-&8QV=xlzw%W@EC`5zw7jIeN=4o;%hRf$RS{7O{r}FrcV=g^n@!rJ&}Q#V z&YW}4J@@sSd(X_y+?nx(!$Bf)5*g*>JTgGeqrO9Qga`34hQmOT3^n04{~~WR8VE;S zo@ltk?~9vBS;3ouQ?0+XYk6f;xGU=OyPBhZe~c39YM;*^i@7}Bc)MLK@+|j9T?@KG z@j$2FmZRAlUG9&&n*8y2Ahg_`X?7s!cQtw=kv{sb09&b;uHJRV?s# zhU0#hFtDksyW-&~aZ$ZZThkSbhdW(o_|H8j9Bs2#P*YJeX_BiZ+!;Z`qV_Bz0plXn zr=_!-J%A$Q58909c~^S5kWO!?&D9VH*|G3&qU;As>YJ&-8(QAwU2e}H3TyO+0?Yid z_>_6owI+_P`p~j)-vSyJ%&U$>1bj*D7p2VaibuNQeY2Q@Jh6vG6T?1h_$N{ty}tQP zu0}uhXG*VS>8{y9Z_+OIczr9-BTS|~D0w_J0IAJC#~-pa$)?4?@U>5w-zTv)5c9@j z{?69q9mM(O1jDV~plf~v<1cVtQvW#_dtyb35_P!HrIrCwQ$gYqWdr-oipINAMk?p? z)XnbGtF+};$2wC{i-sfqX#8AM8nf@p1^)BWjoU?m&;o`zWk7e&JS7eKLOXx9y>+bW z2XjuU>-PD9cR0izA$b~ba)w>P$tk;pm3Ayrols?p|EaXxg5C90rt>E%5OyfSJX_DuVs?1J9*`)XjskgZb zsYu!>=^R95df!l);ub2?c!Wxx6?SJ}SJ?f4(5DPXp-nr~M2c;ciFmp>pPZ1?+w?Qt z%+P0&3$;1FWkj7suoq=T@~o~zRedHjhFYJwT$yGXSMpRzf=npP$t#jmfnuAqQ&!lg zhmiBwA>_CTkT)1io}#QsnxU*nnV_ufJ3m>MJUv;HG&_^ZQj?Q)Npq7G_NmG7zB5zk zl8^NXl{GI}m6(62$@vf7%ZraCcGO-<@- zHBHcOhlNlCUtDx)Ln>bpbx&SbNI)Y)vNB+Y13*=JT;YTL|C zqcdl>Jt=7%+Y?fTtUb|owBW9)$(wqf-8Zc-9I3P3UJF36&3t=`dHgjiPg8Ef3s@PG zJb|fA@&=}|k4G@ocCV16GY`t#9yUsgYFZCrFQ#9}ZJxqz->27Nnx=IbG9{hgkSW## zx1V35FEX_s4p(vE`qP$qq#(rt>N?C5>y53H|sNm6+Da*wgxY8kv^j zZltzWGF!jCNhYUrP%sISkz=oV~`fbe!W>z0jVx6k!q}_+-$9w2U?P9!U6|?)6FJQtqqs}>Cvhny zD=zNpPHF@Ohk$uQ*s4Q-9+&>Ya9@`ux`@nOR{AKVwC0`DnujCuR3zU2xc%hP? z120nYKfp&x*(e5QLNJcUX0#B=I2C-1lFtGktK<-PiIOh@AE)G>gCC;gN5KzO@~hzE zmHcn;!=&us^uv{00nYtLaLmSLf)L5E1pEjkcYzbVk*~vPWk}JVWmAnwVOv&xw$13?N;KwQXyWorwf|Kh#L5SqMAAF*cUji>zGUI%r zlyi7roup*0bCQy)z$Yu2`#VL+d^3#40Yc6z9{z)n4GXAG1nfvr9C9`g(lFtC=ae$D^ZDYI;a%0$hT8NZ;A^0pMUk&~l zB{M#!DR~R{XO;Xqc$JcOgI7yAZ#4MnO6Gl4qhxM-t&+JtbxQspa2_iNd0)b2ju0u2 z+cQ_m*MRf(a; zJ|{%V|2+5tC9eQ)QZkR9W+kryKTF9Qzxib{~;9kv02IzAI8QAbRKp3+en;#32#_&G;iITZpKUMPW;5R6FEBMcp{37^`O8y%-k9CBx z`PlHBL>PNCHjEv@*csT|EJPYR2mBXGZUMhV$zKA$Rmr?>Z&Nb2<(Epn8=TvOF!l*- zczhs?eG8jAgh(ZM;CCwdIB>=ep`;p{Uki~+7K3wH2qiqvHVctTcwB5zGWYc!CI1He zUM0T<&f@@~gvZ|fLZoqw`2$Lx4$gTH#(fT(2Zc!EI>8x7gmF9%9ugvrV+M#+uf&nh_x{+yD(3C_L);m}@eUSJ6_?;jp32#0c=JZBIN{R=iP z3z5bbfd5{}+^-!<{tWmlN^SywRmnU~cpo5)=l%7%5NSM*oi~)sIK8Q4=6{f~-tGkg zJeJx(UeGeoa*!YNMNlhfDX1O9$IulZ9uJ+M5GV|afOua;K`~GVC=OzO$8+u+P&bIj z>3N_pgU$!70(}K^0jLM$1AP_5b9M>nLeT$$R)f9L!#(*Y(jsP7AItp|&=;NSIfQ|vVK&7BE(6ON7K>48K zK^&Z*6F?I|<)9NmCxIq`CWEGcz6rV<^li|UpzngN09^`N1G)_KJEbO=dPOl9_v1${sPDB-x*43VRce_xFnv^U$EHldrA$Q5F2`s#A zE4>l3i%6^uEXRU8SYo|hFyQlwHBgIj0d{fJAM|@;{>7p#qRZk!F|Vl$AL%klR(?83{+@ z1o)LGtme0N*rNTK8}SQdl>m7Q}Ir%|$dq=IhlgBweCrMarrfR;FBt zW~I7)z@mA%dB4xvVXfd;l*YcEj~{$YVLv|=G!4YZs0t7t2i%~1&^Qnue+ogxpvfQ? zXf$XHXe@}2F?Qzx2Es~YPT8PrQQTNns#EkLuZr$Bb=lV$p}9;iisq67K^ zu|6PuDTC9>di)v30aub(s4=*5(^;nt2! z7Jb&RWHQZ|=8k>{`=`J#)E)GUZPAGu0gZr0KqHV{2#DWg>Xx@VlKFgY1R^84zXWgF zT!ObMM8h#G#m%O0&+pdVBvEZc3T{x4%v;sF;(1uug}MeYCl#~HES2R zq2h-Z)%32h#ScR~Vv9Rqzr_|e5bxzU=%Wdhw{0ph3`Zs!g}WH4e}~bmF*`n*hU!{B zQUnUm`m%B55Ms$HE>R%S<&U8ZA_reUOf-#6}A1N67 zz4Bbc3P94i-|N#AXaq7LaF{wkeRRjEOs&&t8i5ZF0X+vkJgjw18Uc;Kfr&u!St)x( zCi%2AU->aZ*858f0ON{pGD?lOmN=y*h%YCZ5M>t^?*_-0ruO*mUqi-B z*yxWg_q*`v-~+mU;^o)-qlKSEsP=do2#*2fdEL?20Pii>A8i<*%>CB)lzQPJQ*>FT z;13e$ki`h(w9IL!Z40dMb%djy8c)+A^{hhzl7eHF@~1cOO^x-G+gNudzFk4nJs$By z+-VB}ZT#}wd0u~yO0SN4Evy!6nP0VO(nnwdL1^ zmIni|_A%Ci9G7Z|_~ul?`=#_!Ric?jAWIPF`>0ZubX#{*BVZxm5MPknwHAMpq{ z+#~U#(%^TSQ9XEDZK)9$aR~4iJ2Ql#o=YShV)Joc;gw&W%}`~=r2TnbVO2cTYp_Y3 zwo1(^L0`kns@U#KtVQjb}#yhZ#Lz6-Qi64x~)4RqFAE`m%2cF z9i?Rpc;=wbJ@hq=%p*jn4iy1|XQ(V>pt(HMs&syhzyXZ_--qBYIiLE|1GsztRANRFabk?5Pgs*W$!l5?kIfidjw1v-!RWzL& zi~BoO#m#<{F{?tAj#dt;amICreVKQ=ah;199~nJroq~1KS|()QcKpPe`L%TuT@!1o zo2$V*^)<~Cvk3-A3C3NxE9&$2_sZ%5eh+iqTnDc2!2^bDV zYB{x731gO>Tm@$^gMG;jz9ed@_nFI8D*K$>o-xrCp;cdtuv-j9J~C<5yWy3Fr_|A*dKs1j2_T zC=WCq=&>&=ry1Yhg%T~VX`%`n{G%SY$|_!mZlrfrFk)xw9E2SgkTVE7ZcA=IcHAZr zAml>G8-yKu!hE(vKtHS^IBrECkG)e|xhXXIh z>cIUS2fIW1DbH#wghOC==pgJ4h240zt4bZu_VRdc95zsSu+d@UBf#T=N2>5$;KJvC z3ts^)<~X>Ro8V$jfs45SF2*o8kG;JOJPuvl0MK}Dg&3q`B}0q2O?g$^CVU+TvJM&n zF*x=h016!I$l-P`mj8TSUxWAAXpFKF0rnkgT=3Hia$IC361vlN1Z?9X7e^6?_z>-s z?wO~!nz|y9a5TPHeUx{xJ(re8Is)dnsN(aVwBw>7&>Hnd1OAvE86$l#*!pK17fv1* zjZOS0y$;!qfNfmlh+igI4#<>gSq^~izD7VJAQ8Y7UU6RS;`8c;KxoBYzg1QvsS!A+ z5oivFg9mj7hT3?2jdrLAj+p#>uFO~M2XRa|q`9u8+3m~R^~Oc6nu?l9llbtG&Qc_% zOR(u=J(DKcR4L-{)swkH!&eV4vg5zq)|1hNeQ@taKD@^(iu zpU;gzY()2$;J4(GMz1d#j=3|Q(2hU=&?WKYg!+vxd#G2}jm$m0*S+FJ(w*KwXs~fAK)?yh0ar3u;;|%gihG~=Y^a-cmAmGA+`{YhDkwhH3I!3a7-1a@se;}=FO9V z$Kdgx6G4AdoOSj%r?ZajIUncG104q9Jja4Q0Xj~_nWu}h9out0&d+o>h|4_^#E(TV zA55I{nPqT!oQHM1oc&`e&OBWl>Z}jxFz$;f?<=-5X)tkf-g8N!;PMjzF=J5K5?Mx`lq;T578@Yx9C;noJvyimI84pa%y{|4-lV@%_MdHhbdfX^EbgwtGDwua1rWoZMAfJQ(gpb^jrXaqC@ z8Uc-fMnEH=5zq)|1U`HOa8G>fJnu@cxW40$xjVekzKw8s>d*F^y{J*>7z#!=Ylk1qGS=8JFh-atR28|mk?iEg5s z=@)bh-AcF7FX?vr72QF1(p~gxx|=rB7P^P-rTgf9dVqd|2Q;?QL-a5`LXXm8w2dC8 z?etrEf__I&(o^&_JwwmZbM!pDKrhlu^fLXPcF-&ID!oRp(;M_A{egDUAL&o@XL^g? zroYf%v7GvE^mqCPy+i+`cj;gB9{rpCL;t1S^gew+VofrA(U@RNpq0ifV;1!otBuvP z+Sq7pq|1!m#%{XY;c~d>TIWpXO!|ql$Js-@&JE5DbhC3OzU+5*PH|2#ZO`%Kc&zUZ z<1wH-uR9ux`hIlMB^ULGrPO5n=%k)Y#f~>7ljmDR9u-?l?TEF@n4^Sx5bG%xmd-0_ z?S(|O$lE=d60%C&O`B|~mMl|q+GwjS)sjm}s0x22)JEO>0D!EQ7Sl}BJJUtfYi#0g zP9X10<402?SpR)L&`M~Q!sdS9USh5s-xLaT3CbUEVZ9d$9bDmXTahi}-99Gm(5{^fV7q@(cc2S5CQ z^+c?n!7GbkB;>V177rE8xR>SP`cN|%WLY$fYYm~c{JPL`e3rC* zjI|)g#VzSA53J$m4ierkrI)G_%`^g8f`ER9OqT3u-OYXxaEPxU;b72bGB|8Ga`3Yu z#emZ-BXJ%n-pOP?pUjtMmv)t{)WM5Omc-)TA-&9N@O7oPly`MRN0NGjRnbFk8h^>9 zuG{bHihEmw;?y908!~A5sNPuDQU{YNX@(R2VG34%6Kxbfn)bSQ{aZtSF?_EF&B)5& zaF4``N`v1=M)lxrwWUU2#38_6Va*VRdM=T4h|R}&g?G@O{7&1d{drztRVc5)TG720 zk~+QDxkp3>eGPL&?8bfWM7Ez->@#pWqefsPAi%G7(jgTA+z-w5y2IjLcWSE8Sa(E3 z%;>?TE)ZWwY1smvIp}i_eN7|t2+^rSMZn-0DhnBCE)TUTonIqxKqJ8SA^1zqQ_mm1 z3;YLkhch-#-F_;X8`~o7I%DyqCoGDlz3nmhRh`yN3|Iv1Wx`^qF{h73(hEFb5iwkS zEKmzpm8noP?X69sf zYVnz`L6<8n-ddBcmi(KM%C8ee8E^Hbv-mvc&sP>(j5{DD_qOc9|A9`N%c6|!Y04$@fpcY37(cOQer?@E*TmZD=4voceND4~E?Yowl;A6H z;jXC9@4;a*(4G1Xlmc>E!$+ZFQyAiSD2|DoC<5iuR+t->$20+j4NqeSS5U>jLyYup3d#6K2?N`40Xc)P|tN5zr4_{ZKLpJAQk{xIwfJ2VFX(pYp86 zqF(;^#i4_+!PVMFKm_KZw@?fKbM!-aX#}7u9xDF%y5@g{|z}XM*9OwDV zbCc&C&nccSJQsMJ^BCswxi5jotBWfK9m*Rqn7L#Q;#`$-s>GNlhh9q}P|5lYkC8nH zU`~t~Jub3-L`VS4aZ$zYB>kzJMoJ@~5zq)^9|AaoJ2M>eXJ5y4k2L}k0dFwr27C6w zV(>349W(+80bBtb#4*8WZ1hC;H)d5_L4mwGwZH4qY=sPyEuCfosg=*ExiW3 z+Y{G|F61|ON}KcPTDu1C^lU>(HXCS~X|-J=O5Tvr6!b!KCEbwF96G_nZxQXG4!lJ4 z(&2iGD(RpRpkt~SgmN;*8@+18ZIol#m~5Cc;C9xuka&CVROmNSxpBL3J54oq8at`V z*zH(KGaV5}giso8G_GZP%!;|p@p>{bJ1(T0ogBxl@+$n<<^(hm{6TYZ4;{npQFfDQ zGAvIPe{4~XIj|A$@T7cUH@bxFq@1%pAv+eZtjcnzvdWRWPYsyCCsQqJvp$&Ly*(S3 zzVqk!Xuh; z3@(TjR$Q139-BD0LPnewug*&ZXF}AHM&Khr;IQO{kRL${bd?%`0|NnlbUHBhgKmXJ zKqK(cA&`7lYM#O$p&FDpgUxz>X#rrI`d^@9G~!y~6!C4j(#lusqPj!5h zE%}J*c8qCUFt0j-S6KMG@jy7ug=K5Vd_*nO)oKJZ0vZ90fJQ(gpb^jrXaqC@8Uc-f zMnEHw2?5*_A3G1<+7{P${4sZjH{}V3v+F&s#zkj)L^1N0zWH8)OeO2I5skp2>IG+y z=nm@6X#_qD1lXf?#yVC8{O5ca)#*Ak0tYSvonBu!)_vf%?*M9+vsuHPQRrm=y&uaJw;E`GxRJyN6*s>^dh}P zFVpX72faeC(rffOy+Lo%A8052k^V$~rnl&A`V0M)cG2JH@AMCPhyF?L(!c0E`ZxWD z{!6>*efogJn&jbn>Vv<;(%|t_^SdDP*cENE6>Lh26*1VeA%-sJuA2RU4dD-+Y_D6ZJEj+0ZHJ+wLv6a3~`{N73 zwBVSf#q%H?P3tMQvF^;XAW!#r!mZ$^EeNzVfY0;#7Wd3T8NgBm`P?KmQB8V(kM}=m9lrjk|jg^ zu>9WDmufB7M~jXm^#&`Whuk#0nV#yp{l2caw>2nE4N~hLK*LA%#=4d|m{m#B!9TP) zK+$NN3W+wlqp_&ZR1cbKZ>zoSpa;vyG|cwz+#A&+bHD0dX#@s^0Dlo9Lm2A0MA9KP zABS%5-hHpEZ`1bYd4*M%ybfzc_gYBm^j_y45gGKAj1jRL_qh|E82E=4#NTptaD=1-}J!7YKFVi*oNPs1Gd*yYcdoSe%f~HiEbeQyzfdyHVMe zgU=K|=1`wMBhSc@e|=NZ#f{UkCNRTaPD|=vvVGp?5`T`*W%|>r*hrs&eK_9cfUPAS z3r5hmU*7(!JMO&e*LQE;a?ic@-T%OE9^CrS!;d`r*tW;F|MrRBJ^9qr&pi9w^Dn&k z(#yZ!@ye^Oz5d3Vf7toQKmGZwxBv3jU4Q%gKi>K0yZ?Ic-~ajV?)N{~Q=@unTy<89 zaa9Xw5$J6Aw=aSggPN}eU1{2xz?gCPvFFp#1v0=VKK8sR+wweX=CSEsfbNyt;%%d3 z+P0<4xjxgztXpsgdDJ=u>!x*5dKN{-Ppp|=TQ|`)v9`Lo8q8B)(>yVY`kz>u(r>2= zGJgHnz>y6rU(DP>z{ccCU&WkHL$Kb$fr|m=f*1Q@n*PV4Jq2G`m#9SP!oPtxR z^Qv%p6&m$Bj16_jW(0VQ>_Gr?^0;uj7t8<7+B2JK!qI(O-I&dYj*PzB#s#l-mE&S$ zbWQhS2ng85MJ|pa4)O8ADcv(qaW!>CBH?I!vHFnX;vrO`vmSH^nB$^KUhPgZE*b)@ zQExQhkLi(d(CyuQZ?J7#IN4t`HmTe8`(C8ZIRX%{jfV`_RjOUGGDbH z#4+KJ=DL<75s;Q`%G>H!{sj0YA&xz?0Y&uy_X0zd|Cv%5}uO42M(SI1m zdNNitZ1uGHm-XM9;YktWpgnV0L+ual)RU+uBWieOG0L#%ZS`iaqB@RWPg0^nP%$n)f4I3pSW558Q@hG=nq`wf|^Wi87NKQ7WHQw2V99i z=Bn@H>)rzum?7PbcUWdf9&(aLmEaBrS~C@fE3~RJLzqCQz#VSw$W+h#){fz+=fl`P z1>zk!nYyFXGy)m{jetfV3lR{%$)q+z5n6bbkqcE-q>G`l8{OJL3uMhy!3N zjruH!mA2|Py6mA|T{q(P^uBe7*JO9%d*l0-dxYdtE76OpAMD{CxtgPX{H_ugc>S$i z%PXsWK7TBRpYHK?ySkPWm;0lx1zn+dpwn;5(d@-L@#C&0e>@%tEw^Wy9SHhejowJ4 zkN)(oK(NhK*By`Ir+uPos}5PBEoFhfGaTpFHKP$WwfJ~?TvTt<)^x?<;ZD~X{&UX> zN89Wb$m?uF`XxF-MuF}I9aF_1xaK#xT3nuJxWn&@+Xj)xQ-hJ~Yo9XTyyce|r?#|| zk;-YFy4ih7N?U$)tP|5L^W)L;0-PB@-A0qB6p)V6j#{}^ zV#c7dSvf|akSm^PlMl8TdS%rJ+`|V~mkv{NGs`#wYnpfJP&(B~B6Fwzn|KDna~0wmy@VRb7{zb&Qce zu!8}gHy#M5xv*>vnJh#{cUdE#5zq)|1T+E~0gZr0KqH_L&pT9KyThCE)VIr1f41lBMUA2}@+z8n)$G9UpKjrPLZEhbL-V-$P~0C4VO3mRcO;1S z_N9!S0V>zmjcBM44uG&uJ{S<_jCHIG_|G{QnyTBV5!inSbb5W^Soi)be*~56l*{eR z9yK-A)tj>B*@-}VJRYg6sOZ4?ygSwva{I!a6_IGTt;-jWRdlqisF>!SR)OcfV-?;= zpkf4Kgg2BIHu~|Lh6_u7(Z3t$XLKX|oHo%-bTj>eZlPQ0Hu@#qPQRi%=uWzeeoc4N zX4*pc(7kjY-A@nDZ|FhVN)OS)^awplkI^=I9A6#!Ej>ZMqbKPpdYYc0XX!b5o?f6A z=_PuZeos5-6?&Ckqu1#TdXxS@JL!+~C;BtJMQ_tz=&!Vk{ziYNf6zPhPkNXBMeotS z=|A*e+D-4%2PD=c(-(~i#spev%ra(CkFnZVO{`Ifbk3xo zID4Eu)a%^f+(0)wcRF{{-8scM#k4)gljE_zQ;Elb^5i$OuZmuBQ4bx?``frG+HO@ud+A@5A%s_w`H!oJyU(=3I}{lLA%h#PVK ztOi>Wi4_a7f4xSp{_2zd>XR4@*}TbVnbT0)7Fgly2uD3No~A|WS%(BB1;;Ec4kLOr zt*6|^x--v$Jl*37w}PLxAkfwTKF{lq^K1DRuJlJ^D52H2EV>-=^NzY0TNNCe2@Zn9 z>-!y>`ThRocdDeLh)z6F$Oh|Yd_l+#A+J?7$3lNE%fmHvp|NPAjicm;qUnIa|a3Um(ojBiDnvsEI~kgT`EiZs=KKXNQZ!f z@>$JOA?aeX!)Ba=pA9JnoNgJ354MSSGT95s=O^ts>kz||B{4kRJ(QPu4Y2|0iM`a` zU{&;xo5o*qsq6Opy5io}pg1+y>(=s7y|J#P4jHRtW_~;0@C0k`CEDnY#s+xVTe_;n z7)h6qJ-W*5>8kE&8U!5fk$6!lO-K7t8P)wHXthQl8xRiwzK+te1w3=m=N_cbJu(jwojODW44$E~jDhCz5Np!e zH3A1V0{nCdzSm3jSM55m8~UNN&bs}iY8(AbIgQ@7g+@O+mqu@A^3dpKK-+pj8$p|p z=2^F}V$HgRC%xBO9)54#!V}M5zp8o5`c;kFd&@g|ddpWl+IvLDanMIVi`TE3br*QU z`c-GW1AR;H5rLiSSIsY2w{ZIFy+^E=j52m0y$$(yAU}b2uV3|P$boVY>R9pYx`mS$ zL+*uq9QX~eX$Li|TR5p5Z9(}{OQ7G`TOLDuD&9hwCnNo6q}Q%nSdOwHi@~2o{dd7; zH{>8>lo77w7k#=TSnv_20+hc7?RXYt^{iiY>PDom1--L=)n^vNek$bV-tz8CA#;7J zL6@vwRd@cnh10lANQ?BK{+)=wi2S|lSJjpvetvIx2>lEkjr_Ziej5@sUq=h-cn@v8fBmXYUfNsUi9U92 zHNJYvF5|1ydLTcEw%h=DEp$7Ljjzu&Ha@uqd;+K(G!wMj*!V_`UcshUuze`L<^ZlF z9t%dec||jSSY>Q{0{jO%6z@^oCHZw*6kn=1Nq%jw;#HD=e~aQ%CBH_cU%gAluU7iC z%5JSk>erIwS8Y;!sp2J)e{YTC-&Og(8&PqWcDOfkH58&OlUz;|5V$J;8x{0odwbjklV4nJ#=85~Y#sjQyM+v?H7w(Gs{QbSM zx`3R0hlx#L5Ei`%5*_0R$4oAi;wph$1%ve#4qOb73tn(g;zcj2^uCp-tYAh?A`M>> zO(g82#Ak=Y6wnyi9=TOti`d)3Cv?`H*@XQN35VMJ(Pq4*9A6LKi(67dhh%SCtnD=d zBOd{Hv0P9-$N=Smc#WSE#J{Q7dzI6yPj82Zgeo~e!PC-Vz_JQ?$Vl&YR9X3KhXA{z z*=$8%#|0EBJNQ8>Nn|uPu){wcp0G=**UAK=T-fFI(>}W$d$qhl*zpQU=RkI_(Lp1i z5y%b%*mtl$Vqe2Pg}q#oLqdFi1@=cSt{jxlH4CRURx-4L+mTnrZ8Jo>5Y-{u5#TYh z2LTMnVsg0Mi{(F`qu1cwG}#VNmiFH^F8E0VIWDpk3*GGm0=99Hi=&7`d{lM{e>$$I zD-sDuY53 z$aVy5<09un`-OA>gFdCT?Er3_Zkk3wBk-Xifc44ZyxPU*)%gDJioJeA?1$D=U6Dp$ ze;^<}Zr;!!zIMGo%0J-C)YoVa_+9j2HIUDh`KtXOjtPe}*R?deeVJRBQ*S5M{+4PQNC!CXo|OvNzPld+;xpK%;;CH|PJzLT$e4^&`=bT{5DoFRG0Ngh># zI~ZuqR2Z(%s?H2y0-*wTxV0lwJ@Z>ThNqqnWB(M$Rhyamqti438Uc-fMj-nT5WmUP zEpK-u^ZDEeL`QUgNzJ55OB%htXgKE1ctShk00@VJl_Z{mP`}Y-59QkC5!YMYp}j?b z7p-@C1EIamH=^vt{7Ceu(&MRd)r8ypcon@1UZZ`=eDVf^LR}k(d1EnuC*F7)wUl#$ z;a2>npC5?_Is@m~64ad$Gc(F^&2MtGn0HK4^qy$A!|#h*1nlCbsE z&9?IODW^KtiR%s_e^44k!x4WpelCi|)7RXF1^)9Az1LFqB5+I<_r0N#KOBmY9||nP zqi<8@RoA+j{PB1owA{td?UTCoq0AP{tByoY?+OI*s9bUqzSR@KD~yuN&7o;m)`oqq zzIqIOOcnXfA31i-9I{l`?4Wmf%qrXC^{wzO_ea$@H*IY*IboVQyY1$+*}MDAo}UNr z*BJ@0i!+fCm#b3MeEU~+;28 z6&-CWDyF%oRp7brScNwds2G75;U(mSa(y{gl%sz)(9h^b`Z;Z)o9Jfx1>Hio(rxrh zx}APSchH@57yX*g4?xB0>KDwVCpx@Agw3QyBhv^Y|lpdpP^f+y&-_jHGJ9?6y zqNnK@dX}D}=jjD{kzS&g>G!mQUZGd%HF}-ipf~9cw3Gfwf1*FrTl6;lh5kyr=x_9Q z`Uky3|D<>6U-TaRoBl)prQP&CeL!MOGJVmQU`(Ku#w=qN^%$#-)wJ5!h;K<&aBiTRojaX7>F%83oMPIZB;eEmMbv9-BD|3dd1o3MwnZio z_RVgaW+`m$2ks?C+=!zbX?a!6m$}KtRV&HpHG1_I`Sch0csyiv$Z46=P}>$*;p+%T zJvE-DMe13H1Skc^EG@ng(W7ZS>` zt-fW^<%pkm)Wz7U;Mh!X5F}pT@7T=m_bU} zt`7wRA-}5@kCC*GZVSYNOO{2$xYiJA%dZP9$0tVH$5;z;T-*}=wkJP#knnyfy;PNG zrV+>z1jLvBvZSxNn;L<12srRnA_SfaNf($YT#5mwTSnqzn&O>I_CoUcNqf#Z z#IR&ZEbblB%e;oz0QJOPYHzSAddN-VFS*op`+Z$;FP^a9rOSDn^Xxi)2#TeP^Tq7z&<%sHy?#$jI;Bb$`i%Nq>!Kfa*t+vz%j5q|wWbV;W z&n1!$vH3W!@b1M2ywfyof1X!Z)yZqHR&=k0q)zX3?h%nepI3~C-MG)4$oBJ!eFjcv z)Ci0O1PVq%C-y@haECP4>kf;1-Tf4A-4PM#Mh~u6f%rN~%NFp=L7#h&KKIBxM0Dy9 z5iod$$}$F;%R{V5XV(ZE*a+~`CHy649T)+HL(!b>T>_BN&y>^XZChycvvX_WE);rL*^d1q|xqj9Bf^`e0zutSqipeNr2h!V+e+TjtX!rV6pN1SL z2ceD?&#qfIc`@W($j5=-0GoDD!@7l&+R+x2KeYtsNhdG3=*8 zZtgAbz7#Uow;FWG`c-x3uUj~c+k~`859;5E_>0KjyM9$|3F7DXmWR;Kz|qLR3;8#q zy)DRxvO9u^Uy1%Be*k%A-2!ZKU^f}~z61Rl=s{uhtz&6#`MDlogZ6c_ppN&@*8A75 z`sAg(<(=qb=T_sZr|dGmI;{utlW5Bgkk>-D)42ZmxyJPmuK}L`DhJI3?KZA|p+>J@ z(<|7r-5JJX!3bkeH1mg5#`Rmlf3QRG9>rafU$;f^rHYf}*Y+x2CHeQaC_Yv4YgGEx zyJY-orC+P;)_SCVElGaWCdHR3ULyJT)=2(cmG8R|6?aK~<#xq;6rU>j6+0AH`L9s= z%a#6e=mCI(JZhbSb<;Xq$Hw@HHS=rhCb}lpRyS9JdFpGLCuS2EjuLzYPP_%XzgJcl z@T!jFfr51|W-y2&mO=>Fm|U4vG3V0|thaFBVt`!mf`bwltjbQVf-{)GzT^g95;fKP z%=zqam_ni}qCh+ay)WV8owa8+VLwE|A-pKR886naRbLMt+XjSdXYk z%V!}j!mp3`*>QXJ-1d+e*F5VGn1F$qc3cpBD8=5LwDFj;3%AAq#YMTO|7Pp=2?dTpL-JCLV+_--r$uLM0lp1=m;@f zoD1g9Z|0~OoO2eOl^BDwvwaxA&NT{cPMTsp35B7=h{ki zPVqv;i){5f^W+y8(LuAB z`~!i181SnhnHS@vyd3N#$U0~Q`XZ2nDS#k-t^6e^<`^q^V!4oZzWR+RCR$N_J?=sv zoP65%1t+{{N8}&>o|>A7<2IqRGi-LOHk~?GE31}Mna3&zH^}@!dpcyZF4nr2$L&mwI3<4o&V4{|aC=V>A~`+@K2gcD!Qo#xIL^T4L?M!+1zh8U)la>4*aBeGtlgHO7 zLL?{epHC^7+dEUqr-7fUwsn5R|x;&|0 zcsYu6N&V;9mgg11=V6nl>b>xPpePV(T5>OVj<7GiVKL-FrT-21ky8K62FS_}EZKoB zR>|^%Jbsi<@VX$6k@mej=ahf&Vm#(vCiQ>040?X44#Kj#pqIFnef@SQ#t1d7xF7rz zvi#4V2U+=$6|bURh0mBbpf8d3!FK_zD(CC~bmOExgn2jiMOn`0cn%#R^&9^~Vd@+GX_!W&m7)(?T(^)7Y^o$*K? z{R7Zb5A@t8oBad5h?hXmbH%RbdeuD2<2j*@9S>k`DnC}b_8ZVo1tC1K81*WDavI~T zjw#Qe9mHS*hmH)qt_rWZoHyjP%YP|jN-;h6}^c|>I`ICvu zpgT?KonNv1Sm{mdV?HbOxfdn;*ytZXS0(j_wSr4OR{B&fJNt zV~$Svv6C5Ro_o-jVSg$=R{BpaAN^td#D&;OKUTW)4#;(^CZinVRQa*8IX{JFw$!^h zpPF~EPRMhVp8Kfu$MT$;tMr$ED?fH5j=5B?^gMsnyngRt$h==subbPZ{Ma%FJkhxse^S+RNtSm4Y@)uZ5#sc84{8-uP|A1z()Gy?D z)}Z_t?=KmQg{vW$e$1{%dn`X@(|>{6Y5Fmne(`b8n|{o$XaAw*X-JQUggK8bHCUZz}~+G`%BH!*1tisl+|Rc;P$MM<<#)8#mj0kf~eO# zA3N$nY&L{QfU`(3lW0!1(u1)G!VZJLrRyN~x$bO~gv90`A3zyIH z3iYl+drUv}!BfyIm-?@8UggKG_#tGTCouiW-_hU8Wc$bSI1fnu>K^Eo|F32L(;@X= zzXV*3x051}S1A4UC`{qw;bVAE>c4S^<;TiCUke@k0+?QUZ^Dm#i^q6K>aXQ_t6q&r!eW$0mOTnz+>8x*A;itJ41#L+(;~^w-pHYlDpahhW^g6 z#^bBma{b3Ej)u^ReEu~mL-3FC7StD)z6-f76eBiuVQ2H!CPV=Cl;XOT0f#KbFud19d)@PS4{n zp*LPyi@52>?0Rm4nvaVjkiQ8+J73y_Zfy|by!7!OL363p|EbyXW2JX&f$m#U|JOF` zFF8+3-{rb5llp(6{pRn=X7&N!mipbh5`Jth_9?9qdPnXd2|xCA-tXU$`hw}!cqn~@ z$Ij(aKZdcD=VN8w`ygMT^s~X${<@j)#Mo4i>*4I9=ioH z&u7-(nDAqt;<~Pq`om@={8%B+U&a^qN7iDi{MZD>>}pm+-->#bAG?Id5|2OVkHY*m ze^*Xk2+jAU{^OW8>UU-7<&4|4Qa^>qxjG;F$8RBDr}V(j{9SqT^U(c3>OVC;;m6#$ z(6Nt3m6hdazxrMIrLEBPvYH&9<$WRjSm{eV{@1aZ991U(fBCzzEare_J*&wv8#t(W zSJwUiATzQ4i`c69c+(!p8(0l}54OsW#eWW&#}?~(9;kV=kp0{BtcITVgYskF>xKM( zAn4~T#a5kB^6FJ_z|IQttsirXOoyZ24G)a$2x4 z{n*>hh}^(xa(sRQS$@pRbNXjezho!u)%cn7Ip{EM5FAV2g}plNmpl#)_Zj--dDy7_ zevvW2d}Dn8KnjJKW5hlF%C)jF`Hie{xr|W?E1G*j_Jqj`rXhgKNjHe%*Ozj`t1he{hjC9!js{c5Y!cIW~L`TlKqgn*;JatcLy?Y}NVL z_%|W*{$M@!r+Izsm@;VC$3g$2cd%9C@Te7#?`JhRu1A^X`Pk@7pm{**e+s{0o{t^F zb^b=`H?0MizwU4?oD~!VgLPFP^acSlNj@ zR$+(WxR2+DIv;D|c5)r8f8Fw9Wuv&Az@7DvwI}@8#FfxICiUCTNBgB8D}9c!+9vf+ ztp`_r?5UR_^PE9BPoD?;r5`K3V=^?`rT#_WV9vWXu9JN$>|f-0rhZpCiy@;w2#yz@ z#8&yS)t5p39jl?|y5u}6-OclY?O6XOY^5J7i+&sOQ>-S(OW2R5AM?+L{4}e{@p4JR zFP&Ea%`;N}29I;;$4a+<1M;&{|0eJ;{a7B4+2^GGFU^)8D?5BObk9ruUxUCwosXU2 zg6;*We|MJU$4Z}JU-F{V|7%`i+&;rty(IPTpN+<<{=Sc~jZZspaO_!zt@8hWL|^d1 z2M&%syie7*x(#in9U#_UkFD}!A2=bu!fNQbf6|YYKE!kSRaQg43tQ#Kgz7b+cZ%!B zsy+8{8!(;_oZ`Bv^kZc;KZoWGr3e1z`Pk~Wp?g#6a~P}Rlpo6#I^H;QHeze~F}r>% z#-ZuQZ2Cgzt>2Y4ebHR#O+RMWw_sSE-x*(y{bRfS?;OwL=;NK?cmO;Roa6t7{Z-&; zz{9>}Q^zCW4!E{9MKeP4U7Pw7ot=57x* X#huyJ<@i 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 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/WinServices.h b/mDNSWindows/WinServices.h new file mode 100644 index 0000000..9700cd4 --- /dev/null +++ b/mDNSWindows/WinServices.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1997-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: WinServices.h,v $ +Revision 1.1 2004/06/18 05:23:33 rpantos +First checked in + + +*/ + + +#pragma once + +#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 + +#include +#include // MFC socket extensions +#include "CommonServices.h" + + +OSStatus UTF8StringToStringObject( const char *inUTF8, CString &inObject ); diff --git a/mDNSWindows/WinVersRes.h b/mDNSWindows/WinVersRes.h new file mode 100644 index 0000000..b6edb1e --- /dev/null +++ b/mDNSWindows/WinVersRes.h @@ -0,0 +1,90 @@ +/* + * 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@ + + Change History (most recent first): + +$Log: WinVersRes.h,v $ +Revision 1.17 2004/10/20 15:37:46 shersche +Bump to Windows-trial-21 + +Revision 1.16 2004/10/12 23:51:36 cheshire +Bump version to 1.0.0.20 + +Revision 1.15 2004/09/21 01:15:56 shersche +bump version to 1.0.19 + +Revision 1.14 2004/09/16 21:27:46 shersche +Bump to version 18 + +Revision 1.13 2004/09/13 21:28:41 shersche +Bump version to 17 + +Revision 1.12 2004/08/28 00:18:01 rpantos +no message + +Revision 1.11 2004/08/26 17:37:15 shersche +bump version to 1.0.0.14 + +Revision 1.10 2004/08/05 18:00:04 rpantos +bump to 1.0.0.13 + +Revision 1.9 2004/07/27 07:31:46 shersche +bump to 1.0.0.12 + +Revision 1.8 2004/07/22 23:28:54 shersche +bump to Version 1.0.0.11 + +Revision 1.7 2004/07/20 23:36:24 shersche +bump to version 1.0.0.10 + +Revision 1.6 2004/07/14 19:53:26 shersche +bump version to 1.0.0.9 + +Revision 1.5 2004/07/13 22:20:02 rpantos +Fix for . + +Revision 1.4 2004/07/09 18:04:17 shersche +bump version to 1.0.0.8 + +Revision 1.3 2004/06/27 17:00:15 rpantos +Cleanup. + +Revision 1.2 2004/06/26 22:24:08 rpantos +Cleanup. + +Revision 1.1 2004/06/26 19:17:41 rpantos +First checked in. + + */ + +#ifndef WINRESVERS_H +#define WINRESVERS_H + +#define MASTER_PROD_NAME "Rendezvous" + +// Define the product version for mDNSResponder on Windows +#define MASTER_PROD_VERS 1,0,0,21 +#define MASTER_PROD_VERS_STR "1,0,0,21" +#define MASTER_PROD_VERS_STR2 "1.0.0.21" +#define MASTER_PROD_VERS_STR3 "Explorer Plugin 1.0.0.21" + +#endif // WINRESVERS_H diff --git a/mDNSWindows/mDNSWin32.c b/mDNSWindows/mDNSWin32.c index b24527f..1f48cf7 100755 --- a/mDNSWindows/mDNSWin32.c +++ b/mDNSWindows/mDNSWin32.c @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,91 @@ Change History (most recent first): $Log: mDNSWin32.c,v $ +Revision 1.63 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.62 2004/11/12 03:16:41 rpantos +rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName + +Revision 1.61 2004/11/05 22:54:38 shersche +Change registry key flags from KEY_ALL_ACCESS to KEY_READ to support mDNSResponder running with limited access rights +Submitted by: Pavel Repin + +Revision 1.60 2004/11/05 22:41:56 shersche +Determine subnet mask when populating network interface data structures +Submitted by: Pavel Repin +Reviewed by: + +Revision 1.59 2004/10/28 03:24:42 cheshire +Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353 + +Revision 1.58 2004/10/16 00:17:01 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.57 2004/10/11 21:53:15 shersche + Change GetWindowsVersionString link scoping from static to non-static so that it can be accessed from other compilation units. The information returned in this function will be used to determine what service dependencies to use when calling CreateService(). +Bug #: 3832450 + +Revision 1.56 2004/09/26 23:20:36 ksekar + Allow default registrations in multiple wide-area domains + +Revision 1.55 2004/09/21 21:02:57 cheshire +Set up ifname before calling mDNS_RegisterInterface() + +Revision 1.54 2004/09/17 01:08:57 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.53 2004/09/17 00:19:11 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.52 2004/09/16 00:24:50 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.51 2004/09/14 23:42:37 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.50 2004/08/25 23:36:56 shersche + Remove code that retrieves TTL from received packets +Bug #: 3658379 + +Revision 1.49 2004/08/25 16:43:29 ksekar +Fix Windows build - change mDNS_SetFQDNs to mDNS_SetFQDN, remove unicast +hostname parameter. + +Revision 1.48 2004/08/14 03:22:43 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.47 2004/08/06 17:33:02 shersche + Put correct length of string in first byte of nicelabel +Bug #: 3753797 + +Revision 1.46 2004/08/05 05:43:01 shersche + Add HostDescriptionChangedCallback so callers can choose to handle it when mDNSWin32 core detects that the computer description string has changed +Bug #: 3751566 + +Revision 1.45 2004/07/26 22:49:31 ksekar +: Feature #9516: Need support for NATPMP in client + +Revision 1.44 2004/07/26 05:42:50 shersche +use "Computer Description" for nicename if available, track dynamic changes to "Computer Description" + +Revision 1.43 2004/07/13 21:24:25 rpantos +Fix for . + +Revision 1.42 2004/06/24 15:23:24 shersche +Add InterfaceListChanged callback. This callback is used in Service.c to add link local routes to the routing table +Submitted by: herscher + +Revision 1.41 2004/06/18 05:22:16 rpantos +Integrate Scott's changes + 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 @@ -42,7 +125,7 @@ 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" +from mDNSMacOSX.h to mDNSEmbeddedAPI.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. @@ -87,7 +170,7 @@ 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. +Best solution is just to combine mDNSEmbeddedAPI.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. @@ -96,7 +179,7 @@ Revision 1.23 2003/10/14 03:26:12 bradley Clear interface list buffer to workaround Windows CE bug where interfaces are not reported correctly. Revision 1.22 2003/08/20 06:21:25 bradley -Updated to latest internal version of the Rendezvous for Windows platform plugin: Added support +Updated to latest internal version of the mDNSWindows platform layer: Added support for Windows CE/PocketPC 2003; re-did interface-related code to emulate getifaddrs/freeifaddrs for restricting usage to only active, multicast-capable, and non-point-to-point interfaces and to ease the addition of IPv6 support in the future; Changed init code to serialize thread initialization to @@ -107,7 +190,7 @@ platforms (re-mapped to CreateThread on Window CE) to avoid a leak in the Micros Added IPv4/IPv6 string<->address conversion routines; Cleaned up some code and added HeaderDoc. Revision 1.21 2003/08/18 23:09:57 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() + mDNSResponder divide by zero in mDNSPlatformRawTime() Revision 1.20 2003/08/12 19:56:27 cheshire Update to APSL 2.0 @@ -195,7 +278,7 @@ Multicast DNS platform plugin for Win32 #include #endif -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "mDNSWin32.h" @@ -223,7 +306,8 @@ Multicast DNS platform plugin for Win32 #define kWaitListCancelEvent ( WAIT_OBJECT_0 + 0 ) #define kWaitListInterfaceListChangedEvent ( WAIT_OBJECT_0 + 1 ) #define kWaitListWakeupEvent ( WAIT_OBJECT_0 + 2 ) -#define kWaitListFixedItemCount 3 +#define kWaitListRegEvent ( WAIT_OBJECT_0 + 3 ) +#define kWaitListFixedItemCount 4 #if( !TARGET_OS_WINDOWS_CE ) static GUID kWSARecvMsgGUID = WSAID_WSARECVMSG; @@ -239,6 +323,8 @@ Multicast DNS platform plugin for Win32 mDNSlocal mStatus SetupSynchronizationObjects( mDNS * const inMDNS ); mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS ); +mDNSlocal mStatus SetupNiceName( mDNS * const inMDNS ); +mDNSlocal mStatus SetupHostName( mDNS * const inMDNS ); mDNSlocal mStatus SetupName( mDNS * const inMDNS ); mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ); mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ); @@ -256,6 +342,7 @@ mDNSlocal mStatus ProcessingThreadInitialize( mDNS * const inMDNS ); mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount ); mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *inIFD, SocketRef inSock ); mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ); +mDNSlocal void ProcessingThreadRegistryChanged( mDNS * inMDNS ); // Platform Accessors @@ -277,6 +364,7 @@ mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInter #if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS ) mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ); + mDNSlocal int getifnetmask_ipv6( struct ifaddrs * ifa ); #endif #if( !TARGET_OS_WINDOWS_CE ) @@ -288,7 +376,6 @@ mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInter #endif mDNSlocal mDNSBool CanReceiveUnicast( void ); -mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize ); #ifdef __cplusplus } @@ -353,7 +440,7 @@ mStatus mDNSPlatformInit( mDNS * const inMDNS ) supported = ( ( LOBYTE( wsaData.wVersion ) == kWinSockMajorMin ) && ( HIBYTE( wsaData.wVersion ) == kWinSockMinorMin ) ); require_action( supported, exit, err = mStatus_UnsupportedErr ); - inMDNS->CanReceiveUnicast = CanReceiveUnicast(); + inMDNS->CanReceiveUnicastOn5353 = CanReceiveUnicast(); // Setup the HINFO HW/SW strings. @@ -406,6 +493,7 @@ void mDNSPlatformClose( mDNS * const inMDNS ) err = TearDownInterfaceList( inMDNS ); check_noerr( err ); + check( !inMDNS->p->inactiveInterfaceList ); err = TearDownSynchronizationObjects( inMDNS ); check_noerr( err ); @@ -434,7 +522,7 @@ void mDNSPlatformClose( mDNS * const inMDNS ) mStatus mDNSPlatformSendUDP( const mDNS * const inMDNS, - const DNSMessage * const inMsg, + const void * const inMsg, const mDNSu8 * const inMsgEnd, mDNSInterfaceID inInterfaceID, const mDNSAddr * inDstIP, @@ -617,25 +705,30 @@ mDNSexport void mDNSPlatformMemFree( void *inMem ) free( inMem ); } +//=========================================================================================================================== +// mDNSPlatformRandomSeed +//=========================================================================================================================== + +mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) +{ + return( GetTickCount() ); +} + //=========================================================================================================================== // mDNSPlatformTimeInit //=========================================================================================================================== -mDNSexport mStatus mDNSPlatformTimeInit( mDNSs32 *outTimeNow ) +mDNSexport mStatus mDNSPlatformTimeInit( void ) { - check( outTimeNow ); - // No special setup is required on Windows -- we just use GetTickCount(). - - *outTimeNow = mDNSPlatformTimeNow(); return( mStatus_NoError ); } //=========================================================================================================================== -// mDNSPlatformTimeNow +// mDNSPlatformRawTime //=========================================================================================================================== -mDNSs32 mDNSPlatformTimeNow( void ) +mDNSs32 mDNSPlatformRawTime( void ) { return( (mDNSs32) GetTickCount() ); } @@ -766,6 +859,7 @@ mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( const mDNS * const inMDNS, mD { mDNSInterfaceData * ifd; + // Search active interfaces. for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next ) { if( (mDNSInterfaceID) ifd == inID ) @@ -774,6 +868,20 @@ mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( const mDNS * const inMDNS, mD break; } } + + // Search inactive interfaces too so remove events for inactive interfaces report the old interface index. + + if( !ifd ) + { + for( ifd = inMDNS->p->inactiveInterfaceList; ifd; ifd = ifd->next ) + { + if( (mDNSInterfaceID) ifd == inID ) + { + index = ifd->scopeID; + break; + } + } + } check( ifd ); } return( index ); @@ -857,6 +965,14 @@ mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void) return mDNS_CopyDNameList(&tmp); } +//=========================================================================================================================== +// mDNSPlatformGetRegDomainList +//=========================================================================================================================== + +mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void) + { + return NULL; + } #if 0 @@ -905,6 +1021,7 @@ void verbosedebugf_( const char *inFormat, ... ) // LogMsg //=========================================================================================================================== +/* void LogMsg( const char *inFormat, ... ) { char buffer[ 512 ]; @@ -917,6 +1034,7 @@ void LogMsg( const char *inFormat, ... ) dlog( kDebugLevelWarning, "%s\n", buffer ); } +*/ #if 0 #pragma mark - @@ -992,52 +1110,141 @@ mDNSlocal mStatus TearDownSynchronizationObjects( mDNS * const inMDNS ) return( mStatus_NoError ); } + //=========================================================================================================================== -// SetupName +// SetupNiceName //=========================================================================================================================== -mDNSlocal mStatus SetupName( mDNS * const inMDNS ) +mDNSlocal mStatus SetupNiceName( mDNS * const inMDNS ) { - mStatus err; + mStatus err = 0; char tempString[ 256 ]; check( inMDNS ); // Set up the nice name. + tempString[ 0 ] = '\0'; + + // First try and open the registry key that contains the computer description value + if (inMDNS->p->regKey == NULL) + { + const char * s = "SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters"; + err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, s, 0, KEY_READ, &inMDNS->p->regKey); + check_translated_errno( err == 0, errno_compat(), kNameErr ); + + if (err) + { + inMDNS->p->regKey = NULL; + } + } + + // if we opened it... + if (inMDNS->p->regKey != NULL) + { + DWORD type; + DWORD valueLen = sizeof(tempString); + + // look for the computer description + err = RegQueryValueEx(inMDNS->p->regKey, "srvcomment", 0, &type, (LPBYTE) &tempString, &valueLen); + check_translated_errno( err == 0, errno_compat(), kNameErr ); + } + + // if we can't find it in the registry, then use the hostname of the machine + if (err || ( tempString[ 0] == '\0' ) ) + { + err = gethostname( tempString, sizeof( tempString ) - 1 ); + check_translated_errno( err == 0, errno_compat(), kNameErr ); + } + + // if we can't get the hostname + if( err || ( tempString[ 0 ] == '\0' ) ) + { + // Invalidate name so fall back to a default name. + + strcpy( tempString, kMDNSDefaultName ); + } + + tempString[ sizeof( tempString ) - 1 ] = '\0'; + inMDNS->nicelabel.c[ 0 ] = (mDNSu8) (strlen( tempString ) < MAX_DOMAIN_LABEL ? strlen( tempString ) : MAX_DOMAIN_LABEL); + memcpy( &inMDNS->nicelabel.c[ 1 ], tempString, inMDNS->nicelabel.c[ 0 ] ); + + dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] ); + + return( err ); +} + + +//=========================================================================================================================== +// SetupHostName +//=========================================================================================================================== + +mDNSlocal mStatus SetupHostName( mDNS * const inMDNS ) +{ + mStatus err = 0; + char tempString[ 256 ]; + domainlabel tempLabel; + + check( inMDNS ); + + // Set up the nice name. tempString[ 0 ] = '\0'; + + // use the hostname of the machine err = gethostname( tempString, sizeof( tempString ) - 1 ); check_translated_errno( err == 0, errno_compat(), kNameErr ); + + // if we can't get the hostname if( err || ( tempString[ 0 ] == '\0' ) ) { // Invalidate name so fall back to a default name. strcpy( tempString, kMDNSDefaultName ); } + tempString[ sizeof( tempString ) - 1 ] = '\0'; - - inMDNS->nicelabel.c[ 0 ] = (mDNSu8) strlen( tempString ); - memcpy( &inMDNS->nicelabel.c[ 1 ], tempString, inMDNS->nicelabel.c[ 0 ] ); + tempLabel.c[ 0 ] = (mDNSu8) (strlen( tempString ) < MAX_DOMAIN_LABEL ? strlen( tempString ) : MAX_DOMAIN_LABEL ); + memcpy( &tempLabel.c[ 1 ], tempString, tempLabel.c[ 0 ] ); // Set up the host name. - ConvertUTF8PstringToRFC1034HostLabel( inMDNS->nicelabel.c, &inMDNS->hostlabel ); + ConvertUTF8PstringToRFC1034HostLabel( tempLabel.c, &inMDNS->hostlabel ); if( inMDNS->hostlabel.c[ 0 ] == 0 ) { // Nice name has no characters that are representable as an RFC1034 name (e.g. Japanese) so use the default. MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName ); } - check( inMDNS->nicelabel.c[ 0 ] != 0 ); + check( inMDNS->hostlabel.c[ 0 ] != 0 ); - mDNS_GenerateFQDN( inMDNS ); + mDNS_SetFQDN( inMDNS ); - dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] ); dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] ); + return( err ); } +//=========================================================================================================================== +// SetupName +//=========================================================================================================================== + +mDNSlocal mStatus SetupName( mDNS * const inMDNS ) +{ + mStatus err = 0; + + check( inMDNS ); + + err = SetupNiceName( inMDNS ); + check_noerr( err ); + + err = SetupHostName( inMDNS ); + check_noerr( err ); + + return err; +} + + //=========================================================================================================================== // SetupInterfaceList //=========================================================================================================================== @@ -1062,7 +1269,7 @@ mDNSlocal mStatus SetupInterfaceList( mDNS * const inMDNS ) // Tear down any existing interfaces that may be set up. TearDownInterfaceList( inMDNS ); - + // Set up the name of this machine. err = SetupName( inMDNS ); @@ -1197,12 +1404,32 @@ exit: mDNSlocal mStatus TearDownInterfaceList( mDNS * const inMDNS ) { mStatus err; + mDNSInterfaceData ** p; mDNSInterfaceData * ifd; dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list\n" ); check( inMDNS ); check( inMDNS->p ); + // Free any interfaces that were previously marked inactive and are no longer referenced by the mDNS cache. + // Interfaces are marked inactive, but not deleted immediately if they were still referenced by the mDNS cache + // so that remove events that occur after an interface goes away can still report the correct interface. + + p = &inMDNS->p->inactiveInterfaceList; + while( *p ) + { + ifd = *p; + if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) ifd ) > 0 ) + { + p = &ifd->next; + continue; + } + + dlog( kDebugLevelInfo, DEBUG_NAME "freeing unreferenced, inactive interface %#p %#a\n", ifd, &ifd->interfaceInfo.ip ); + *p = ifd->next; + free( ifd ); + } + // Tear down interface list change notifications. err = TearDownNotifications( inMDNS ); @@ -1252,7 +1479,10 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inI 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'; - + + strncpy(ifd->interfaceInfo.ifname, inIFA->ifa_name, sizeof(ifd->interfaceInfo.ifname)); + ifd->interfaceInfo.ifname[sizeof(ifd->interfaceInfo.ifname)-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 @@ -1351,6 +1581,9 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inI err = SockAddrToMDNSAddr( inIFA->ifa_addr, &ifd->interfaceInfo.ip, NULL ); require_noerr( err, exit ); + err = SockAddrToMDNSAddr( inIFA->ifa_netmask, &ifd->interfaceInfo.mask, NULL ); + require_noerr( err, exit ); + ifd->interfaceInfo.Advertise = inMDNS->AdvertiseLocalAddresses; err = mDNS_RegisterInterface( inMDNS, &ifd->interfaceInfo ); @@ -1408,10 +1641,21 @@ mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inI { close_compat( sock ); } - - // Free the memory used by the interface info. - free( inIFD ); + // If the interface is still referenced by items in the mDNS cache then put it on the inactive list. This keeps + // the InterfaceID valid so remove events report the correct interface. If it is no longer referenced, free it. + + if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) inIFD ) > 0 ) + { + inIFD->next = inMDNS->p->inactiveInterfaceList; + inMDNS->p->inactiveInterfaceList = inIFD; + dlog( kDebugLevelInfo, DEBUG_NAME "deferring free of interface %#p %#a\n", inIFD, &inIFD->interfaceInfo.ip ); + } + else + { + dlog( kDebugLevelInfo, DEBUG_NAME "freeing interface %#p %#a immediately\n", inIFD, &inIFD->interfaceInfo.ip ); + free( inIFD ); + } return( mStatus_NoError ); } @@ -1468,7 +1712,7 @@ mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAdd // Join the all-DNS multicast group so we receive Multicast DNS packets. - mreqv4.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; + mreqv4.imr_multiaddr.s_addr = AllDNSLinkGroupv4.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 ); @@ -1523,12 +1767,6 @@ mDNSlocal mStatus SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAdd 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). @@ -1680,6 +1918,16 @@ mDNSlocal mStatus SetupNotifications( mDNS * const inMDNS ) err = translate_errno( err == 0, errno_compat(), kUnknownErr ); require_noerr( err, exit ); + inMDNS->p->regEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + err = translate_errno( inMDNS->p->regEvent, (mStatus) GetLastError(), kUnknownErr ); + require_noerr( err, exit ); + + if (inMDNS->p->regKey != NULL) + { + err = RegNotifyChangeKeyValue(inMDNS->p->regKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, inMDNS->p->regEvent, TRUE); + require_noerr( err, exit ); + } + exit: if( err ) { @@ -1699,6 +1947,19 @@ mDNSlocal mStatus TearDownNotifications( mDNS * const inMDNS ) close_compat( inMDNS->p->interfaceListChangedSocket ); inMDNS->p->interfaceListChangedSocket = kInvalidSocketRef; } + + if ( inMDNS->p->regEvent != NULL ) + { + CloseHandle( inMDNS->p->regEvent ); + inMDNS->p->regEvent = NULL; + } + + if ( inMDNS->p->regKey != NULL ) + { + RegCloseKey( inMDNS->p->regKey ); + inMDNS->p->regKey = NULL; + } + return( mStatus_NoError ); } @@ -1813,7 +2074,11 @@ mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) { // Give the mDNS core a chance to do its work and determine next event time. - mDNSs32 interval = mDNS_Execute(m) - mDNSPlatformTimeNow(); + mDNSs32 interval = mDNS_Execute(m) - mDNS_TimeNow(m); + if (m->p->idleThreadCallback) + { + interval = m->p->idleThreadCallback(m, interval); + } if (interval < 0) interval = 0; else if (interval > (0x7FFFFFFF / 1000)) interval = 0x7FFFFFFF / mDNSPlatformOneSecond; else interval = (interval * 1000) / mDNSPlatformOneSecond; @@ -1850,6 +2115,14 @@ mDNSlocal unsigned WINAPI ProcessingThread( LPVOID inParam ) dlog( kDebugLevelChatty - 1, DEBUG_NAME "wakeup for mDNS_Execute\n" ); continue; } + else if ( result == kWaitListRegEvent ) + { + // + // The computer description might have changed + // + ProcessingThreadRegistryChanged( m ); + break; + } else { int waitItemIndex; @@ -1966,6 +2239,7 @@ mDNSlocal mStatus ProcessingThreadSetupWaitList( mDNS * const inMDNS, HANDLE **o *waitItemPtr++ = inMDNS->p->cancelEvent; *waitItemPtr++ = inMDNS->p->interfaceListChangedEvent; *waitItemPtr++ = inMDNS->p->wakeupEvent; + *waitItemPtr++ = inMDNS->p->regEvent; // Append all the dynamic wait items to the list. @@ -2066,10 +2340,6 @@ mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *i 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 @@ -2094,7 +2364,7 @@ mDNSlocal void ProcessingThreadProcessPacket( mDNS *inMDNS, mDNSInterfaceData *i dlog( kDebugLevelChatty, DEBUG_NAME "\n" ); end = ( (mDNSu8 *) &packet ) + n; - mDNSCoreReceive( inMDNS, &packet, end, &srcAddr, srcPort, &dstAddr, dstPort, inIFD->interfaceInfo.InterfaceID, ttl ); + mDNSCoreReceive( inMDNS, &packet, end, &srcAddr, srcPort, &dstAddr, dstPort, inIFD->interfaceInfo.InterfaceID ); exit: return; @@ -2110,6 +2380,11 @@ mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ) dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed\n" ); check( inMDNS ); + + if (inMDNS->p->interfaceListChangedCallback) + { + inMDNS->p->interfaceListChangedCallback(inMDNS); + } mDNSPlatformLock( inMDNS ); @@ -2135,6 +2410,38 @@ mDNSlocal void ProcessingThreadInterfaceListChanged( mDNS *inMDNS ) mDNSCoreMachineSleep( inMDNS, mDNSfalse ); } + +//=========================================================================================================================== +// ProcessingThreadRegistryChanged +//=========================================================================================================================== +mDNSlocal void ProcessingThreadRegistryChanged( mDNS *inMDNS ) +{ + mStatus err; + + dlog( kDebugLevelInfo, DEBUG_NAME "registry has changed\n" ); + check( inMDNS ); + + mDNSPlatformLock( inMDNS ); + + // redo the names + SetupNiceName( inMDNS ); + + if (inMDNS->p->hostDescriptionChangedCallback) + { + inMDNS->p->hostDescriptionChangedCallback(inMDNS); + } + + // and reset the event handler + if ((inMDNS->p->regKey != NULL) && (inMDNS->p->regEvent)) + { + err = RegNotifyChangeKeyValue(inMDNS->p->regKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, inMDNS->p->regEvent, TRUE); + check_noerr( err ); + } + + mDNSPlatformUnlock( inMDNS ); +} + + #if 0 #pragma mark - #pragma mark == Utilities == @@ -2330,6 +2637,12 @@ mDNSlocal int getifaddrs_ipv6( struct ifaddrs **outAddrs ) 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 ); + + ifa->ifa_netmask = (struct sockaddr *) calloc( 1, sizeof(struct sockaddr) ); + require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS ); + err = getifnetmask_ipv6(ifa); + require_noerr(err, exit); + break; default: @@ -2358,6 +2671,60 @@ exit: } return( (int) err ); } + +mDNSlocal int +getifnetmask_ipv6( struct ifaddrs * ifa ) +{ + PMIB_IPADDRTABLE pIPAddrTable = NULL; + DWORD dwSize = 0; + DWORD dwRetVal; + DWORD i; + int err = 0; + + // Make an initial call to GetIpAddrTable to get the + // necessary size into the dwSize variable + + dwRetVal = GetIpAddrTable(NULL, &dwSize, 0); + require_action( dwRetVal == ERROR_INSUFFICIENT_BUFFER, exit, err = WSAENOBUFS ); + + pIPAddrTable = (MIB_IPADDRTABLE *) malloc ( dwSize ); + require_action( pIPAddrTable != NULL, exit, err = WSAENOBUFS ); + + // Make a second call to GetIpAddrTable to get the + // actual data we want + + dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); + require_action(dwRetVal == NO_ERROR, exit, err = WSAENOBUFS); + + // Now try and find the correct IP Address + + for (i = 0; i < pIPAddrTable->dwNumEntries; i++) + { + struct sockaddr_in sa; + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = pIPAddrTable->table[i].dwAddr; + + if (memcmp(ifa->ifa_addr, &sa, sizeof(sa)) == 0) + { + // Found the right one, so copy the subnet mask information + + sa.sin_addr.s_addr = pIPAddrTable->table[i].dwMask; + memcpy( ifa->ifa_netmask, &sa, sizeof(sa) ); + break; + } + } + +exit: + + if ( pIPAddrTable != NULL ) + { + free(pIPAddrTable); + } + + return err; +} #endif // MDNS_WINDOWS_USE_IPV6_IF_ADDRS #if( !TARGET_OS_WINDOWS_CE ) @@ -2454,6 +2821,12 @@ mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs ) ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa4 ) ); require_action( ifa->ifa_addr, exit, err = WSAENOBUFS ); memcpy( ifa->ifa_addr, sa4, sizeof( *sa4 ) ); + + sa4 = &ifInfo->iiNetmask.AddressIn; + ifa->ifa_netmask = (struct sockaddr*) calloc(1, sizeof( *sa4 ) ); + require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS ); + memcpy( ifa->ifa_netmask, sa4, sizeof( *sa4 ) ); + break; } @@ -2696,7 +3069,7 @@ mDNSlocal mDNSBool CanReceiveUnicast( void ) // GetWindowsVersionString //=========================================================================================================================== -mDNSlocal OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize ) +OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize ) { #if( !defined( VER_PLATFORM_WIN32_CE ) ) #define VER_PLATFORM_WIN32_CE 3 diff --git a/mDNSWindows/mDNSWin32.h b/mDNSWindows/mDNSWin32.h index 946fd7e..70dbe22 100755 --- a/mDNSWindows/mDNSWin32.h +++ b/mDNSWindows/mDNSWin32.h @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,33 @@ Change History (most recent first): $Log: mDNSWin32.h,v $ +Revision 1.18 2004/10/11 21:53:15 shersche + Change GetWindowsVersionString link scoping from static to non-static so that it can be accessed from other compilation units. The information returned in this function will be used to determine what service dependencies to use when calling CreateService(). +Bug #: 3832450 + +Revision 1.17 2004/09/17 01:08:57 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.16 2004/08/05 05:43:01 shersche + Add HostDescriptionChangedCallback so callers can choose to handle it when mDNSWin32 core detects that the computer description string has changed +Bug #: 3751566 + +Revision 1.15 2004/07/26 05:42:50 shersche +use "Computer Description" for nicename if available, track dynamic changes to "Computer Description" + +Revision 1.14 2004/07/13 21:24:25 rpantos +Fix for . + +Revision 1.13 2004/06/24 15:23:24 shersche +Add InterfaceListChanged callback. This callback is used in Service.c to add link local routes to the routing table +Submitted by: herscher + +Revision 1.12 2004/06/18 05:22:16 rpantos +Integrate Scott's changes + 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 @@ -37,7 +62,7 @@ Revision 1.10 2003/10/24 23:23:02 bradley Removed legacy port 53 support as it is no longer needed. Revision 1.9 2003/08/20 06:21:25 bradley -Updated to latest internal version of the Rendezvous for Windows platform plugin: Added support +Updated to latest internal version of the mDNSWindows platform layer: Added support for Windows CE/PocketPC 2003; re-did interface-related code to emulate getifaddrs/freeifaddrs for restricting usage to only active, multicast-capable, and non-point-to-point interfaces and to ease the addition of IPv6 support in the future; Changed init code to serialize thread initialization to @@ -80,7 +105,7 @@ Multicast DNS platform plugin for Win32 #include #endif -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #ifdef __cplusplus extern "C" { @@ -109,6 +134,35 @@ struct mDNSInterfaceData mDNSBool hostRegistered; }; +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef IdleThreadCallback + + @abstract mDNSWin32 core will call out through this function pointer + after calling mDNS_Execute +*/ +typedef mDNSs32 (*IdleThreadCallback)(mDNS * const inMDNS, mDNSs32 interval); +//--------------------------------------------------------------------------------------------------------------------------- + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef InterfaceListChangedCallback + + @abstract mDNSWin32 core will call out through this function pointer + after detecting an interface list changed event +*/ +typedef void (*InterfaceListChangedCallback)(mDNS * const inMDNS); +//--------------------------------------------------------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @typedef HostDescriptionChangedCallback + + @abstract mDNSWin32 core will call out through this function pointer + after detecting that the computer description has changed +*/ +typedef void (*HostDescriptionChangedCallback)(mDNS * const inMDNS); +//--------------------------------------------------------------------------------------------------------------------------- + + //--------------------------------------------------------------------------------------------------------------------------- /*! @struct mDNS_PlatformSupport_struct @@ -122,13 +176,19 @@ struct mDNS_PlatformSupport_struct HANDLE cancelEvent; HANDLE quitEvent; HANDLE interfaceListChangedEvent; + HANDLE regEvent; HANDLE wakeupEvent; HANDLE initEvent; + HKEY regKey; mStatus initStatus; SocketRef interfaceListChangedSocket; int interfaceCount; mDNSInterfaceData * interfaceList; + mDNSInterfaceData * inactiveInterfaceList; DWORD threadID; + IdleThreadCallback idleThreadCallback; + InterfaceListChangedCallback interfaceListChangedCallback; + HostDescriptionChangedCallback hostDescriptionChangedCallback; }; //--------------------------------------------------------------------------------------------------------------------------- @@ -155,6 +215,16 @@ struct ifaddrs } ifa_extra; }; + +//--------------------------------------------------------------------------------------------------------------------------- +/*! @function GetWindowsVersionString + + @abstract Stores Windows version information in the string passed in (inBuffer) +*/ + +OSStatus GetWindowsVersionString( char *inBuffer, size_t inBufferSize ); + + //--------------------------------------------------------------------------------------------------------------------------- /*! @function getifaddrs diff --git a/mDNSWindows/mdnsNSP/ReadMe.txt b/mDNSWindows/mdnsNSP/ReadMe.txt new file mode 100644 index 0000000..7a6e2cc --- /dev/null +++ b/mDNSWindows/mdnsNSP/ReadMe.txt @@ -0,0 +1,15 @@ +The mdnsNSP is a NameSpace Provider. It hooks into the Windows name resolution infrastructure to allow any software using the standard Windows APIs for resolving names to work with DNS-SD. For example, when the mdnsNSP is installed, you can type "http://computer.local./" in Internet Explorer and it will resolve "computer.local." using DNS-SD and go to the web site (assuming you have a computer named "computer" on the local network and advertised via DNS-SD). + +NSP's are implemented DLLs and must be installed to work. NSP DLLs export an NSPStartup function, which is called when the NSP is used, and NSPStartup provides information about itself (e.g. version number, compatibility information, and a list of function pointers for each of the supported NSP routines). + +If you need to register the mdnsNSP, you can use the NSPTool (sources for it are provided along with the mdnsNSP) with the following line from the DOS command line prompt ("" is the actual parent path of the DLL): + +NSPTool -install "mdnsNSP" "B600E6E9-553B-4a19-8696-335E5C896153" "" + +You can remove remove the mdnsNSP with the following line: + +NSPTool -remove "B600E6E9-553B-4a19-8696-335E5C896153" + +For more information, check out the following URL: + + diff --git a/mDNSWindows/mdnsNSP/mdnsNSP.aps b/mDNSWindows/mdnsNSP/mdnsNSP.aps new file mode 100644 index 0000000000000000000000000000000000000000..2e3b63a468e1c1a3626c28c5381eb781b4838db0 GIT binary patch literal 34332 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 Use the DNS types and classes defined in dns_sd.h +Bug #: 3789425 + +Revision 1.5 2004/07/13 21:24:28 rpantos +Fix for . + +Revision 1.4 2004/07/09 18:03:33 shersche +removed extraneous DNSServiceQueryRecord call + +Revision 1.3 2004/07/07 17:03:49 shersche + Check for LUP_RETURN_ADDR as well as LUP_RETURN_BLOB in NSPLookupServiceBegin +Bug #: 3715582 + +Revision 1.2 2004/06/24 19:18:07 shersche +Rename to mdnsNSP +Submitted by: herscher + +Revision 1.1 2004/06/18 04:13:44 rpantos +Move up one level. + 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. +mDNS NameSpace Provider (NSP). Hooks into the Windows name resolution system to perform +.local name lookups using Multicast DNS in all Windows apps. */ @@ -44,7 +63,7 @@ Rendezvous name lookups using Multicast DNS so .local names work in all Windows #include #include -#include "DNSSD.h" +#include "dns_sd.h" #if 0 #pragma mark == Structures == @@ -176,7 +195,7 @@ DEBUG_LOCAL size_t QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *in //=========================================================================================================================== // {B600E6E9-553B-4a19-8696-335E5C896153} -// GUID kRendezvousNSPGUID = { 0xb600e6e9, 0x553b, 0x4a19, { 0x86, 0x96, 0x33, 0x5e, 0x5c, 0x89, 0x61, 0x53 } }; +// GUID kmdnsNSPGUID = { 0xb600e6e9, 0x553b, 0x4a19, { 0x86, 0x96, 0x33, 0x5e, 0x5c, 0x89, 0x61, 0x53 } }; DEBUG_LOCAL LONG gRefCount = 0; DEBUG_LOCAL CRITICAL_SECTION gLock; @@ -200,7 +219,7 @@ BOOL APIENTRY DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved ) switch( inReason ) { case DLL_PROCESS_ATTACH: - debug_initialize( kDebugOutputTypeWindowsEventLog, "Rendezvous NSP", inInstance ); + debug_initialize( kDebugOutputTypeWindowsEventLog, "mDNS NSP", inInstance ); debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelInfo ); dlog( kDebugLevelTrace, "\n" ); dlog( kDebugLevelVerbose, "%s: process attach\n", __ROUTINE__ ); @@ -319,7 +338,6 @@ int WSPAPI NSPCleanup( LPGUID inProviderID ) if( gDNSSDInitialized ) { gDNSSDInitialized = false; - DNSServiceFinalize(); } if( gLockInitialized ) { @@ -376,7 +394,7 @@ DEBUG_LOCAL int WSPAPI // 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 ); + require_action_quiet( inFlags & (LUP_RETURN_ADDR|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 ); @@ -409,21 +427,71 @@ DEBUG_LOCAL int WSPAPI p = name + ( size - 1 ); p = ( *p == '.' ) ? ( p - sizeof_string( ".local" ) ) : ( ( p - sizeof_string( ".local" ) ) + 1 ); + if ( ( ( 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' ) ) ) ) + { + require_action_quiet( size > sizeof_string( ".0.8.e.f.ip6.arpa" ), exit, err = WSASERVICE_NOT_FOUND ); + + p = name + ( size - 1 ); + p = ( *p == '.' ) ? ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) : ( ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) + 1 ); + + if ( ( ( p[ 0 ] != '.' ) || + ( ( p[ 1 ] != '0' ) ) || + ( ( p[ 2 ] != '.' ) ) || + ( ( p[ 3 ] != '8' ) ) || + ( ( p[ 4 ] != '.' ) ) || + ( ( p[ 5 ] != 'E' ) && ( p[ 5 ] != 'e' ) ) || + ( ( p[ 6 ] != '.' ) ) || + ( ( p[ 7 ] != 'F' ) && ( p[ 7 ] != 'f' ) ) || + ( ( p[ 8 ] != '.' ) ) || + ( ( p[ 9 ] != 'I' ) && ( p[ 9 ] != 'i' ) ) || + ( ( p[ 10 ] != 'P' ) && ( p[ 10 ] != 'p' ) ) || + ( ( p[ 11 ] != '6' ) ) || + ( ( p[ 12 ] != '.' ) ) || + ( ( p[ 13 ] != 'A' ) && ( p[ 13 ] != 'a' ) ) || + ( ( p[ 14 ] != 'R' ) && ( p[ 14 ] != 'r' ) ) || + ( ( p[ 15 ] != 'P' ) && ( p[ 15 ] != 'p' ) ) || + ( ( p[ 16 ] != 'A' ) && ( p[ 16 ] != 'a' ) ) ) ) + { + require_action_quiet( size > sizeof_string( ".254.169.in-addr.arpa" ), exit, err = WSASERVICE_NOT_FOUND ); + + p = name + ( size - 1 ); + p = ( *p == '.' ) ? ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) : ( ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) + 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. + ( ( p[ 1 ] == '2' ) ) && + ( ( p[ 2 ] == '5' ) ) && + ( ( p[ 3 ] == '4' ) ) && + ( ( p[ 4 ] == '.' ) ) && + ( ( p[ 5 ] == '1' ) ) && + ( ( p[ 6 ] == '6' ) ) && + ( ( p[ 7 ] == '9' ) ) && + ( ( p[ 8 ] == '.' ) ) && + ( ( p[ 9 ] == 'I' ) || ( p[ 9 ] == 'i' ) ) && + ( ( p[ 10 ] == 'N' ) || ( p[ 10 ] == 'n' ) ) && + ( ( p[ 11 ] == '-' ) ) && + ( ( p[ 12 ] == 'A' ) || ( p[ 12 ] == 'a' ) ) && + ( ( p[ 13 ] == 'D' ) || ( p[ 13 ] == 'd' ) ) && + ( ( p[ 14 ] == 'D' ) || ( p[ 14 ] == 'd' ) ) && + ( ( p[ 15 ] == 'R' ) || ( p[ 15 ] == 'r' ) ) && + ( ( p[ 16 ] == '.' ) ) && + ( ( p[ 17 ] == 'A' ) || ( p[ 17 ] == 'a' ) ) && + ( ( p[ 18 ] == 'R' ) || ( p[ 18 ] == 'r' ) ) && + ( ( p[ 19 ] == 'P' ) || ( p[ 19 ] == 'p' ) ) && + ( ( p[ 20 ] == 'A' ) || ( p[ 20 ] == 'a' ) ) ), + exit, err = WSASERVICE_NOT_FOUND ); + } + } + + // The name ends in .local, .0.8.e.f.ip6.arpa, or .254.169.in-addr.arpa so start the resolve operation. Lazy initialize DNS-SD if needed. NSPLock(); if( !gDNSSDInitialized ) { - err = DNSServiceInitialize( kDNSServiceInitializeFlagsNoServerCheck, 0 ); - require_noerr( err, exit ); gDNSSDInitialized = true; } @@ -485,6 +553,7 @@ DEBUG_LOCAL int WSPAPI 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 ); + DNSServiceProcessResult(obj->resolver); require_action_quiet( obj->addrValid, exit, err = WSA_E_NO_MORE ); // Copy the externalized query results to the callers buffer (if it fits). @@ -687,6 +756,16 @@ DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQueryS obj->cancelEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); require_action( obj->cancelEvent, exit, err = WSA_NOT_ENOUGH_MEMORY ); + // Start the query. + + err = DNSServiceQueryRecord( &obj->resolver, 0, 0, name, kDNSServiceType_A, kDNSServiceClass_IN, + QueryRecordCallback, obj ); + require_noerr( err, exit ); + + // Attach the socket to the event + + WSAEventSelect(DNSServiceRefSockFD(obj->resolver), obj->dataEvent, FD_READ|FD_CLOSE); + obj->waitCount = 0; obj->waitHandles[ obj->waitCount++ ] = obj->dataEvent; obj->waitHandles[ obj->waitCount++ ] = obj->cancelEvent; @@ -699,12 +778,6 @@ DEBUG_LOCAL OSStatus QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQueryS 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; @@ -849,8 +922,8 @@ DEBUG_LOCAL void CALLBACK_COMPAT check( obj ); require_noerr( inErrorCode, exit ); require_quiet( inFlags & kDNSServiceFlagsAdd, exit ); - require( inRRClass == kDNSServiceDNSClass_IN, exit ); - require( inRRType == kDNSServiceDNSType_A, exit ); + require( inRRClass == kDNSServiceClass_IN, exit ); + require( inRRType == kDNSServiceType_A, exit ); require( inRDataSize == 4, exit ); dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=%s, rrType=%d, rDataSize=%d)\n", diff --git a/mDNSWindows/Applications/mdnsNSP/mdnsNSP.def b/mDNSWindows/mdnsNSP/mdnsNSP.def similarity index 77% rename from mDNSWindows/Applications/mdnsNSP/mdnsNSP.def rename to mDNSWindows/mdnsNSP/mdnsNSP.def index 948f863..16f4f18 100644 --- a/mDNSWindows/Applications/mdnsNSP/mdnsNSP.def +++ b/mDNSWindows/mdnsNSP/mdnsNSP.def @@ -23,13 +23,19 @@ ; Change History (most recent first): ; ; $Log: mdnsNSP.def,v $ +; Revision 1.2 2004/07/13 21:24:28 rpantos +; Fix for . +; +; Revision 1.1 2004/06/18 04:13:44 rpantos +; Move up one level. +; ; 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. +; mDNS NameSpace Provider (NSP). Hooks into the Windows name resolution system to +; perform .local name lookups using Multicast DNS in all Windows apps. ; ; -LIBRARY RendezvousNSP +LIBRARY mdnsNSP EXPORTS NSPStartup diff --git a/mDNSWindows/mdnsNSP/mdnsNSP.rc b/mDNSWindows/mdnsNSP/mdnsNSP.rc new file mode 100644 index 0000000..e0d4772 --- /dev/null +++ b/mDNSWindows/mdnsNSP/mdnsNSP.rc @@ -0,0 +1,104 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "WinVersRes.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" + "#include ""WinVersRes.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION MASTER_PROD_VERS + PRODUCTVERSION MASTER_PROD_VERS + FILEFLAGSMASK 0x17L +#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", "mdnsNSP Dynamic Link Library" + VALUE "FileVersion", MASTER_PROD_VERS_STR + VALUE "InternalName", "mdnsNSP" + VALUE "LegalCopyright", "Copyright (C) 2004" + VALUE "OriginalFilename", "mdnsNSP.dll" + VALUE "ProductName", MASTER_PROD_NAME + VALUE "ProductVersion", MASTER_PROD_VERS_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/mDNSWindows/mdnsNSP/mdnsNSP.vcproj b/mDNSWindows/mdnsNSP/mdnsNSP.vcproj new file mode 100644 index 0000000..5e49db5 --- /dev/null +++ b/mDNSWindows/mdnsNSP/mdnsNSP.vcproj @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSWindows/mdnsNSP/resource.h b/mDNSWindows/mdnsNSP/resource.h new file mode 100644 index 0000000..818f083 --- /dev/null +++ b/mDNSWindows/mdnsNSP/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by mdnsNSP.rc +// +#define IDS_PROJNAME 100 +#define IDR_WMDMLOGGER 101 +#define IDS_LOG_SEV_INFO 201 +#define IDS_LOG_SEV_WARN 202 +#define IDS_LOG_SEV_ERROR 203 +#define IDS_LOG_DATETIME 204 +#define IDS_LOG_SRCNAME 205 +#define IDS_DEF_LOGFILE 301 +#define IDS_DEF_MAXSIZE 302 +#define IDS_DEF_SHRINKTOSIZE 303 +#define IDS_DEF_LOGENABLED 304 +#define IDS_MUTEX_TIMEOUT 401 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif -- 2.47.2

- @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 bd7611a..9868372 100644 --- a/mDNSShared/Java/DNSSDService.java +++ b/mDNSShared/Java/DNSSDService.java @@ -3,8 +3,6 @@ * * @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 13fad3b..bfb319a 100644 --- a/mDNSShared/Java/DomainListener.java +++ b/mDNSShared/Java/DomainListener.java @@ -3,8 +3,6 @@ * * @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 b57bd46..ef9940a 100644 --- a/mDNSShared/Java/JNISupport.c +++ b/mDNSShared/Java/JNISupport.c @@ -3,8 +3,6 @@ * * @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 @@ -25,8 +23,30 @@ 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 doesn't build on Tiger8A132 +: Java project build errors Revision 1.1 2004/04/30 16:29:35 rpantos First checked in. @@ -34,46 +54,45 @@ 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 it does on Windows). +// callbacks automatically (as in the early Windows prototypes). // AUTO_CALLBACKS should be set to 0 if the client must call DNSServiceProcessResult() to -// invoke response callbacks (as is true on Mac OS X). +// 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.) #ifndef AUTO_CALLBACKS #define AUTO_CALLBACKS 0 #endif -// (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 +#if !AUTO_CALLBACKS +#ifdef _WIN32 +#include +#else //_WIN32 +#include +#include +#endif // _WIN32 +#endif // AUTO_CALLBACKS + +#include #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 @@ -112,16 +131,6 @@ 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; @@ -189,7 +198,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) { @@ -303,7 +312,7 @@ JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv } -static void CALLBACK_COMPAT ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, +static void DNSSD_API ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) { @@ -368,7 +377,7 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv * } -static void CALLBACK_COMPAT ServiceResolveReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, +static void DNSSD_API 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) { @@ -453,7 +462,7 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv } -static void CALLBACK_COMPAT ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, +static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *fullname, const char *regType, const char *domain, void *context) { @@ -564,23 +573,28 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv return err; } -JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_UpdateRecord( JNIEnv *pEnv, jobject pThis, - jobject destObj, jint flags, jbyteArray rData, jint ttl) +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, jobject pThis, + jint flags, jbyteArray rData, jint ttl) { jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "I"); - jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); - jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "I"); + jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "I"); OpContext *pContext = NULL; DNSServiceErrorType err = kDNSServiceErr_NoError; jbyte *pBytes; jsize numBytes; DNSRecordRef recRef = NULL; - if ( contextField != 0) - pContext = (OpContext*) (*pEnv)->GetIntField( pEnv, pThis, contextField); + 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 ( recField != 0) - recRef = (DNSRecordRef) (*pEnv)->GetIntField( pEnv, destObj, recField); + recRef = (DNSRecordRef) (*pEnv)->GetIntField( pEnv, pThis, recField); if ( pContext == NULL || pContext->ServiceRef == NULL) return kDNSServiceErr_BadParam; @@ -595,31 +609,35 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_UpdateRecord( JNIE return err; } -JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_RemoveRecord( JNIEnv *pEnv, jobject pThis, - jobject destObj, jint flags) +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv *pEnv, jobject pThis) { jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); - jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "I"); - jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); - jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "I"); + jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "I"); OpContext *pContext = NULL; DNSServiceErrorType err = kDNSServiceErr_NoError; DNSRecordRef recRef = NULL; - if ( contextField != 0) - pContext = (OpContext*) (*pEnv)->GetIntField( pEnv, pThis, contextField); + 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 ( recField != 0) - recRef = (DNSRecordRef) (*pEnv)->GetIntField( pEnv, destObj, recField); + recRef = (DNSRecordRef) (*pEnv)->GetIntField( pEnv, pThis, recField); if ( pContext == NULL || pContext->ServiceRef == NULL) return kDNSServiceErr_BadParam; - err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, flags); + err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0); return err; } -static void CALLBACK_COMPAT ServiceQueryReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, +static void DNSSD_API 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) @@ -685,7 +703,7 @@ JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv *pEnv } -static void CALLBACK_COMPAT DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, +static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *replyDomain, void *context) { OpContext *pContext = (OpContext*) context; @@ -784,5 +802,141 @@ 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 1a8637b..7ece74c 100644 --- a/mDNSShared/Java/QueryListener.java +++ b/mDNSShared/Java/QueryListener.java @@ -3,8 +3,6 @@ * * @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 28c9fa5..a6cf331 100644 --- a/mDNSShared/Java/RegisterListener.java +++ b/mDNSShared/Java/RegisterListener.java @@ -3,8 +3,6 @@ * * @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 ac606cf..0e21353 100644 --- a/mDNSShared/Java/ResolveListener.java +++ b/mDNSShared/Java/ResolveListener.java @@ -3,8 +3,6 @@ * * @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 97bcea9..5eed93e 100644 --- a/mDNSShared/Java/TXTRecord.java +++ b/mDNSShared/Java/TXTRecord.java @@ -3,8 +3,6 @@ * * @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 @@ -25,6 +23,15 @@ 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. @@ -42,7 +49,7 @@ package com.apple.dnssd; /** Object used to construct and parse DNS-SD format TXT records. - For more info see DNS-SD TXT record format. + For more info see DNS-Based Service Discovery, section 6. */ public class TXTRecord @@ -90,10 +97,8 @@ 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"); @@ -108,17 +113,66 @@ public class TXTRecord if ( keyBytes.length + valLen >= 255) throw new ArrayIndexOutOfBoundsException(); - newLen = (byte) ( keyBytes.length + valLen + (valLen > 0 ? 1 : 0)); - 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 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[ oldBytes.length + 1 + keyBytes.length] = kAttrSep; - System.arraycopy( value, 0, fBytes, oldBytes.length + keyBytes.length + 2, value.length); + 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; } + return -1; } /** Return the number of keys in the TXT record. */ @@ -187,6 +241,7 @@ public class TXTRecord { value = new byte[ avLen - aLen - 1]; System.arraycopy( fBytes, avStart + aLen + 2, value, 0, avLen - aLen - 1); + break; } } } @@ -234,5 +289,26 @@ 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/PlatformCommon.c b/mDNSShared/PlatformCommon.c new file mode 100644 index 0000000..4525eb4 --- /dev/null +++ b/mDNSShared/PlatformCommon.c @@ -0,0 +1,131 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * 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@ + + Change History (most recent first): + +$Log: PlatformCommon.c,v $ +Revision 1.3 2004/12/13 17:46:52 cheshire +Use sizeof(buf) instead of fixed constant 1024 + +Revision 1.2 2004/12/01 03:30:29 cheshire + Add Unicast DNS support to mDNSPosix + +Revision 1.1 2004/12/01 01:51:35 cheshire +Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c + + */ + +#include // Needed for fopen() etc. +#include // Needed for close() +#include // Needed for strlen() etc. +#include // Needed for errno etc. +#include // Needed for socket() etc. +#include // Needed for sockaddr_in + +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "PlatformCommon.h" + +#ifdef NOT_HAVE_SOCKLEN_T + typedef unsigned int socklen_t; +#endif + +// Bind a UDP socket to a global destination to find the default route's interface address +mDNSexport void FindDefaultRouteIP(mDNSAddr *a) + { + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + int sock = socket(AF_INET,SOCK_DGRAM,0); + a->type = mDNSAddrType_None; + if (sock == -1) return; + addr.sin_family = AF_INET; + addr.sin_port = 1; // Not important, any port and public address will do + addr.sin_addr.s_addr = 0x11111111; + if ((connect(sock,(const struct sockaddr*)&addr,sizeof(addr))) == -1) { close(sock); return; } + if ((getsockname(sock,(struct sockaddr*)&addr, &len)) == -1) { close(sock); return; } + close(sock); + a->type = mDNSAddrType_IPv4; + a->ip.v4.NotAnInteger = addr.sin_addr.s_addr; + } + +// dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 20 bytes in length +mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f) + { + char buf[20+1+MAX_ESCAPED_DOMAIN_NAME]; // Option name, one space, option value + unsigned int len = strlen(option); + if (len + 1 + MAX_ESCAPED_DOMAIN_NAME > sizeof(buf)-1) { LogMsg("GetConfigOption: option %s too long", option); return mDNSfalse; } + fseek(f, 0, SEEK_SET); // set position to beginning of stream + while (fgets(buf, sizeof(buf), f)) // Read at most sizeof(buf)-1 bytes from file, and append '\0' C-string terminator + { + if (!strncmp(buf, option, len)) + { + strncpy(dst, buf + len + 1, MAX_ESCAPED_DOMAIN_NAME-1); + if (dst[MAX_ESCAPED_DOMAIN_NAME-1]) dst[MAX_ESCAPED_DOMAIN_NAME-1] = '\0'; + len = strlen(dst); + if (len && dst[len-1] == '\n') dst[len-1] = '\0'; // chop newline + return mDNStrue; + } + } + debugf("Option %s not set", option); + return mDNSfalse; + } + +mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain) + { + char zone [MAX_ESCAPED_DOMAIN_NAME]; + char fqdn [MAX_ESCAPED_DOMAIN_NAME]; + char secret[MAX_ESCAPED_DOMAIN_NAME] = ""; + int slen; + mStatus err; + FILE *f = fopen(filename, "r"); + + hostname->c[0] = 0; + domain->c[0] = 0; + + if (f) + { + if (GetConfigOption(fqdn, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, fqdn)) goto badf; + if (GetConfigOption(zone, "zone", f) && !MakeDomainNameFromDNSNameString(domain, zone)) goto badf; + GetConfigOption(secret, "secret-64", f); // failure means no authentication + fclose(f); + f = NULL; + } + else + { + if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened."); + return; + } + + if (secret[0]) + { + // for now we assume keyname = service reg domain and we use same key for service and hostname registration + slen = strlen(secret); + err = mDNS_SetSecretForZone(m, domain, domain, secret, slen, mDNStrue); + if (err) LogMsg("ERROR: mDNS_SetSecretForZone returned %d for domain %##s", err, domain->c); + } + + return; + + badf: + LogMsg("ERROR: malformatted config file"); + if (f) fclose(f); + } diff --git a/mDNSShared/PlatformCommon.h b/mDNSShared/PlatformCommon.h new file mode 100644 index 0000000..9e4638b --- /dev/null +++ b/mDNSShared/PlatformCommon.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * 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@ + + Change History (most recent first): + +$Log: PlatformCommon.h,v $ +Revision 1.2 2004/12/01 03:30:29 cheshire + Add Unicast DNS support to mDNSPosix + +Revision 1.1 2004/12/01 01:51:35 cheshire +Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c + + */ + +extern void FindDefaultRouteIP(mDNSAddr *a); +extern void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain); diff --git a/mDNSShared/dns-sd.1 b/mDNSShared/dns-sd.1 new file mode 100644 index 0000000..a8a602b --- /dev/null +++ b/mDNSShared/dns-sd.1 @@ -0,0 +1,208 @@ +.\" 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: dns-sd.1,v $ +.\" Revision 1.2 2004/09/24 18:33:05 cheshire +.\" Update man pages to clarify that mDNS and dns-sd are not intended for script use +.\" +.\" Revision 1.1 2004/09/22 22:46:25 cheshire +.\" Man page for dns-sd command-line tool +.\" +.\" +.\" +.Dd April 2004 \" Date +.Dt dns-sd 1 \" Document Title +.Os Darwin \" Operating System +.\" +.Sh NAME +.Nm dns-sd +.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 . +However, unlike those tools, most of its functionality is not implemented in the +.Nm +executable itself, but in library code that is available to any application. +The library API that +.Nm +uses is documented in +.Pa /usr/include/dns_sd.h . +The +.Nm +command replaces the older +.Xr mDNS 1 +command. +.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 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. +.br +If you wish to perform DNS Service Discovery operations from a +scripting language, then the best way to do this is not to execute the +.Nm +command and then attempt to decipher the textual output, but instead to +directly call the DNS-SD APIs using a binding for your chosen language. +.br +For example, if you are programming in Ruby, then you can +directly call DNS-SD APIs using the dnssd package documented at +.Pa . +.br +Similar bindings for other languages are also in development. +.Pp +.Bl -tag -width R +.It Nm 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 Nm 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 Nm 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 Nm Fl R Ns \ \&"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 Nm Fl R Ns \ \&"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 Nm Fl B Ns \ _http._tcp +.Pp +While that command is running, in another window, try the +.Nm Fl R +example given above to advertise a web page, and you should see the +"Add" event reported to the +.Nm Fl B +window. Now press Ctrl-C in the +.Nm Fl R +window and you should see the "Remove" event reported to the +.Nm Fl B +window. +.Pp +.Sh FILES +.Pa /usr/bin/dns-sd \" Pathname +.\" +.Sh SEE ALSO +.Xr mDNS 1 +.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/dns_sd.h b/mDNSShared/dns_sd.h index 45f51b7..7d14fca 100755 --- a/mDNSShared/dns_sd.h +++ b/mDNSShared/dns_sd.h @@ -1,84 +1,28 @@ /* - * 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 + * 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. */ #ifndef _DNS_SD_H @@ -88,9 +32,28 @@ Update to APSL 2.0 extern "C" { #endif -#if defined(__FreeBSD__) && (__FreeBSD_version < 500000) +/* 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) /* 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 @@ -155,10 +118,89 @@ 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 { @@ -175,12 +217,16 @@ 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_NoSuchKey = -65556, + kDNSServiceErr_NATTraversal = -65557, + kDNSServiceErr_DblNAT = -65558, + kDNSServiceErr_BadTime = -65559 /* mDNS Error codes are in the range * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ }; @@ -191,13 +237,91 @@ enum #define kDNSServiceMaxDomainName 1005 -/* 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 +/* + * 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 pass kDNSServiceInterfaceIndexLocalOnly when browsing. + * 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. + * Note that to discover these special non-public services, the browsing + * client also has to explicitly use kDNSServiceInterfaceIndexLocalOnly in its + * DNSServiceBrowse() call. These special non-public services are not reported to + * other clients on the same machine using interface index 0 or other index values. + * + * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing + * then it will find only records registered on that same local machine. + * Note that this is *not* exactly symmetrical with the registering case: + * Services advertised using LocalOnly are ONLY discovered by clients browsing + * on LocalOnly; in contrast, clients browsing on LocalOnly find ALL services + * advertised by this machine, not only those advertised on LocalOnly. + * 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. */ #define kDNSServiceInterfaceIndexAny 0 -#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) ~0 ) +#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) -1 ) typedef uint32_t DNSServiceFlags; @@ -232,7 +356,7 @@ typedef int32_t DNSServiceErrorType; * error. */ -int DNSServiceRefSockFD(DNSServiceRef sdRef); +int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); /* DNSServiceProcessResult() @@ -253,7 +377,7 @@ int DNSServiceRefSockFD(DNSServiceRef sdRef); * an error code indicating the specific failure that occurred. */ -DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef); +DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); /* DNSServiceRefDeallocate() @@ -282,7 +406,7 @@ DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef); * */ -void DNSServiceRefDeallocate(DNSServiceRef sdRef); +void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); /********************************************************************************************* @@ -294,11 +418,16 @@ void 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: * @@ -321,7 +450,7 @@ void DNSServiceRefDeallocate(DNSServiceRef sdRef); * */ -typedef void (*DNSServiceDomainEnumReply) +typedef void (DNSSD_API *DNSServiceDomainEnumReply) ( DNSServiceRef sdRef, DNSServiceFlags flags, @@ -335,8 +464,10 @@ typedef void (*DNSServiceDomainEnumReply) /* DNSServiceEnumerateDomains() Parameters: * * - * sdRef: A pointer to an uninitialized DNSServiceRef. May be passed to - * DNSServiceRefDeallocate() to cancel the enumeration. + * 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(). * * flags: Possible values are: * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing. @@ -346,7 +477,7 @@ typedef void (*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. + * all interfaces. See "Constants for specifying an interface index" for more details. * * callBack: The function to be called when a domain is found or the call asynchronously * fails. @@ -360,7 +491,7 @@ typedef void (*DNSServiceDomainEnumReply) * is not initialized.) */ -DNSServiceErrorType DNSServiceEnumerateDomains +DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -403,7 +534,7 @@ DNSServiceErrorType DNSServiceEnumerateDomains * */ -typedef void (*DNSServiceRegisterReply) +typedef void (DNSSD_API *DNSServiceRegisterReply) ( DNSServiceRef sdRef, DNSServiceFlags flags, @@ -417,15 +548,15 @@ typedef void (*DNSServiceRegisterReply) /* DNSServiceRegister() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. If this call succeeds, the reference - * may be passed to - * DNSServiceRefDeallocate() to deregister the service. + * 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(). * * interfaceIndex: If non-zero, specifies the interface on which to register the service * (the index for a given interface is determined via the if_nametoindex() * family of calls.) Most applications will pass 0 to register on all - * available interfaces. Pass -1 to register a service only on the local - * machine (service will not be visible to remote hosts.) + * available interfaces. See "Constants for specifying an interface index" for more details. * * flags: Indicates the renaming behavior on name conflict (most applications * will pass 0). See flag definitions above for details. @@ -478,7 +609,7 @@ typedef void (*DNSServiceRegisterReply) * */ -DNSServiceErrorType DNSServiceRegister +DNSServiceErrorType DNSSD_API DNSServiceRegister ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -514,19 +645,19 @@ DNSServiceErrorType DNSServiceRegister * * flags: Currently ignored, reserved for future use. * - * rrtype: The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h. + * rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc) * * 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. + * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. * * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an * error code indicating the error that occurred (the RecordRef is not initialized). */ -DNSServiceErrorType DNSServiceAddRecord +DNSServiceErrorType DNSSD_API DNSServiceAddRecord ( DNSServiceRef sdRef, DNSRecordRef *RecordRef, @@ -566,7 +697,7 @@ DNSServiceErrorType DNSServiceAddRecord * error code indicating the error that occurred. */ -DNSServiceErrorType DNSServiceUpdateRecord +DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord ( DNSServiceRef sdRef, DNSRecordRef RecordRef, /* may be NULL */ @@ -598,7 +729,7 @@ DNSServiceErrorType DNSServiceUpdateRecord * error code indicating the error that occurred. */ -DNSServiceErrorType DNSServiceRemoveRecord +DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord ( DNSServiceRef sdRef, DNSRecordRef RecordRef, @@ -629,19 +760,29 @@ DNSServiceErrorType DNSServiceRemoveRecord * indicate the failure that occurred. Other parameters are undefined if * the errorCode is nonzero. * - * serviceName: The service name discovered. + * serviceName: The discovered service name. This name should be displayed to the user, + * and stored for subsequent use in the DNSServiceResolve() call. * - * regtype: The service type, as passed in to DNSServiceBrowse(). + * 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. * - * 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.) + * 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. * * context: The context pointer that was passed to the callout. * */ -typedef void (*DNSServiceBrowseReply) +typedef void (DNSSD_API *DNSServiceBrowseReply) ( DNSServiceRef sdRef, DNSServiceFlags flags, @@ -656,15 +797,17 @@ typedef void (*DNSServiceBrowseReply) /* DNSServiceBrowse() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. May be passed to - * DNSServiceRefDeallocate() to terminate the browse. + * 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(). * * flags: Currently ignored, reserved for future use. * * interfaceIndex: If non-zero, specifies the interface on which to browse for services * (the index for a given interface is determined via the if_nametoindex() * family of calls.) Most applications will pass 0 to browse on all available - * interfaces. Pass -1 to only browse for services provided on the local host. + * interfaces. See "Constants for specifying an interface index" for more details. * * 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". @@ -685,7 +828,7 @@ typedef void (*DNSServiceBrowseReply) * is not initialized.) */ -DNSServiceErrorType DNSServiceBrowse +DNSServiceErrorType DNSSD_API DNSServiceBrowse ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -725,12 +868,10 @@ DNSServiceErrorType DNSServiceBrowse * the errorCode is nonzero. * * fullname: The full service domain name, in the form ... - * (Any literal dots (".") are escaped with a backslash ("\."), and literal - * backslashes are escaped with a second backslash ("\\"), e.g. a web server - * named "Dr. Pepper" would have the fullname "Dr\.\032Pepper._http._tcp.local."). - * This is the appropriate format to pass to standard system DNS APIs such as - * res_query(), or to the special-purpose functions included in this API that - * take fullname parameters. + * (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.) * * 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. @@ -746,7 +887,7 @@ DNSServiceErrorType DNSServiceBrowse * */ -typedef void (*DNSServiceResolveReply) +typedef void (DNSSD_API *DNSServiceResolveReply) ( DNSServiceRef sdRef, DNSServiceFlags flags, @@ -763,23 +904,29 @@ typedef void (*DNSServiceResolveReply) /* DNSServiceResolve() Parameters * - * sdRef: A pointer to an uninitialized DNSServiceRef. May be passed to - * DNSServiceRefDeallocate() to terminate the resolve. + * 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(). * * flags: Currently ignored, reserved for future use. * - * interfaceIndex: The interface on which to resolve the service. The client should - * pass the interface on which the servicename was discovered, i.e. - * the interfaceIndex passed to the DNSServiceBrowseReply callback, - * or 0 to resolve the named service on all available interfaces. + * 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. * - * name: The servicename to be resolved. + * name: The name 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". + * regtype: The type 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. + * domain: The domain of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. * * callBack: The function to be called when a result is found, or if the call * asynchronously fails. @@ -793,7 +940,7 @@ typedef void (*DNSServiceResolveReply) * is not initialized.) */ -DNSServiceErrorType DNSServiceResolve +DNSServiceErrorType DNSSD_API DNSServiceResolve ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -812,28 +959,6 @@ DNSServiceErrorType 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 @@ -851,7 +976,7 @@ DNSServiceErrorType DNSServiceResolve * case the DNSServiceRef is not initialized). */ -DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef); +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); /* DNSServiceRegisterRecord @@ -881,7 +1006,7 @@ DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef); * */ - typedef void (*DNSServiceRegisterRecordReply) + typedef void (DNSSD_API *DNSServiceRegisterRecordReply) ( DNSServiceRef sdRef, DNSRecordRef RecordRef, @@ -907,21 +1032,19 @@ DNSServiceErrorType 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. - * Passing -1 causes the record to only be visible on the local host. + * See "Constants for specifying an interface index" for more details. * * fullname: The full domain name of the resource record. * - * rrtype: The numerical type of the resource record (e.g. PTR, SRV, etc), as defined - * in nameser.h. + * rrtype: The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) * - * rrclass: The class of the resource record, as defined in nameser.h (usually 1 for the - * Internet class). + * rrclass: The class of the resource record (usually kDNSServiceClass_IN) * * 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. + * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. * * callBack: The function to be called when a result is found, or if the call * asynchronously fails (e.g. because of a name conflict.) @@ -935,7 +1058,7 @@ DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef); * not initialized.) */ -DNSServiceErrorType DNSServiceRegisterRecord +DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord ( DNSServiceRef sdRef, DNSRecordRef *RecordRef, @@ -967,6 +1090,7 @@ DNSServiceErrorType 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 @@ -974,9 +1098,9 @@ DNSServiceErrorType DNSServiceRegisterRecord * * fullname: The resource record's full domain name. * - * rrtype: The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) * - * rrclass: The class of the resource record, as defined in nameser.h (usually 1). + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). * * rdlen: The length, in bytes, of the resource record rdata. * @@ -988,7 +1112,7 @@ DNSServiceErrorType DNSServiceRegisterRecord * */ -typedef void (*DNSServiceQueryRecordReply) +typedef void (DNSSD_API *DNSServiceQueryRecordReply) ( DNSServiceRef DNSServiceRef, DNSServiceFlags flags, @@ -1006,7 +1130,10 @@ typedef void (*DNSServiceQueryRecordReply) /* DNSServiceQueryRecord() Parameters: * - * sdRef: A pointer to an uninitialized DNSServiceRef. + * 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(). * * flags: Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast * query in a non-local domain. Without setting this flag, unicast queries @@ -1018,16 +1145,14 @@ typedef void (*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. Passing -1 causes the name to be queried for only on the - * local host. + * interfaces. See "Constants for specifying an interface index" for more details. * * fullname: The full domain name of the resource record to be queried for. * - * rrtype: The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc) - * as defined in nameser.h. + * rrtype: The numerical type of the resource record to be queried for + * (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) * - * rrclass: The class of the resource record, as defined in nameser.h - * (usually 1 for the Internet class). + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). * * callBack: The function to be called when a result is found, or if the call * asynchronously fails. @@ -1041,7 +1166,7 @@ typedef void (*DNSServiceQueryRecordReply) * is not initialized.) */ -DNSServiceErrorType DNSServiceQueryRecord +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord ( DNSServiceRef *sdRef, DNSServiceFlags flags, @@ -1065,11 +1190,14 @@ DNSServiceErrorType 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. PTR, SRV, etc) as defined in nameser.h. + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) * - * rrclass: The class of the resource record, as defined in nameser.h (usually 1). + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). * * rdlen: The length, in bytes, of the resource record rdata. * @@ -1077,7 +1205,7 @@ DNSServiceErrorType DNSServiceQueryRecord * */ -void DNSServiceReconfirmRecord +void DNSSD_API DNSServiceReconfirmRecord ( DNSServiceFlags flags, uint32_t interfaceIndex, @@ -1107,21 +1235,21 @@ void 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 slashes must NOT be escaped. + * service: The service name - any dots or backslashes 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". Any literal dots or backslashes - * must be escaped. + * domain: The domain name, e.g. "apple.com.". Literal dots or backslashes, + * if any, must be escaped, e.g. "1st\. Floor.apple.com." * * return value: Returns 0 on success, -1 on error. * */ -int DNSServiceConstructFullName +int DNSSD_API DNSServiceConstructFullName ( char *fullName, const char *service, /* may be NULL */ @@ -1157,7 +1285,7 @@ int DNSServiceConstructFullName * Note: Represents a DNS-SD TXT record. */ -typedef struct _TXTRecordRef_t { char private[16]; } TXTRecordRef; +typedef struct _TXTRecordRef_t { char privatedata[16]; } TXTRecordRef; /* TXTRecordCreate() @@ -1187,6 +1315,11 @@ typedef struct _TXTRecordRef_t { char private[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. @@ -1196,7 +1329,7 @@ typedef struct _TXTRecordRef_t { char private[16]; } TXTRecordRef; * the TXTRecordRef. */ -void TXTRecordCreate +void DNSSD_API TXTRecordCreate ( TXTRecordRef *txtRecord, uint16_t bufferLen, @@ -1214,7 +1347,7 @@ void TXTRecordCreate * */ -void TXTRecordDeallocate +void DNSSD_API TXTRecordDeallocate ( TXTRecordRef *txtRecord ); @@ -1237,7 +1370,7 @@ void TXTRecordDeallocate * * key: A null-terminated string which only contains printable ASCII * values (0x20-0x7E), excluding '=' (0x3D). Keys should be - * 14 characters or less (not counting the terminating null). + * 8 characters or less (not counting the terminating null). * * valueSize: The size of the value. * @@ -1257,7 +1390,7 @@ void TXTRecordDeallocate * exceed the available storage. */ -DNSServiceErrorType TXTRecordSetValue +DNSServiceErrorType DNSSD_API TXTRecordSetValue ( TXTRecordRef *txtRecord, const char *key, @@ -1281,7 +1414,7 @@ DNSServiceErrorType TXTRecordSetValue * */ -DNSServiceErrorType TXTRecordRemoveValue +DNSServiceErrorType DNSSD_API TXTRecordRemoveValue ( TXTRecordRef *txtRecord, const char *key @@ -1301,7 +1434,7 @@ DNSServiceErrorType TXTRecordRemoveValue * */ -uint16_t TXTRecordGetLength +uint16_t DNSSD_API TXTRecordGetLength ( const TXTRecordRef *txtRecord ); @@ -1319,7 +1452,7 @@ uint16_t TXTRecordGetLength * */ -const void * TXTRecordGetBytesPtr +const void * DNSSD_API TXTRecordGetBytesPtr ( const TXTRecordRef *txtRecord ); @@ -1374,7 +1507,7 @@ const void * TXTRecordGetBytesPtr * */ -int TXTRecordContainsKey +int DNSSD_API TXTRecordContainsKey ( uint16_t txtLen, const void *txtRecord, @@ -1403,7 +1536,7 @@ int TXTRecordContainsKey * For non-empty value, valueLen will be length of value data. */ -const void * TXTRecordGetValuePtr +const void * DNSSD_API TXTRecordGetValuePtr ( uint16_t txtLen, const void *txtRecord, @@ -1425,7 +1558,7 @@ const void * TXTRecordGetValuePtr * */ -uint16_t TXTRecordGetCount +uint16_t DNSSD_API TXTRecordGetCount ( uint16_t txtLen, const void *txtRecord @@ -1456,7 +1589,7 @@ uint16_t 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 - * 14 characters or less. To hold the maximum possible + * 8 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. @@ -1470,7 +1603,7 @@ uint16_t TXTRecordGetCount * TXTRecordGetCount()-1. */ -DNSServiceErrorType TXTRecordGetItemAtIndex +DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex ( uint16_t txtLen, const void *txtRecord, @@ -1481,6 +1614,39 @@ DNSServiceErrorType 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/dnsextd.8 b/mDNSShared/dnsextd.8 new file mode 100644 index 0000000..12009f6 --- /dev/null +++ b/mDNSShared/dnsextd.8 @@ -0,0 +1,80 @@ +.\" 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: dnsextd.8,v $ +.\" Revision 1.1 2004/08/15 18:49:18 cheshire +.\" No man page for dnsextd +.\" +.\" +.\" +.Dd August 2004 \" Date +.Dt dnsextd 8 \" Document Title +.Os Darwin \" Operating System +.\" +.Sh NAME +.Nm dnsextd +.Nd BIND Extension Daemon \" Name Description for whatis database +.\" +.Sh SYNOPSIS +.Nm +.\" +.Sh DESCRIPTION +.Nm +is a daemon invoked at boot time, running alongside BIND 9, +to implement two EDNS0 extensions to the standard DNS protocol. +.Pp +.Nm +allows clients to perform DNS Updates with an attached lease lifetime, +so that if the client crashes or is disconnected from the network, its +address records will be automatically deleted after the lease expires. +.Pp +.Nm +allows clients to perform long-lived queries. Instead of rapidly polling +the server to discover when information changes, long-lived queries +enable a client to indicate its interest in some set of data, and then +be notified asynchronously by the server whenever any of that data changes. +.Pp +.Nm +has no user-specifiable command-line argument, and users should not run +.Nm +manually. +.\" +.Sh SEE ALSO +.Xr mDNS 1 +.Xr mDNSResponder 8 +.Pp +For information on Dynamic DNS Update, see RFC 2136 +"Dynamic Updates in the Domain Name System (DNS UPDATE)" +.Pp +For information on Dynamic DNS Update Leases, see +.Pa http://files.dns-sd.org/draft-dns-update-leases.txt +.Pp +For information on Long-Lived Queries, see +.Pa http://files.dns-sd.org/draft-dns-llq.txt +.\" +.Sh BUGS +.Nm +bugs are tracked in Apple Radar component "mDNSResponder". +.\" +.Sh HISTORY +The +.Nm +daemon first appeared in Mac OS X 10.4 (Tiger). diff --git a/mDNSShared/dnssd_clientlib.c b/mDNSShared/dnssd_clientlib.c index d5a0f04..40ab0d0 100755 --- a/mDNSShared/dnssd_clientlib.c +++ b/mDNSShared/dnssd_clientlib.c @@ -1,30 +1,47 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * 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@ + * 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. Change History (most recent first): $Log: dnssd_clientlib.c,v $ +Revision 1.9 2004/10/06 02:22:19 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.8 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.7 2004/06/26 03:16:34 shersche +clean up warning messages on Win32 platform + +Submitted by: herscher + +Revision 1.6 2004/06/12 01:09:45 cheshire +To be callable from the broadest range of clients on Windows (e.g. Visual Basic, C#, etc.) +API routines have to be declared as "__stdcall", instead of the C default, "__cdecl" + 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. @@ -53,6 +70,11 @@ like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code #pragma export on #endif +#if defined(_WIN32) +// disable warning "conversion from to uint16_t" +#pragma warning(disable:4244) +#endif + /********************************************************************************************* * * Supporting Functions @@ -86,7 +108,7 @@ static uint8_t *InternalTXTRecordSearch { uint8_t *p = (uint8_t*)txtRecord; uint8_t *e = p + txtLen; - *keylen = strlen(key); + *keylen = (unsigned long) strlen(key); while (p Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.5 2004/09/21 23:29:51 cheshire + DNSServiceResolve should delay sending packets + +Revision 1.4 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + Revision 1.3 2004/05/27 06:26:31 cheshire Add shim for DNSServiceQueryRecord() @@ -46,7 +61,7 @@ like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code */ #include "dns_sd.h" // Defines the interface to the client layer above -#include "mDNSClientAPI.h" // The interface we're building on top of +#include "mDNSEmbeddedAPI.h" // The interface we're building on top of extern mDNS mDNSStorage; // We need to pass the address of this storage to the lower-layer functions #if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY @@ -75,8 +90,8 @@ typedef struct mDNS_DirectOP_Dispose *disposefn; DNSServiceRegisterReply callback; void *context; - mDNSBool autoname; - mDNSBool autorename; + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool autorename; // Set if we just got a name conflict and now need to automatically pick a new name domainlabel name; ServiceRecordSet s; } mDNS_DirectOP_Register; @@ -431,7 +446,7 @@ DNSServiceErrorType DNSServiceBrowse x->q.QuestionContext = x; // Do the operation - err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSInterface_Any, FoundInstance, x); + err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSInterface_Any, (flags & kDNSServiceFlagsForceMulticast) != 0, FoundInstance, x); if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_StartBrowse"; goto fail; } // Succeeded: Wrap up and return @@ -524,6 +539,9 @@ DNSServiceErrorType DNSServiceResolve AssignDomainName(x->qSRV.qname, srv); x->qSRV.qtype = kDNSType_SRV; x->qSRV.qclass = kDNSClass_IN; + x->qSRV.LongLived = mDNSfalse; + x->qSRV.ExpectUnique = mDNStrue; + x->qSRV.ForceMCast = mDNSfalse; x->qSRV.QuestionCallback = FoundServiceInfo; x->qSRV.QuestionContext = x; @@ -533,6 +551,9 @@ DNSServiceErrorType DNSServiceResolve AssignDomainName(x->qTXT.qname, srv); x->qTXT.qtype = kDNSType_TXT; x->qTXT.qclass = kDNSClass_IN; + x->qTXT.LongLived = mDNSfalse; + x->qTXT.ExpectUnique = mDNStrue; + x->qTXT.ForceMCast = mDNSfalse; x->qTXT.QuestionCallback = FoundServiceInfo; x->qTXT.QuestionContext = x; @@ -652,6 +673,9 @@ DNSServiceErrorType DNSServiceQueryRecord MakeDomainNameFromDNSNameString(&x->q.qname, fullname); x->q.qtype = rrtype; x->q.qclass = rrclass; + x->q.LongLived = (flags & kDNSServiceFlagsLongLivedQuery) != 0; + x->q.ExpectUnique = mDNSfalse; + x->q.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0; x->q.QuestionCallback = DNSServiceQueryRecordResponse; x->q.QuestionContext = x; diff --git a/mDNSShared/dnssd_clientstub.c b/mDNSShared/dnssd_clientstub.c index 6173ca7..d060a66 100755 --- a/mDNSShared/dnssd_clientstub.c +++ b/mDNSShared/dnssd_clientstub.c @@ -1,30 +1,104 @@ -/* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- * - * @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@ + * 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. Change History (most recent first): $Log: dnssd_clientstub.c,v $ +Revision 1.40 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.39 2004/11/12 03:22:00 rpantos +rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. + +Revision 1.38 2004/11/02 02:51:23 cheshire + Remove overly-restrictive flag checks + +Revision 1.37 2004/10/14 01:43:35 cheshire +Fix opaque port passing problem + +Revision 1.36 2004/10/06 02:22:19 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.35 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.34 2004/09/17 22:36:13 cheshire +Add comment explaining that deliver_request frees the message it sends + +Revision 1.33 2004/09/17 01:17:31 ksekar +Remove double-free of msg header, freed automatically by deliver_request() + +Revision 1.32 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.31 2004/09/16 23:37:19 cheshire +Free hdr before returning + +Revision 1.30 2004/09/16 23:14:24 cheshire +Changes for Windows compatibility + +Revision 1.29 2004/09/16 21:46:38 ksekar + Need SPI for LoginWindow to associate a UID with a Wide Area Rendezvous domain + +Revision 1.28 2004/08/11 17:10:04 cheshire +Fix signed/unsigned warnings + +Revision 1.27 2004/08/11 00:54:16 cheshire +Change "hdr->op.request_op" to just "hdr->op" + +Revision 1.26 2004/07/26 06:07:27 shersche +fix bugs when using an error socket to communicate with the daemon + +Revision 1.25 2004/07/26 05:54:02 shersche +DNSServiceProcessResult() returns NoError if socket read returns EWOULDBLOCK + +Revision 1.24 2004/07/20 06:46:21 shersche + fix endless loop in my_read() if recv returns 0 +Bug #: 3730123 + +Revision 1.23 2004/06/29 00:48:38 cheshire +Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; +use an explicit while() loop instead. + +Revision 1.22 2004/06/26 03:16:34 shersche +clean up warning messages on Win32 platform + +Submitted by: herscher + +Revision 1.21 2004/06/18 04:53:56 rpantos +Use platform layer for socket types. Introduce USE_TCP_LOOPBACK. Remove dependency on mDNSEmbeddedAPI.h. + +Revision 1.20 2004/06/12 00:50:22 cheshire +Changes for Windows compatibility + 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. @@ -76,111 +150,348 @@ Update to APSL 2.0 */ #include +#include +#if defined(_WIN32) +#include +#include +#define sockaddr_mdns sockaddr_in +#define AF_MDNS AF_INET +#else #include #include +#define sockaddr_mdns sockaddr_un +#define AF_MDNS AF_LOCAL +#endif #include "dnssd_ipc.h" +#if defined(_WIN32) +// disable warning: "'type cast' : from data pointer 'void *' to +// function pointer" +#pragma warning(disable:4055) + +// disable warning: "nonstandard extension, function/data pointer +// conversion in expression" +#pragma warning(disable:4152) + +static int g_initWinsock = 0; +#endif + #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); -// 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 + dnssd_sock_t sockfd; // connected socket between client and daemon + uint32_t op; // request_op_t or 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; + } _DNSServiceRef_t; typedef struct _DNSRecordRef_t { void *app_context; DNSServiceRegisterRecordReply app_callback; DNSRecordRef recref; - int record_index; // index is unique to the ServiceDiscoveryRef + uint32_t record_index; // index is unique to the ServiceDiscoveryRef DNSServiceRef sdr; } _DNSRecordRef_t; - // exported functions -int DNSServiceRefSockFD(DNSServiceRef sdRef) +// write len bytes. return 0 on success, -1 on error +static int my_write(dnssd_sock_t sd, char *buf, int len) + { + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. + //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; + } + +// read len bytes. return 0 on success, -1 on error +static int my_read(dnssd_sock_t sd, char *buf, int len) + { + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. + //if (recv(sd, buf, len, MSG_WAITALL) != len) return -1; + while (len) + { + ssize_t num_read = recv(sd, buf, len, 0); + if ((num_read == 0) || (num_read < 0) || (num_read > len)) return -1; + buf += num_read; + len -= num_read; + } + return 0; + } + +/* 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(uint32_t op, size_t *len, char **data_start, int reuse_socket) + { + char *msg = NULL; + ipc_msg_hdr *hdr; + int datalen; + char ctrl_path[256]; + + if (!reuse_socket) + { +#if defined(USE_TCP_LOOPBACK) + *len += 2; // Allocate space for two-byte port number +#else + struct timeval time; + 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; +#endif + } + + datalen = (int) *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 = op; + if (reuse_socket) hdr->flags |= IPC_FLAGS_REUSE_SOCKET; + *data_start = msg + sizeof(ipc_msg_hdr); +#if defined(USE_TCP_LOOPBACK) + // Put dummy data in for the port, since we don't know what + // it is yet. The data will get filled in before we + // send the message. This happens in deliver_request(). + if (!reuse_socket) put_short(0, data_start); +#else + if (!reuse_socket) put_string(ctrl_path, data_start); +#endif + return hdr; + } + + // return a connected service ref (deallocate with DNSServiceRefDeallocate) +static DNSServiceRef connect_to_server(void) + { + dnssd_sockaddr_t saddr; + DNSServiceRef sdr; + +#if defined(_WIN32) + if (!g_initWinsock) + { + WSADATA wsaData; + DNSServiceErrorType err; + + g_initWinsock = 1; + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsaData ); + + if (err != 0) return NULL; + } +#endif + + sdr = malloc(sizeof(_DNSServiceRef_t)); + if (!sdr) return(NULL); + sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (sdr->sockfd == dnssd_InvalidSocket) { free(sdr); return NULL; } +#if defined(USE_TCP_LOOPBACK) + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + saddr.sin_port = htons(MDNS_TCP_SERVERPORT); +#else + saddr.sun_family = AF_LOCAL; + strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH); +#endif + if (connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { free(sdr); return NULL; } + return sdr; + } + +static DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd) + { + ipc_msg_hdr *hdr = msg; + uint32_t datalen = hdr->datalen; + dnssd_sockaddr_t caddr, daddr; // (client and daemon address structs) + char *data = (char *)msg + sizeof(ipc_msg_hdr); + dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket; + int ret, len = sizeof(caddr); + DNSServiceErrorType err = kDNSServiceErr_Unknown; + + if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown; + + if (!reuse_sd) + { + // setup temporary error socket + if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) < 0) + goto cleanup; + bzero(&caddr, sizeof(caddr)); + +#if defined(USE_TCP_LOOPBACK) + { + union { uint16_t s; u_char b[2]; } port; + caddr.sin_family = AF_INET; + caddr.sin_port = 0; + caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + ret = bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr)); + if (ret < 0) goto cleanup; + if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) goto cleanup; + listen(listenfd, 1); + port.s = caddr.sin_port; + data[0] = port.b[0]; // don't switch the byte order, as the + data[1] = port.b[1]; // daemon expects it in network byte order + } +#else + { + mode_t mask = umask(0); + caddr.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. + caddr.sun_len = sizeof(struct sockaddr_un); +#endif + strcpy(caddr.sun_path, data); + ret = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)); + umask(mask); + if (ret < 0) goto cleanup; + listen(listenfd, 1); + } +#endif + } + + ConvertHeaderBytes(hdr); + if (my_write(sdr->sockfd, msg, 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; + } + + if (my_read(errsd, (char*)&err, (int)sizeof(err)) < 0) + err = kDNSServiceErr_Unknown; + else + err = ntohl(err); + +cleanup: + if (!reuse_sd && listenfd > 0) dnssd_close(listenfd); + if (!reuse_sd && errsd > 0) dnssd_close(errsd); +#if !defined(USE_TCP_LOOPBACK) + if (!reuse_sd && data) unlink(data); +#endif + if (msg) free(msg); + return err; + } + +int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) { if (!sdRef) return -1; - return sdRef->sockfd; + return (int) 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) +DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) { ipc_msg_hdr hdr; char *data; - if (!sdRef || sdRef->sockfd < 0 || !sdRef->process_reply) + 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 (my_read(sdRef->sockfd, (void *)&hdr, sizeof(hdr)) < 0) + // return NoError on EWOULDBLOCK. This will handle the case + // where a non-blocking socket is told there is data, but + // it was a false positive. + return (dnssd_errno() == dnssd_EWOULDBLOCK) ? kDNSServiceErr_NoError : kDNSServiceErr_Unknown; + ConvertHeaderBytes(&hdr); 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) + if (my_read(sdRef->sockfd, data, hdr.datalen) < 0) return kDNSServiceErr_Unknown; sdRef->process_reply(sdRef, &hdr, data); free(data); return kDNSServiceErr_NoError; } - -void DNSServiceRefDeallocate(DNSServiceRef sdRef) +void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef) { if (!sdRef) return; - if (sdRef->sockfd > 0) close(sdRef->sockfd); + if (sdRef->sockfd > 0) dnssd_close(sdRef->sockfd); free(sdRef); } +static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + char fullname[kDNSServiceMaxDomainName]; + char target[kDNSServiceMaxDomainName]; + uint16_t txtlen; + union { uint16_t s; u_char b[2]; } port; + uint32_t ifi; + DNSServiceErrorType err; + char *txtrecord; + int str_error = 0; + (void)hdr; //unused + + flags = get_flags(&data); + ifi = get_long(&data); + err = get_error_code(&data); + if (get_string(&data, fullname, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, target, kDNSServiceMaxDomainName) < 0) str_error = 1; + port.b[0] = *data++; + port.b[1] = *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.s, txtlen, txtrecord, sdr->app_context); + } -DNSServiceErrorType DNSServiceResolve +DNSServiceErrorType DNSSD_API DNSServiceResolve ( DNSServiceRef *sdRef, - const DNSServiceFlags flags, - const uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *name, const char *regtype, const char *domain, - const DNSServiceResolveReply callBack, + DNSServiceResolveReply callBack, void *context ) { char *msg = NULL, *ptr; - int len; + size_t len; ipc_msg_hdr *hdr; DNSServiceRef sdr; DNSServiceErrorType err; - + if (!sdRef) return kDNSServiceErr_BadParam; *sdRef = NULL; - + if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; // calculate total message length @@ -199,7 +510,7 @@ DNSServiceErrorType DNSServiceResolve 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); @@ -213,7 +524,7 @@ DNSServiceErrorType DNSServiceResolve sdr->app_callback = callBack; sdr->app_context = context; *sdRef = sdr; - + return err; error: @@ -221,54 +532,52 @@ error: if (*sdRef) { free(*sdRef); *sdRef = NULL; } return kDNSServiceErr_Unknown; } - - -static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + +static void handle_query_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; + uint32_t interfaceIndex, ttl; + DNSServiceErrorType errorCode; + char name[kDNSServiceMaxDomainName]; + uint16_t rrtype, rrclass, rdlen; + char *rdata; int str_error = 0; - (void)hdr; //unused - + (void)hdr;//Unused + flags = get_flags(&data); - ifi = get_long(&data); - err = get_error_code(&data); - 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); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + 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 (!err && str_error) err = kDNSServiceErr_Unknown; - ((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port, txtlen, txtrecord, sdr->app_context); + 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; } - - - -DNSServiceErrorType DNSServiceQueryRecord +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord ( DNSServiceRef *sdRef, - const DNSServiceFlags flags, - const uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *name, - const uint16_t rrtype, - const uint16_t rrclass, - const DNSServiceQueryRecordReply callBack, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, void *context ) { char *msg = NULL, *ptr; - int len; + size_t len; ipc_msg_hdr *hdr; DNSServiceRef sdr; DNSServiceErrorType err; - + if (!sdRef) return kDNSServiceErr_BadParam; *sdRef = NULL; @@ -312,47 +621,39 @@ error: return kDNSServiceErr_Unknown; } - -static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) +static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) { - DNSServiceFlags flags; - uint32_t interfaceIndex, ttl; - DNSServiceErrorType errorCode; - char name[kDNSServiceMaxDomainName]; - uint16_t rrtype, rrclass, rdlen; - char *rdata; + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + char replyName[256], replyType[kDNSServiceMaxDomainName], + replyDomain[kDNSServiceMaxDomainName]; int str_error = 0; - (void)hdr;//Unused + (void)hdr;//Unused flags = get_flags(&data); interfaceIndex = get_long(&data); errorCode = get_error_code(&data); - 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 (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; - ((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass, - rdlen, rdata, ttl, sdr->app_context); - return; + ((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context); } -DNSServiceErrorType DNSServiceBrowse +DNSServiceErrorType DNSSD_API DNSServiceBrowse ( DNSServiceRef *sdRef, - const DNSServiceFlags flags, - const uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *regtype, const char *domain, - const DNSServiceBrowseReply callBack, + DNSServiceBrowseReply callBack, void *context ) { char *msg = NULL, *ptr; - int len; + size_t len; ipc_msg_hdr *hdr; DNSServiceRef sdr; DNSServiceErrorType err; @@ -396,51 +697,71 @@ error: return kDNSServiceErr_Unknown; } +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser +( + DNSServiceFlags flags, + const char *domain + ) + { + DNSServiceRef sdr; + DNSServiceErrorType err; + char *ptr = NULL; + size_t len = sizeof(flags) + strlen(domain) + 1; + ipc_msg_hdr *hdr = create_hdr(setdomain_request, &len, &ptr, 1); + if (!hdr) return kDNSServiceErr_Unknown; + put_flags(flags, &ptr); + put_string(domain, &ptr); + sdr = connect_to_server(); + if (!sdr) { free(hdr); return kDNSServiceErr_Unknown; } + err = deliver_request((char *)hdr, sdr, 1); // deliver_request frees the message for us + DNSServiceRefDeallocate(sdr); + return err; + } -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) { - DNSServiceFlags flags; - uint32_t interfaceIndex; - DNSServiceErrorType errorCode; - char replyName[256], replyType[kDNSServiceMaxDomainName], - replyDomain[kDNSServiceMaxDomainName]; + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + 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); - 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 (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; - ((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context); + ((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context); } - -DNSServiceErrorType DNSServiceRegister +DNSServiceErrorType DNSSD_API 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 + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + const char *host, + uint16_t PortInNetworkByteOrder, + uint16_t txtLen, + const void *txtRecord, + DNSServiceRegisterReply callBack, + void *context ) { char *msg = NULL, *ptr; - int len; + size_t len; ipc_msg_hdr *hdr; DNSServiceRef sdr; DNSServiceErrorType err; + union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder }; if (!sdRef) return kDNSServiceErr_BadParam; *sdRef = NULL; @@ -449,8 +770,8 @@ DNSServiceErrorType DNSServiceRegister if (!regtype) return kDNSServiceErr_BadParam; if (!domain) domain = ""; if (!host) host = ""; - if (!txtRecord) (char *)txtRecord = ""; - + if (!txtRecord) txtRecord = (void*)""; + // auto-name must also have auto-rename if (!name[0] && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam; @@ -474,7 +795,8 @@ DNSServiceErrorType DNSServiceRegister put_string(regtype, &ptr); put_string(domain, &ptr); put_string(host, &ptr); - put_short(port, &ptr); + *ptr++ = port.b[0]; + *ptr++ = port.b[1]; put_short(txtLen, &ptr); put_rdata(txtLen, txtRecord, &ptr); @@ -486,7 +808,7 @@ DNSServiceErrorType DNSServiceRegister DNSServiceRefDeallocate(sdr); return err; } - + sdr->op = reg_service_request; sdr->process_reply = callBack ? handle_regservice_response : NULL; sdr->app_callback = callBack; @@ -494,55 +816,51 @@ DNSServiceErrorType DNSServiceRegister *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) +static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) { DNSServiceFlags flags; uint32_t interfaceIndex; - DNSServiceErrorType errorCode; - char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName]; + DNSServiceErrorType err; + char domain[kDNSServiceMaxDomainName]; int str_error = 0; (void)hdr;//Unused flags = get_flags(&data); interfaceIndex = get_long(&data); - errorCode = get_error_code(&data); - if (get_string(&data, name, 256) < 0) str_error = 1; - if (get_string(&data, regtype, kDNSServiceMaxDomainName) < 0) str_error = 1; + err = get_error_code(&data); 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); + if (!err && str_error) err = kDNSServiceErr_Unknown; + ((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context); } -DNSServiceErrorType DNSServiceEnumerateDomains +DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains ( DNSServiceRef *sdRef, - const DNSServiceFlags flags, - const uint32_t interfaceIndex, - const DNSServiceDomainEnumReply callBack, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, void *context ) { char *msg = NULL, *ptr; - int len; + size_t len; ipc_msg_hdr *hdr; DNSServiceRef sdr; DNSServiceErrorType err; - + int f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0; + int f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0; + if (f1 + f2 != 1) return kDNSServiceErr_BadParam; if (!sdRef) return kDNSServiceErr_BadParam; *sdRef = NULL; - if (flags != kDNSServiceFlagsBrowseDomains && flags != kDNSServiceFlagsRegistrationDomains) - return kDNSServiceErr_BadParam; - len = sizeof(DNSServiceFlags); len += sizeof(uint32_t); @@ -575,26 +893,26 @@ error: return kDNSServiceErr_Unknown; } - -static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) +static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) { DNSServiceFlags flags; uint32_t interfaceIndex; - DNSServiceErrorType err; - char domain[kDNSServiceMaxDomainName]; - int str_error = 0; - (void)hdr;//Unused + 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); - err = get_error_code(&data); - 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); - } + errorCode = get_error_code(&data); + rref->app_callback(rref->sdr, rref, flags, errorCode, rref->app_context); + } -DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef) +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef) { if (!sdRef) return kDNSServiceErr_BadParam; *sdRef = connect_to_server(); @@ -605,55 +923,34 @@ DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef) 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 +DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord ( - const DNSServiceRef sdRef, - DNSRecordRef *RecordRef, - const DNSServiceFlags flags, - const uint32_t interfaceIndex, + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *fullname, - const uint16_t rrtype, - const uint16_t rrclass, - const uint16_t rdlen, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, const void *rdata, - const uint32_t ttl, - const DNSServiceRegisterRecordReply callBack, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, void *context ) { char *msg = NULL, *ptr; - int len; + size_t len; ipc_msg_hdr *hdr = NULL; DNSServiceRef tmp = NULL; DNSRecordRef rref = NULL; - - if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0) + int f1 = (flags & kDNSServiceFlagsShared) != 0; + int f2 = (flags & kDNSServiceFlagsUnique) != 0; + if (f1 + f2 != 1) return kDNSServiceErr_BadParam; + + if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0) return kDNSServiceErr_BadReference; *RecordRef = NULL; - - if (flags != kDNSServiceFlagsShared && flags != kDNSServiceFlagsUnique) - return kDNSServiceErr_BadReference; len = sizeof(DNSServiceFlags); len += 2 * sizeof(uint32_t); // interfaceIndex, ttl @@ -681,8 +978,8 @@ DNSServiceErrorType DNSServiceRegisterRecord rref->sdr = sdRef; *RecordRef = rref; hdr->client_context.context = rref; - hdr->reg_index = rref->record_index; - + hdr->reg_index = rref->record_index; + return deliver_request(msg, sdRef, 0); error: @@ -693,26 +990,26 @@ error: } //sdRef returned by DNSServiceRegister() -DNSServiceErrorType DNSServiceAddRecord +DNSServiceErrorType DNSSD_API DNSServiceAddRecord ( - const DNSServiceRef sdRef, + DNSServiceRef sdRef, DNSRecordRef *RecordRef, - const DNSServiceFlags flags, - const uint16_t rrtype, - const uint16_t rdlen, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, const void *rdata, - const uint32_t ttl + uint32_t ttl ) { ipc_msg_hdr *hdr; - int len = 0; + size_t len = 0; char *ptr; DNSRecordRef rref; - if (!sdRef || (sdRef->op != reg_service_request) || !RecordRef) + 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); @@ -734,7 +1031,7 @@ DNSServiceErrorType DNSServiceAddRecord rref->sdr = sdRef; *RecordRef = rref; hdr->client_context.context = rref; - hdr->reg_index = rref->record_index; + hdr->reg_index = rref->record_index; return deliver_request((char *)hdr, sdRef, 0); error: @@ -743,25 +1040,24 @@ error: if (*RecordRef) *RecordRef = NULL; return kDNSServiceErr_Unknown; } - //DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord -DNSServiceErrorType DNSServiceUpdateRecord +DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord ( - const DNSServiceRef sdRef, + DNSServiceRef sdRef, DNSRecordRef RecordRef, - const DNSServiceFlags flags, - const uint16_t rdlen, + DNSServiceFlags flags, + uint16_t rdlen, const void *rdata, - const uint32_t ttl + uint32_t ttl ) { ipc_msg_hdr *hdr; - int len = 0; + size_t len = 0; char *ptr; if (!sdRef) return kDNSServiceErr_BadReference; - + len += sizeof(uint16_t); len += rdlen; len += sizeof(uint32_t); @@ -776,24 +1072,22 @@ DNSServiceErrorType DNSServiceUpdateRecord put_long(ttl, &ptr); return deliver_request((char *)hdr, sdRef, 0); } - - -DNSServiceErrorType DNSServiceRemoveRecord +DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord ( - const DNSServiceRef sdRef, - const DNSRecordRef RecordRef, - const DNSServiceFlags flags + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags ) { ipc_msg_hdr *hdr; - int len = 0; + size_t len = 0; char *ptr; DNSServiceErrorType err; - if (!sdRef || !RecordRef || !sdRef->max_index) + 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; @@ -804,20 +1098,19 @@ DNSServiceErrorType DNSServiceRemoveRecord return err; } - -void DNSServiceReconfirmRecord +void DNSSD_API DNSServiceReconfirmRecord ( - const DNSServiceFlags flags, - const uint32_t interfaceIndex, + DNSServiceFlags flags, + uint32_t interfaceIndex, const char *fullname, - const uint16_t rrtype, - const uint16_t rrclass, - const uint16_t rdlen, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, const void *rdata ) { char *ptr; - int len; + size_t len; ipc_msg_hdr *hdr; DNSServiceRef tmp; @@ -838,164 +1131,9 @@ void DNSServiceReconfirmRecord put_short(rrclass, &ptr); put_short(rdlen, &ptr); put_rdata(rdlen, rdata, &ptr); - my_write(tmp->sockfd, (char *)hdr, len); + ConvertHeaderBytes(hdr); + my_write(tmp->sockfd, (char *)hdr, (int) len); + free(hdr); DNSServiceRefDeallocate(tmp); } - - - // 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) - { - 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; - } - - -// 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; -#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); - 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-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), - (unsigned long)(time.tv_sec & 0xFFF), (unsigned long)(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/mDNSShared/dnssd_ipc.c b/mDNSShared/dnssd_ipc.c index 9f509a4..e06737b 100644 --- a/mDNSShared/dnssd_ipc.c +++ b/mDNSShared/dnssd_ipc.c @@ -1,30 +1,47 @@ /* - * Copyright (c) 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 - * 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): + * 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. + + Change History (most recent first): $Log: dnssd_ipc.c,v $ +Revision 1.14 2004/10/06 02:22:20 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.13 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.12 2004/09/16 23:14:24 cheshire +Changes for Windows compatibility + +Revision 1.11 2004/06/18 04:56:09 rpantos +casting goodness + +Revision 1.10 2004/06/12 01:08:14 cheshire +Changes for Windows compatibility + Revision 1.9 2004/05/18 23:51:27 cheshire Tidy up all checkin comments to use consistent "" format for bug numbers @@ -39,107 +56,71 @@ 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); - } + { + (*ptr)[0] = (char)((l >> 24) & 0xFF); + (*ptr)[1] = (char)((l >> 16) & 0xFF); + (*ptr)[2] = (char)((l >> 8) & 0xFF); + (*ptr)[3] = (char)((l ) & 0xFF); + *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); - } + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint32_t); + return((uint32_t) ((uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | p[3])); + } + +void put_short(uint16_t s, char **ptr) + { + (*ptr)[0] = (char)((s >> 8) & 0xFF); + (*ptr)[1] = (char)((s ) & 0xFF); + *ptr += sizeof(uint16_t); + } uint16_t get_short(char **ptr) - { - uint16_t s; - - s = *(uint16_t *)(*ptr); - *ptr += sizeof(uint16_t); - return s; - } - + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint16_t); + return((uint16_t) ((uint16_t)p[0] << 8 | p[1])); + } int put_string(const char *str, char **ptr) - { - if (!str) str = ""; - strcpy(*ptr, str); - *ptr += strlen(str) + 1; - return 0; - } + { + if (!str) str = ""; + strcpy(*ptr, str); + *ptr += strlen(str) + 1; + return 0; + } 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; - } + { + int 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; - } + { + memcpy(*ptr, rdata, rdlen); + *ptr += rdlen; + } char *get_rdata(char **ptr, int rdlen) - { - char *rd; - - rd = *ptr; - *ptr += rdlen; - return rd; - } - - - - - - - - - + { + char *rd = *ptr; + *ptr += rdlen; + return rd; + } + +void ConvertHeaderBytes(ipc_msg_hdr *hdr) + { + hdr->version = htonl(hdr->version); + hdr->datalen = htonl(hdr->datalen); + hdr->flags = htonl(hdr->flags); + hdr->op = htonl(hdr->op ); + hdr->reg_index = htonl(hdr->reg_index); + } diff --git a/mDNSShared/dnssd_ipc.h b/mDNSShared/dnssd_ipc.h index 4585284..60ffc16 100644 --- a/mDNSShared/dnssd_ipc.h +++ b/mDNSShared/dnssd_ipc.h @@ -1,30 +1,67 @@ /* - * Copyright (c) 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 - * 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@ + * 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. Change History (most recent first): $Log: dnssd_ipc.h,v $ +Revision 1.17 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.16 2004/11/12 03:21:41 rpantos +rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. + +Revision 1.15 2004/10/06 02:22:20 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.14 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.13 2004/09/16 23:14:25 cheshire +Changes for Windows compatibility + +Revision 1.12 2004/09/16 21:46:38 ksekar + Need SPI for LoginWindow to associate a UID with a Wide Area Rendezvous domain + +Revision 1.11 2004/08/10 06:24:56 cheshire +Use types with precisely defined sizes for 'op' and 'reg_index', for better +compatibility if the daemon and the client stub are built using different compilers + +Revision 1.10 2004/07/07 17:39:25 shersche +Change MDNS_SERVERPORT from 5533 to 5354. + +Revision 1.9 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.8 2004/06/18 04:56:51 rpantos +Add layer for platform code + +Revision 1.7 2004/06/12 01:08:14 cheshire +Changes for Windows compatibility + Revision 1.6 2003/08/12 19:56:25 cheshire Update to APSL 2.0 @@ -34,21 +71,67 @@ Update to APSL 2.0 #define DNSSD_IPC_H #include "dns_sd.h" -#include -#include -#include -#include -#include -#include -#include + + +// +// Common cross platform services +// +#if defined(WIN32) +# include +# define dnssd_InvalidSocket INVALID_SOCKET +# define dnssd_EWOULDBLOCK WSAEWOULDBLOCK +# define dnssd_EINTR WSAEINTR +# define MSG_WAITALL 0 +# define dnssd_sock_t SOCKET +# define dnssd_sockbuf_t +# define dnssd_close(sock) closesocket(sock) +# define dnssd_errno() WSAGetLastError() +# define ssize_t int +# define getpid _getpid +#else +# include +# include +# include +# include +# include +# include +# include +# include +# include +# define dnssd_InvalidSocket -1 +# define dnssd_EWOULDBLOCK EWOULDBLOCK +# define dnssd_EINTR EINTR +# define dnssd_EPIPE EPIPE +# define dnssd_sock_t int +# define dnssd_close(sock) close(sock) +# define dnssd_errno() errno +#endif + +#if defined(USE_TCP_LOOPBACK) +# define AF_DNSSD AF_INET +# define MDNS_TCP_SERVERADDR "127.0.0.1" +# define MDNS_TCP_SERVERPORT 5354 +# define LISTENQ 5 +# define dnssd_sockaddr_t struct sockaddr_in +#else +# define AF_DNSSD AF_LOCAL +# define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder" +# define LISTENQ 100 + // longest legal control path length +# define MAX_CTLPATH 256 +# define dnssd_sockaddr_t struct sockaddr_un +#endif + //#define UDSDEBUG // verbose debug output +// Compatibility workaround +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + // 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 +#define TXT_RECORD_INDEX ((uint32_t)(-1)) // record index for default text record // IPC data encoding constants and types #define VERSION 1 @@ -56,7 +139,6 @@ Update to APSL 2.0 #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() @@ -69,7 +151,8 @@ typedef enum query_request, reconfirm_record_request, add_record_request, - update_record_request + update_record_request, + setdomain_request } request_op_t; typedef enum @@ -82,10 +165,8 @@ typedef enum 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 @@ -103,62 +184,43 @@ typedef union 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; + uint32_t op; // request_op_t or reply_op_t 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 + uint32_t 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; - - - + } ipc_msg_hdr_struct; +// it is advanced to point to the next field, or the end of the message // routines to write to and extract data from message buffers. -// caller responsible for bounds checking. +// 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); +void put_short(uint16_t s, char **ptr); +uint16_t get_short(char **ptr); + +#define put_flags put_long +#define get_flags get_long + +#define put_error_code put_long +#define get_error_code get_long 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 - +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); - - +void ConvertHeaderBytes(ipc_msg_hdr *hdr); #endif // DNSSD_IPC_H - - - - - - - - - - - diff --git a/mDNSShared/mDNS.1 b/mDNSShared/mDNS.1 index be51457..71c3daf 100644 --- a/mDNSShared/mDNS.1 +++ b/mDNSShared/mDNS.1 @@ -20,6 +20,12 @@ .\" @APPLE_LICENSE_HEADER_END@ .\" .\" $Log: mDNS.1,v $ +.\" Revision 1.5 2004/09/24 18:33:05 cheshire +.\" Update man pages to clarify that mDNS and dns-sd are not intended for script use +.\" +.\" Revision 1.4 2004/09/22 22:18:48 cheshire +.\" Update man page to cross-reference new dns-sd man page +.\" .\" Revision 1.3 2004/05/19 00:31:28 cheshire .\" Add missing "name type domain" for -L option .\" @@ -50,19 +56,32 @@ The .Nm command is a network diagnostic tool, much like -.Xr ping 8 or +.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 . +However, unlike those tools, most of its functionality is not implemented in the +.Nm +executable itself, but in library code that is available to any application. +The library API that +.Nm +uses is documented in +.Pa /usr/include/DNSServiceDiscovery/DNSServiceDiscovery.h . +Note that this Mach-based API, first introduced in Mac OS X 10.2, +is now deprecated in favour of the newer +.Pa /usr/include/dns_sd.h +API, which is built on Unix Domain Sockets and is supported on +multiple platforms. +The command-line tool to exercise the cross-platform +.Pa dns_sd.h +API is +.Xr dns-sd 1 . .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 +the asynchronous nature of 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 @@ -73,9 +92,21 @@ 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. +.br +If you wish to perform DNS Service Discovery operations from a +scripting language, then the best way to do this is not to execute the +.Nm +command and then attempt to decipher the textual output, but instead to +directly call the DNS-SD APIs using a binding for your chosen language. +.br +For example, if you are programming in Ruby, then you can +directly call DNS-SD APIs using the dnssd package documented at +.Pa . +.br +Similar bindings for other languages are also in development. .Pp .Bl -tag -width R -.It Fl R Ar name type domain port Op Ar key=value ... +.It Nm Fl R Ar name type domain port Op Ar key=value ... register (advertise) a service in the specified .Ar domain with the given @@ -112,7 +143,7 @@ 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 +.It Nm Fl B Ar type domain browse for instances of service .Ar type in @@ -125,7 +156,7 @@ see as described above. Omitting the .Ar domain or using "." means "pick a sensible default." -.It Fl L Ar name type domain +.It Nm 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 @@ -144,7 +175,7 @@ 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 +.Dl Nm Fl R Ns \ \&"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 @@ -155,23 +186,29 @@ 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 +.Dl Nm Fl R Ns \ \&"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 +.Dl Nm Fl B Ns \ _http._tcp .Pp -While that command is running, in another window, try the "mDNS -R" +While that command is running, in another window, try the +.Nm Fl 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. +"Add" event reported to the +.Nm Fl B +window. Now press Ctrl-C in the +.Nm Fl R +window and you should see the "Remove" event reported to the +.Nm Fl B +window. .Pp .Sh FILES .Pa /usr/bin/mDNS \" Pathname .\" .Sh SEE ALSO +.Xr dns-sd 1 .Xr mDNSResponder 8 .\" .Sh BUGS diff --git a/mDNSShared/mDNSDebug.c b/mDNSShared/mDNSDebug.c index 9196f00..dc5ad0f 100644 --- a/mDNSShared/mDNSDebug.c +++ b/mDNSShared/mDNSDebug.c @@ -3,8 +3,6 @@ * * @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 @@ -31,6 +29,15 @@ Change History (most recent first): $Log: mDNSDebug.c,v $ +Revision 1.5 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.4 2004/06/11 22:36:51 cheshire +Fixes for compatibility with Windows + Revision 1.3 2004/01/28 21:14:23 cheshire Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode) @@ -45,9 +52,20 @@ Changes necessary to support mDNSResponder on Linux. #include "mDNSDebug.h" #include + +#if defined(WIN32) +// Need to add Windows syslog support here +#define LOG_PID 0x01 +#define LOG_CONS 0x02 +#define LOG_PERROR 0x20 +#define openlog(A,B,C) (void)(A); (void)(B) +#define syslog(A,B,C) +#define closelog() +#else #include +#endif -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #if MDNS_DEBUGMSGS mDNSexport int mDNS_DebugMode = mDNStrue; @@ -98,13 +116,7 @@ mDNSlocal void WriteLogMsg(const char *ident, const char *buffer, int logoptflag } } -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); - } - +// Log message with default "mDNSResponder" ident string at the start mDNSexport void LogMsg(const char *format, ...) { unsigned char buffer[512]; @@ -115,18 +127,24 @@ mDNSexport void LogMsg(const char *format, ...) WriteLogMsg("mDNSResponder", buffer, 0); } +// Log message with specified ident string at the start mDNSexport void LogMsgIdent(const char *ident, const char *format, ...) { + unsigned char buffer[512]; va_list ptr; va_start(ptr,format); - LogMsgWithIdent(ident, format, ptr); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; va_end(ptr); + WriteLogMsg(ident, buffer, ident && *ident ? LOG_PID : 0); } +// Log message with no ident string at the start mDNSexport void LogMsgNoIdent(const char *format, ...) { + unsigned char buffer[512]; va_list ptr; va_start(ptr,format); - LogMsgWithIdent("", format, ptr); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; va_end(ptr); + WriteLogMsg("", buffer, 0); } diff --git a/mDNSShared/mDNSResponder.8 b/mDNSShared/mDNSResponder.8 index cef8944..8e7a23f 100644 --- a/mDNSShared/mDNSResponder.8 +++ b/mDNSShared/mDNSResponder.8 @@ -20,6 +20,9 @@ .\" @APPLE_LICENSE_HEADER_END@ .\" .\" $Log: mDNSResponder.8,v $ +.\" Revision 1.5 2004/06/29 02:41:38 cheshire +.\" Add note that mDNSResponder is called mdnsd on some systems +.\" .\" Revision 1.4 2004/05/18 18:14:36 cheshire .\" Minor wording update .\" @@ -47,6 +50,9 @@ .\" .Sh DESCRIPTION .Nm +(also known as +.Nm mdnsd +on some systems) is a daemon invoked at boot time to implement Multicast DNS and DNS Service Discovery. .Pp diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c index e393f69..7f50d74 100644 --- a/mDNSShared/uds_daemon.c +++ b/mDNSShared/uds_daemon.c @@ -1,10 +1,9 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- + * * 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 @@ -25,8 +24,283 @@ Change History (most recent first): $Log: uds_daemon.c,v $ -Revision 1.55.2.1 2004/06/13 22:32:24 ksekar -WWDC Branch +Revision 1.139 2004/12/13 21:18:45 ksekar +Include uDNS registrations in CountPeerRegistrations + +Revision 1.138 2004/12/13 18:23:18 ksekar + mDNSResponder error when quitting iChat - +don't close sockets delivering errors to blocked clients + +Revision 1.137 2004/12/13 00:09:22 ksekar + mDNSResponder error when quitting iChat + +Revision 1.136 2004/12/11 01:52:10 cheshire + Support kDNSServiceFlagsAllowRemoteQuery for registering services too + +Revision 1.135 2004/12/10 20:46:37 cheshire +Change LogOperation message to debugf + +Revision 1.134 2004/12/10 13:19:37 cheshire +Add verbosedebugf() logging message in CountPeerRegistrations() + +Revision 1.133 2004/12/10 05:27:26 cheshire + Guard against multiple autoname services of the same type on the same machine + +Revision 1.132 2004/12/10 04:28:28 cheshire + User not notified of name changes for services using new UDS API + +Revision 1.131 2004/12/10 02:09:25 cheshire + Modify default TTLs + +Revision 1.130 2004/12/10 00:55:24 cheshire +Add full name and type to LogOperation messages for DNSServiceAddRecord/UpdateRecord/RemoveRecord + +Revision 1.129 2004/12/09 03:17:23 ksekar + DomainEnumeration interface index should be zero + +Revision 1.128 2004/12/07 21:26:05 ksekar + DNSServiceRegisterRecord() can crash on deregistration + +Revision 1.127 2004/12/07 20:42:34 cheshire +Add explicit context parameter to mDNS_RemoveRecordFromService() + +Revision 1.126 2004/12/07 17:23:55 ksekar +Fixed LogOperation + +Revision 1.125 2004/12/06 21:15:23 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.124 2004/11/30 02:19:14 cheshire + Raise maxfds.rlim_cur for mDNSResponder + +Revision 1.123 2004/11/29 23:50:57 cheshire +Checkin 1.122 not necessary + +Revision 1.122 2004/11/24 17:55:01 ksekar +Added log message clarifying For unicast operations, verify that service types are legal + +Revision 1.121 2004/11/24 04:45:52 cheshire +Spelling mistake + +Revision 1.120 2004/11/24 00:10:44 cheshire + For unicast operations, verify that service types are legal + +Revision 1.119 2004/11/23 23:54:17 ksekar + Wide-Area DNSServiceRegisterRecord() failures +can crash mDNSResponder + +Revision 1.118 2004/11/23 22:33:01 cheshire + Remove temporary workaround code for iChat + +Revision 1.117 2004/11/23 20:23:10 ksekar +Fixed LogOperation that causes crash on connected service deregistrations + +Revision 1.116 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.115 2004/11/13 00:12:53 ksekar +Fixed some LogOperation printf converstions for debug builds. + +Revision 1.114 2004/11/12 18:25:45 shersche +Tidy up cross platform usleep code fragment. + +Revision 1.113 2004/11/12 03:21:41 rpantos +rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. + +Revision 1.112 2004/11/11 16:58:32 ksekar +Removed unused code (previously wrapped in #if 0) + +Revision 1.111 2004/11/05 22:47:37 shersche +Conditionally compile usleep(1000) to be Sleep(1) on Windows +Submitted by: Pavel Repin + +Revision 1.110 2004/11/05 19:56:56 ksekar + We no longer need to browse .Mac domains by +default - changed #if 0 to more descriptive #ifdef _HAVE_SETDOMAIN_SUPPORT_ + +Revision 1.109 2004/11/04 03:40:45 cheshire +More debugging messages + +Revision 1.108 2004/11/03 02:25:51 cheshire + Conflict for Computer Name should update *all* empty string services, not just the one with the conflict + +Revision 1.107 2004/11/02 19:39:23 ksekar + We no longer need to browse .Mac domains by default + +Revision 1.106 2004/11/02 02:12:21 cheshire + Remove unnecessary memory allocations + +Revision 1.105 2004/10/28 19:07:19 cheshire +Add some more debugging checks and improved LogOperation() messages + +Revision 1.104 2004/10/26 18:53:15 cheshire +Avoid unused variable warning + +Revision 1.103 2004/10/26 07:15:55 cheshire +Add file descriptor number to all LogOperation messages + +Revision 1.102 2004/10/26 06:11:42 cheshire +Add improved logging to aid in diagnosis of mDNSResponder crashed + +Revision 1.101 2004/10/26 04:31:44 cheshire +Rename CountSubTypes() as ChopSubTypes() + +Revision 1.100 2004/10/26 01:17:48 cheshire +Use "#if 0" instead of commenting out code + +Revision 1.99 2004/10/19 21:33:22 cheshire + Cannot resolve non-local registrations using the mach API +Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name +doesn't force multicast unless you set this flag to indicate explicitly that this is what you want + +Revision 1.98 2004/10/14 01:59:33 cheshire + UDS resolves don't work for uDNS services + +Revision 1.97 2004/10/13 00:58:35 cheshire + Registering a proxy doesn't work + +Revision 1.96 2004/09/30 00:25:00 ksekar + Dynamically update default registration domains on config change + +Revision 1.95 2004/09/26 23:20:36 ksekar + Allow default registrations in multiple wide-area domains + +Revision 1.94 2004/09/22 18:27:06 ksekar + allow DNSServiceAddRecord to pass zero to get +default record TTL + +Revision 1.93 2004/09/22 02:39:44 cheshire + Allow DNSServiceRegisterRecord to pass zero to get default record TTL + +Revision 1.92 2004/09/22 02:34:04 cheshire +Rename parameter "ttl" to "GetTTL" for clarity + +Revision 1.91 2004/09/22 02:25:43 cheshire +Fix spelling errors + +Revision 1.90 2004/09/21 23:40:12 ksekar + mDNSResponder to return errors on NAT traversal failure + +Revision 1.89 2004/09/21 23:29:51 cheshire + DNSServiceResolve should delay sending packets + +Revision 1.88 2004/09/21 23:12:46 cheshire +Reorder initialization of question fields to match structure order + +Revision 1.87 2004/09/21 22:18:33 cheshire +In SIGINFO output, display a '-' next to records that have the Unique bit set + +Revision 1.86 2004/09/21 21:05:11 cheshire +Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c, +into mDNSShared/uds_daemon.c + +Revision 1.85 2004/09/18 01:11:58 ksekar + Add a user's default domain to empty-string browse list + +Revision 1.84 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.83 2004/09/16 23:26:33 cheshire +Move version check inside preceeding "if" that checks we have a complete header + +Revision 1.82 2004/09/16 23:14:25 cheshire +Changes for Windows compatibility + +Revision 1.81 2004/09/16 21:46:38 ksekar + Need SPI for LoginWindow to associate a UID with a Wide Area Rendezvous domain + +Revision 1.80 2004/09/16 01:58:23 cheshire +Fix compiler warnings + +Revision 1.79 2004/09/16 00:24:49 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.78 2004/09/15 21:44:20 cheshire + Randomize initial timenow_adjust value in mDNS_Init +Show time value in log to help diagnose errors + +Revision 1.77 2004/09/15 00:19:18 cheshire + read_rr_from_ipc_msg should use mDNS_SetupResourceRecord() + +Revision 1.76 2004/09/02 06:39:52 cheshire +Minor textual cleanup for clarity + +Revision 1.75 2004/09/02 03:48:47 cheshire + Disable targeted unicast query support by default +1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record +2. New field AllowRemoteQuery in AuthRecord structure +3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set +4. mDNS.c only answers remote queries if AllowRemoteQuery is set + +Revision 1.74 2004/08/25 02:32:47 cheshire +Minor cleanup: replace "®type[0]" with "regtype" + +Revision 1.73 2004/08/25 02:30:40 cheshire + Current method of doing subtypes causes name collisions + +Revision 1.72 2004/08/14 03:22:42 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.71 2004/08/11 04:21:21 rpantos +Fix Windows build. + +Revision 1.70 2004/08/11 02:07:00 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init() +Move CheckForDuplicateRegistrations from daemon.c + No warning when accidentally registering the same service multiple times using socket API + +Revision 1.69 2004/08/10 16:14:48 cheshire +Fix debug builds (oops) + +Revision 1.68 2004/08/10 06:24:56 cheshire +Use types with precisely defined sizes for 'op' and 'reg_index', for better +compatibility if the daemon and the client stub are built using different compilers + +Revision 1.67 2004/07/27 07:14:16 shersche +make error socket non-blocking after call to connect() + +Revision 1.66 2004/07/13 21:24:25 rpantos +Fix for . + +Revision 1.65 2004/06/26 03:17:14 shersche +implement cross-platform strerror function + +Submitted by: herscher + +Revision 1.64 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.63 2004/06/24 03:43:44 rpantos +Fix previous checkin so it builds on Windows. + +Revision 1.62 2004/06/24 00:57:08 ksekar +Replaced code acccidentally removed in checkin 1.59. + +Revision 1.61 2004/06/19 00:09:39 cheshire +Remove unused strsep() implementation + +Revision 1.60 2004/06/18 19:10:00 cheshire + Current method of doing subtypes causes name collisions + +Revision 1.59 2004/06/18 05:10:31 rpantos +Changes to allow code to be used on Windows + +Revision 1.58 2004/06/15 03:54:08 cheshire +Include mDNS_TimeNow(&mDNSStorage) in SIGINFO output + +Revision 1.57 2004/06/12 01:47:27 ksekar +: BBEdit crashes when trying to check for newer version +udsserver_idle compared time in ticks to interval in seconds. + +Revision 1.56 2004/06/12 01:35:47 cheshire +Changes for Windows compatibility Revision 1.55 2004/06/05 00:04:27 cheshire : wide-area domains should be returned in reg. domain enumeration @@ -39,7 +313,7 @@ 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 +: wide-area DNS-SD servers don't appear in Finder Use local-only InterfaceID for GetDomains calls for sockets-API @@ -127,7 +401,7 @@ Reviewed by: Stuart Cheshire Revision 1.26 2003/10/23 17:51:04 ksekar : handle blocked clients more efficiently -Changed gettimeofday() to mDNSPlatformTimeNow() +Changed gettimeofday() to mDNS_TimeNow(&mDNSStorage); Revision 1.25 2003/10/22 23:37:49 ksekar : crash/hang in abort_client @@ -175,21 +449,37 @@ Update to APSL 2.0 */ +#if defined(_WIN32) +#include +#define dnssd_strerror(X) win32_strerror(X) +#define usleep(X) Sleep(((X)+999)/1000) +static char * win32_strerror(int inErrorCode); +#else #include #include #include #include #include #include -#include +#define dnssd_strerror(X) strerror(X) +#endif -#include "mDNSClientAPI.h" +#include +#include +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" #include "uds_daemon.h" #include "dns_sd.h" #include "dnssd_ipc.h" -// convenience definition -#define _UNUSED __attribute__ ((unused)) +// Apple specific configuration functionality, not required for other platforms +#ifdef __MACOSX__ +#include +#ifndef LOCAL_PEERCRED +#define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */ +#endif // LOCAL_PEERCRED +#endif //__MACOSX__ + // Types and Data Structures // ---------------------------------------------------------------------- @@ -204,51 +494,85 @@ typedef enum typedef void (*req_termination_fn)(void *); - typedef struct registered_record_entry { - int key; + uint32_t key; AuthRecord *rr; struct registered_record_entry *next; + client_context_t client_context; + struct request_state *rstate; } registered_record_entry; - -typedef struct extra_record_entry - { - int key; - struct extra_record_entry *next; - ExtraResourceRecord e; - } extra_record_entry; -typedef struct registered_service +// A single registered service: ServiceRecordSet + bookkeeping +// Note that we duplicate some fields from parent service_info object +// to facilitate cleanup, when instances and parent may be deallocated at different times. +typedef struct service_instance { - int autoname; - int renameonconflict; - int rename_on_memfree; // set flag on config change when we deregister original name + struct service_instance *next; + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool autorename; // Set if this client wants us to automatically rename on conflict + mDNSBool allowremotequery; // Respond to unicast queries from outside the local link? + mDNSBool rename_on_memfree; // Set on config change when we deregister original name domainlabel name; - ServiceRecordSet *srs; + domainname domain; + mDNSBool default_local; // is this the "local." from an empty-string registration? struct request_state *request; + int sd; AuthRecord *subtypes; - extra_record_entry *extras; - } registered_service; + ServiceRecordSet srs; // note - must be last field in struct + } service_instance; + +// A client-created service. May reference several service_info objects if default +// settings cause registration in multiple domains. +typedef struct + { + uint16_t txtlen; + void *txtdata; + mDNSIPPort port; + domainlabel name; + char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; + domainname type; + mDNSBool default_domain; + domainname host; + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool autorename; // Set if this client wants us to automatically rename on conflict + mDNSBool allowremotequery; // Respond to unicast queries from outside the local link? + int num_subtypes; + mDNSInterfaceID InterfaceID; + service_instance *instances; + struct request_state *request; + } service_info; +// for multi-domain default browsing +typedef struct browser_t + { + DNSQuestion q; + domainname domain; + struct browser_t *next; + } browser_t; + +// parent struct for browser instances: list pointer plus metadata typedef struct { - registered_service *local; - registered_service *global; - } servicepair_t; + mDNSBool default_domain; + mDNSBool ForceMCast; + domainname regtype; + mDNSInterfaceID interface_id; + struct request_state *rstate; + browser_t *browsers; + } browser_info_t; typedef struct { - mStatus err; + mStatus err; // Note: This field is in NETWORK byte order int nwritten; - int sd; + dnssd_sock_t sd; } undelivered_error_t; typedef struct request_state { // connection structures - int sd; - int errfd; + dnssd_sock_t sd, errfd; // state of read (in case message is read over several recv() calls) transfer_state ts; @@ -271,25 +595,24 @@ 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 - servicepair_t servicepair; - struct resolve_result_t *resolve_results; - + service_info *service_registration; + browser_info_t *browser_info; 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; + DNSServiceFlags flags; // Note: This field is in NETWORK byte order + uint32_t ifi; // Note: This field is in NETWORK byte order + DNSServiceErrorType error; // Note: This field is in NETWORK byte order } reply_hdr; typedef struct reply_state { // state of the transmission - int sd; + dnssd_sock_t sd; transfer_state ts; uint32_t nwriten; uint32_t len; @@ -323,57 +646,41 @@ typedef struct typedef struct { - DNSQuestion question; - uint16_t qtype; - request_state *rstate; - } resolve_t; - -typedef struct - { - resolve_t *txt; - resolve_t *srv; request_state *rstate; + DNSQuestion qtxt; + DNSQuestion qsrv; + // const ResourceRecord *txt; + // const ResourceRecord *srv; + mDNSBool srv; + mDNSBool txt; + domainname target; + mDNSIPPort port; + mDNSu16 txtlen; + mDNSu8 txtdata[AbsoluteMaxDNSMessageData]; } 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; - -// for multi-domain browsing -typedef struct qlist_t - { - DNSQuestion q; - struct qlist_t *next; - } qlist_t; - -typedef struct +#ifdef _HAVE_SETDOMAIN_SUPPORT_ +typedef struct default_browse_list_t { - qlist_t *qlist; - request_state *rstate; - } browse_termination_context; + struct default_browse_list_t *next; + uid_t uid; + AuthRecord ptr_rec; + } default_browse_list_t; +static default_browse_list_t *default_browse_list = NULL; +#endif // _HAVE_SETDOMAIN_SUPPORT_ // 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 -//in the idle() routine. - - -#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 +mDNSexport mDNS mDNSStorage; +#define gmDNS (&mDNSStorage) + +static dnssd_sock_t listenfd = dnssd_InvalidSocket; +static request_state * all_requests = NULL; + +#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(void *info); static int read_msg(request_state *rs); @@ -410,16 +717,18 @@ 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 reply_state *create_reply(reply_op_t op, size_t 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); static int validate_message(request_state *rstate); -static mStatus remove_extra_rr_from_service(request_state *rstate); +static mStatus remove_extra(request_state *rstate, service_instance *serv); static mStatus remove_record(request_state *rstate); -static void free_service_registration(registered_service *srv); +static void free_service_instance(service_instance *srv); +static uint32_t dnssd_htonl(uint32_t l); +static void handle_setdomain_request(request_state *rstate); // initialization, setup/teardown functions @@ -428,15 +737,13 @@ static void free_service_registration(registered_service *srv); #define PID_FILE "/var/run/mDNSResponder.pid" #endif -int udsserver_init( mDNS *globalInstance) +int udsserver_init(void) { - mode_t mask; - struct sockaddr_un laddr; - struct rlimit maxfds; - - if ( !globalInstance) - goto error; - gmDNS = globalInstance; + dnssd_sockaddr_t laddr; + int ret; +#if defined(_WIN32) + u_long opt = 1; +#endif // If a particular platform wants to opt out of having a PID file, define PID_FILE to be "" if (PID_FILE[0]) @@ -449,60 +756,104 @@ int udsserver_init( mDNS *globalInstance) } } - if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) - goto error; - unlink(MDNS_UDS_SERVERPATH); //OK if this fails + if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) == dnssd_InvalidSocket) + goto error; + 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); + +#if defined(USE_TCP_LOOPBACK) + { + laddr.sin_family = AF_INET; + laddr.sin_port = htons(MDNS_TCP_SERVERPORT); + laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); + if (ret < 0) + goto error; + } +#else + { + mode_t mask = umask(0); + unlink(MDNS_UDS_SERVERPATH); //OK if this fails + 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); + ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); + umask(mask); + if (ret < 0) + goto error; + } #endif - 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); - +#if defined(_WIN32) + // + // SEH: do we even need to do this on windows? this socket + // will be given to WSAEventSelect which will automatically + // set it to non-blocking + // + if (ioctlsocket(listenfd, FIONBIO, &opt) != 0) +#else + if (fcntl(listenfd, F_SETFL, O_NONBLOCK) != 0) +#endif + { + my_perror("ERROR: could not set listen socket to non-blocking mode"); + goto error; + } + + if (listen(listenfd, LISTENQ) != 0) + { + my_perror("ERROR: could not listen on listen socket"); + goto error; + } + 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) - { - 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"); + +#if !defined(PLATFORM_NO_RLIMIT) + { + // Set maximum number of open file descriptors + #define MIN_OPENFILES 10240 + struct rlimit maxfds, newfds; + + // Due to bugs in OS X (, , ) + // you have to get and set rlimits once before getrlimit will return sensible values + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); + + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES; + newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES; + if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur) + if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit"); + + if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; } + debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max); + debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur); + } +#endif + return 0; error: + my_perror("ERROR: udsserver_init"); return -1; } int udsserver_exit(void) { - close(listenfd); - unlink(MDNS_UDS_SERVERPATH); + dnssd_close(listenfd); + +#if !defined(USE_TCP_LOOPBACK) + unlink(MDNS_UDS_SERVERPATH); +#endif + return 0; } @@ -512,7 +863,7 @@ mDNSs32 udsserver_idle(mDNSs32 nextevent) request_state *req = all_requests, *tmp, *prev = NULL; reply_state *fptr; transfer_state result; - mDNSs32 now = mDNSPlatformTimeNow(); + mDNSs32 now = mDNS_TimeNow(&mDNSStorage); while(req) { @@ -524,7 +875,7 @@ mDNSs32 udsserver_idle(mDNSs32 nextevent) { while(req->replies) { - if (req->replies->next) req->replies->rhdr->flags |= kDNSServiceFlagsMoreComing; + if (req->replies->next) req->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing); result = send_msg(req->replies); if (result == t_complete) { @@ -544,10 +895,10 @@ mDNSs32 udsserver_idle(mDNSs32 nextevent) 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); + debugf("udsserver_idle: client has been blocked for %ld seconds", (now - req->time_blocked) / mDNSPlatformOneSecond); if (now - req->time_blocked >= MAX_TIME_BLOCKED) { - LogMsg("Could not write data to client after %d seconds - aborting connection", MAX_TIME_BLOCKED / mDNSPlatformOneSecond); + LogMsg("Could not write data to client after %ld seconds - aborting connection", MAX_TIME_BLOCKED / mDNSPlatformOneSecond); abort_request(req); result = t_terminated; } @@ -571,41 +922,75 @@ mDNSs32 udsserver_idle(mDNSs32 nextevent) return nextevent; } -void udsserver_info(void) +void udsserver_info(mDNS *const m) { + mDNSs32 now = mDNS_TimeNow(m); + mDNSu32 CacheUsed = 0, CacheActive = 0; + mDNSu32 slot; request_state *req; - qlist_t *qlist; + + LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now); + + for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { + mDNSu32 SlotUsed = 0; + CacheRecord *rr; + 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 %s%-6s%-6s%s", + rr->CRActiveQuestion ? "*" : " ", remain, + (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "-" : " ", DNSTypeName(rr->resrec.rrtype), + ((NetworkInterfaceInfo *)rr->resrec.InterfaceID)->ifname, CRDisplayString(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); + for (req = all_requests; req; req=req->next) { void *t = req->termination_context; if (!t) continue; if (req->terminate == regservice_termination_callback) - 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) { - for (qlist = ((browse_termination_context *)t)->qlist; qlist; qlist = qlist->next) - LogMsgNoIdent("DNSServiceBrowse %##s", qlist->q.qname.c); + service_instance *ptr; + for (ptr = ((service_info *)t)->instances; ptr; ptr = ptr->next) + LogMsgNoIdent("DNSServiceRegister %##s %u", ptr->srs.RR_SRV.resrec.name.c, SRS_PORT(&ptr->srs)); + } + else if (req->terminate == browse_termination_callback) + { + browser_t *blist; + for (blist = req->browser_info->browsers; blist; blist = blist->next) + LogMsgNoIdent("DNSServiceBrowse %##s", blist->q.qname.c); } else if (req->terminate == resolve_termination_callback) - LogMsgNoIdent("DNSServiceResolve %##s", ((resolve_termination_t *)t)->srv->question.qname.c); + LogMsgNoIdent("DNSServiceResolve %##s", ((resolve_termination_t *)t)->qsrv.qname.c); else if (req->terminate == question_termination_callback) LogMsgNoIdent("DNSServiceQueryRecord %##s", ((DNSQuestion *) t)->qname.c); else if (req->terminate == enum_termination_callback) LogMsgNoIdent("DNSServiceEnumerateDomains %##s", ((enum_termination_t *) t)->all->question.qname.c); } - } + now = mDNS_TimeNow(m); + LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now); + } -static void rename_service(registered_service *srv) +static void rename_service(service_instance *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 + if (mDNS_DeregisterService(gmDNS, &srv->srs)) // If service deregistered already, we can re-register immediately + regservice_callback(gmDNS, &srv->srs, mStatus_MemFree); } } @@ -616,69 +1001,58 @@ void udsserver_handle_configchange(void) for (req = all_requests; req; req = req->next) { - if (req->servicepair.local) rename_service(req->servicepair.local); - if (req->servicepair.global) rename_service(req->servicepair.global); + if (req->service_registration) + { + service_instance *ptr; + for (ptr = req->service_registration->instances; ptr; ptr = ptr->next) + rename_service(ptr); + } } } -static void connect_callback(void *info _UNUSED) +static void connect_callback(void *info) { - int sd, clilen, optval; - struct sockaddr_un cliaddr; + dnssd_sock_t sd; + int len; + unsigned long optval; + dnssd_sockaddr_t cliaddr; request_state *rstate; -// int errpipe[2]; + (void)info; // Unused + + len = (int) sizeof(cliaddr); - clilen = sizeof(cliaddr); - sd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen); + sd = accept(listenfd, (struct sockaddr*) &cliaddr, &len); - if (sd < 0) + if (sd == dnssd_InvalidSocket) { - if (errno == EWOULDBLOCK) return; + if (dnssd_errno() == dnssd_EWOULDBLOCK) return; my_perror("ERROR: accept"); 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); + dnssd_close(sd); return; } #endif - 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) +#if defined(_WIN32) + if (ioctlsocket(sd, FIONBIO, &optval) != 0) +#else + if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) +#endif { - 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 + my_perror("ERROR: setsockopt - SOL_NOSIGPIPE - aborting client"); + dnssd_close(sd); + return; + } + + // allocate a request_state struct that will live with the socket rstate = mallocL("connect_callback", sizeof(request_state)); if (!rstate) { @@ -688,8 +1062,8 @@ static void connect_callback(void *info _UNUSED) bzero(rstate, sizeof(request_state)); rstate->ts = t_morecoming; rstate->sd = sd; - //rstate->errfd = errpipe[1]; + LogOperation("%3d: Adding FD", rstate->sd); if ( mStatus_NoError != udsSupportAddFDToEventLoop( sd, request_callback, rstate)) return; rstate->next = all_requests; @@ -702,8 +1076,10 @@ static void request_callback(void *info) { request_state *rstate = info; transfer_state result; - struct sockaddr_un cliaddr; - char ctrl_path[MAX_CTLPATH]; + dnssd_sockaddr_t cliaddr; +#if defined(_WIN32) + u_long opt = 1; +#endif result = read_msg(rstate); if (result == t_morecoming) @@ -749,35 +1125,53 @@ static void request_callback(void *info) if (rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET) rstate->errfd = rstate->sd; else - { - if ((rstate->errfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) + { + if ((rstate->errfd = socket(AF_DNSSD, SOCK_STREAM, 0)) == dnssd_InvalidSocket) { my_perror("ERROR: socket"); exit(1); } - if (fcntl(rstate->errfd, F_SETFL, O_NONBLOCK) < 0) - { - my_perror("ERROR: could not set control socket to non-blocking mode"); - abort_request(rstate); - unlink_request(rstate); - return; - } + +#if defined(USE_TCP_LOOPBACK) + { + mDNSOpaque16 port; + port.b[0] = rstate->msgdata[0]; + port.b[1] = rstate->msgdata[1]; + rstate->msgdata += 2; + cliaddr.sin_family = AF_INET; + cliaddr.sin_port = port.NotAnInteger; + cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + } +#else + { + char ctrl_path[MAX_CTLPATH]; 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); + } +#endif if (connect(rstate->errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0) { my_perror("ERROR: connect"); abort_request(rstate); unlink_request(rstate); + return; } - } - - - +#if defined(_WIN32) + if (ioctlsocket(rstate->errfd, FIONBIO, &opt) != 0) +#else + if (fcntl(rstate->errfd, F_SETFL, O_NONBLOCK) != 0) +#endif + { + my_perror("ERROR: could not set control socket to non-blocking mode"); + abort_request(rstate); + unlink_request(rstate); + return; + } + } - switch(rstate->hdr.op.request_op) + switch(rstate->hdr.op) { case resolve_request: handle_resolve_request(rstate); break; case query_request: handle_query_request(rstate); break; @@ -789,8 +1183,9 @@ static void request_callback(void *info) case update_record_request: handle_update_request(rstate); break; case remove_record_request: handle_removerecord_request(rstate); break; case reconfirm_record_request: handle_reconfirm_request(rstate); break; + case setdomain_request: handle_setdomain_request(rstate); break; default: - debugf("ERROR: udsserver_recv_request - unsupported request type: %d", rstate->hdr.op.request_op); + LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op); } } @@ -843,17 +1238,21 @@ static void handle_query_request(request_state *rstate) } bzero(q, sizeof(DNSQuestion)); + q->InterfaceID = InterfaceID; + q->Target = zeroAddr; if (!MakeDomainNameFromDNSNameString(&q->qname, name)) { freeL("DNSQuestion", q); goto bad_param; } - q->QuestionContext = rstate; + q->qtype = rrtype; + q->qclass = rrclass; + q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery) != 0; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0; q->QuestionCallback = question_result_callback; - q->qtype = rrtype; - q->qclass = rrclass; - q->InterfaceID = InterfaceID; - q->Target = zeroAddr; - if (flags & kDNSServiceFlagsLongLivedQuery) q->LongLived = mDNStrue; + q->QuestionContext = rstate; + rstate->termination_context = q; rstate->terminate = question_termination_callback; + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) START", rstate->sd, q->qname.c, DNSTypeName(q->qtype)); result = mDNS_StartQuery(gmDNS, q); if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result); @@ -878,7 +1277,6 @@ static void handle_resolve_request(request_state *rstate) 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; @@ -902,69 +1300,60 @@ static void handle_resolve_request(request_state *rstate) flags = get_flags(&ptr); interfaceIndex = get_long(&ptr); InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex); - if (interfaceIndex && !InterfaceID) goto bad_param; + if (interfaceIndex && !InterfaceID) + { LogMsg("ERROR: handle_resolve_request - Couldn't find InterfaceID for interfaceIndex %d", interfaceIndex); 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; + { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); 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; - srv->question.Target = zeroAddr; - - 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; - txt->question.Target = zeroAddr; + { LogMsg("ERROR: handle_resolve_request - Couldn't build_domainname_from_strings “%s” “%s” “%s”", name, regtype, domain); goto bad_param; } // set up termination info term = mallocL("handle_resolve_request", sizeof(resolve_termination_t)); + bzero(term, sizeof(*term)); if (!term) goto malloc_error; - term->srv = srv; - term->txt = txt; + + // format questions + term->qsrv.InterfaceID = InterfaceID; + term->qsrv.Target = zeroAddr; + memcpy(&term->qsrv.qname, &fqdn, MAX_DOMAIN_NAME); + term->qsrv.qtype = kDNSType_SRV; + term->qsrv.qclass = kDNSClass_IN; + term->qsrv.LongLived = mDNSfalse; + term->qsrv.ExpectUnique = mDNStrue; + term->qsrv.ForceMCast = mDNSfalse; + term->qsrv.QuestionCallback = resolve_result_callback; + term->qsrv.QuestionContext = rstate; + + term->qtxt.InterfaceID = InterfaceID; + term->qtxt.Target = zeroAddr; + memcpy(&term->qtxt.qname, &fqdn, MAX_DOMAIN_NAME); + term->qtxt.qtype = kDNSType_TXT; + term->qtxt.qclass = kDNSClass_IN; + term->qtxt.LongLived = mDNSfalse; + term->qtxt.ExpectUnique = mDNStrue; + term->qtxt.ForceMCast = mDNSfalse; + term->qtxt.QuestionCallback = resolve_result_callback; + term->qtxt.QuestionContext = rstate; + 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(gmDNS, &srv->question); - if (!err) err = mDNS_StartQuery(gmDNS, &txt->question); + LogOperation("%3d: DNSServiceResolve(%##s) START", rstate->sd, term->qsrv.qname.c); + err = mDNS_StartQuery(gmDNS, &term->qsrv); + if (!err) err = mDNS_StartQuery(gmDNS, &term->qtxt); 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) @@ -996,44 +1385,60 @@ static void resolve_termination_callback(void *context) return; } rs = term->rstate; + LogOperation("%3d: DNSServiceResolve(%##s) STOP", rs->sd, term->qtxt.qname.c); - mDNS_StopQuery(gmDNS, &term->txt->question); - mDNS_StopQuery(gmDNS, &term->srv->question); + mDNS_StopQuery(gmDNS, &term->qtxt); + mDNS_StopQuery(gmDNS, &term->qsrv); - 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 _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +static void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { - int len = 0; + size_t 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; + resolve_termination_t *res = rs->termination_context; + (void)m; // Unused + + LogOperation("%3d: DNSServiceResolve(%##s, %s) RESULT %s", rs->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer)); + + // This code used to do this trick of just keeping a copy of the pointer to + // the answer record in the cache, but the unicast query code doesn't currently + // put its answer records in the cache, so for now we can't do this. if (!AddRecord) { - if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL; - if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL; + // 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 (answer->rrtype == kDNSType_TXT) res->txt = answer; + // if (answer->rrtype == kDNSType_SRV) res->srv = answer; + + if (answer->rrtype == kDNSType_SRV) + { + AssignDomainName(res->target, answer->rdata->u.srv.target); + res->port = answer->rdata->u.srv.port; + res->srv = mDNStrue; + } + if (answer->rrtype == kDNSType_TXT) + { + if (answer->rdlength > AbsoluteMaxDNSMessageData) return; + res->txtlen = answer->rdlength; + mDNSPlatformMemCopy(answer->rdata->u.data, res->txtdata, res->txtlen); + res->txt = mDNStrue; + } 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); + ConvertDomainNameToCString(&res->target, target); // calculate reply length len += sizeof(DNSServiceFlags); @@ -1042,21 +1447,23 @@ static void resolve_result_callback(mDNS *const m _UNUSED, DNSQuestion *question len += strlen(fullname) + 1; len += strlen(target) + 1; len += 2 * sizeof(uint16_t); // port, txtLen - len += res->txt->rdlength; + len += res->txtlen; // allocate/init reply header rep = create_reply(resolve_reply, len, rs); - rep->rhdr->flags = 0; - rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID); - rep->rhdr->error = kDNSServiceErr_NoError; + rep->rhdr->flags = dnssd_htonl(0); + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID)); + rep->rhdr->error = dnssd_htonl(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); + *data++ = res->port.b[0]; + *data++ = res->port.b[1]; + put_short(res->txtlen, &data); + put_rdata(res->txtlen, res->txtdata, &data); result = send_msg(rep); if (result == t_error || result == t_terminated) @@ -1070,16 +1477,17 @@ static void resolve_result_callback(mDNS *const m _UNUSED, DNSQuestion *question } // 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 _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +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; + request_state *req = question->QuestionContext; reply_state *rep; - int len; + size_t len; + (void)m; // Unused + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) RESULT %s", req->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer)); //mDNS_StopQuery(m, question); - req = question->QuestionContext; // calculate reply data length len = sizeof(DNSServiceFlags); @@ -1091,9 +1499,11 @@ static void question_result_callback(mDNS *const m _UNUSED, DNSQuestion *questio len += strlen(name) + 1; rep = create_reply(query_reply, len, req); - rep->rhdr->flags = AddRecord ? kDNSServiceFlagsAdd : 0; - rep->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID); - rep->rhdr->error = kDNSServiceErr_NoError; + + rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0); + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID)); + rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); + data = rep->sdata; put_string(name, &data); @@ -1110,12 +1520,205 @@ static void question_result_callback(mDNS *const m _UNUSED, DNSQuestion *questio static void question_termination_callback(void *context) { DNSQuestion *q = context; - - + LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", ((request_state *)q->QuestionContext)->sd, q->qname.c, DNSTypeName(q->qtype)); mDNS_StopQuery(gmDNS, 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 +mDNSexport mDNSs32 ChopSubTypes(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); + } + +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); + } + + +#ifdef _HAVE_SETDOMAIN_SUPPORT_ +static void free_defdomain(mDNS *const m, AuthRecord *const rr, mStatus result) + { + (void)m; // unused + if (result == mStatus_MemFree) free(rr->RecordContext); // context is the enclosing list structure + } +#endif + +static void handle_setdomain_request(request_state *request) + { + mStatus err = mStatus_NoError; + char *ptr; + char domainstr[MAX_ESCAPED_DOMAIN_NAME]; + domainname domain; + DNSServiceFlags flags; +#ifdef _HAVE_SETDOMAIN_SUPPORT_ + struct xucred xuc; + socklen_t xuclen; +#endif + + if (request->ts != t_complete) + { + LogMsg("ERROR: handle_setdomain_request - transfer state != t_complete"); + abort_request(request); + unlink_request(request); + return; + } + + // extract flags/domain from message + ptr = request->msgdata; + flags = get_flags(&ptr); + if (get_string(&ptr, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 || + !MakeDomainNameFromDNSNameString(&domain, domainstr)) + { err = mStatus_BadParamErr; goto end; } + + freeL("handle_setdomain_request", request->msgbuf); + request->msgbuf = NULL; + + debugf("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c); + +#ifdef _HAVE_SETDOMAIN_SUPPORT_ + // this functionality currently only used for Apple-specific configuration, so we don't burned other platforms by mandating + // the existence of this socket option + xuclen = sizeof(xuc); + if (getsockopt(request->sd, 0, LOCAL_PEERCRED, &xuc, &xuclen)) + { my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); err = mStatus_UnknownErr; goto end; } + if (xuc.cr_version != XUCRED_VERSION) { LogMsg("getsockopt, LOCAL_PEERCRED - bad version"); err = mStatus_UnknownErr; goto end; } + LogMsg("Default domain %s %s for UID %d", domainstr, flags & kDNSServiceFlagsAdd ? "set" : "removed", xuc.cr_uid); + + if (flags & kDNSServiceFlagsAdd) + { + // register a local-only PRT record + default_browse_list_t *newelem = malloc(sizeof(default_browse_list_t)); + if (!newelem) { LogMsg("ERROR: malloc"); err = mStatus_NoMemoryErr; goto end; } + mDNS_SetupResourceRecord(&newelem->ptr_rec, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, free_defdomain, newelem); + MakeDomainNameFromDNSNameString(&newelem->ptr_rec.resrec.name, "_default._browse._dns-sd._udp.local."); + AssignDomainName(newelem->ptr_rec.resrec.rdata->u.name, domain); + newelem->uid = xuc.cr_uid; + err = mDNS_Register(gmDNS, &newelem->ptr_rec); + if (err) free(newelem); + else + { + // link into list + newelem->next = default_browse_list; + default_browse_list = newelem; + } + + } + else + { + // remove - find in list, deregister + default_browse_list_t *ptr = default_browse_list, *prev = NULL; + while (ptr) + { + if (SameDomainName(&ptr->ptr_rec.resrec.rdata->u.name, &domain)) + { + if (prev) prev->next = ptr->next; + else default_browse_list = ptr->next; + err = mDNS_Deregister(gmDNS, &ptr->ptr_rec); + break; + } + prev = ptr; + ptr = ptr->next; + } + if (!ptr) { LogMsg("Attempt to remove nonexistent domain %s for UID %d", domainstr, xuc.cr_uid); err = mStatus_Invalid; } + } +#else + err = mStatus_NoError; +#endif // _HAVE_SETDOMAIN_SUPPORT_ + + end: + deliver_error(request, err); + abort_request(request); + unlink_request(request); + } + +static mStatus add_domain_to_browser(browser_info_t *info, const domainname *d) + { + browser_t *b, *p; + mStatus err; + + for (p = info->browsers; p; p = p->next) + { + if (SameDomainName(&p->domain, d)) + { LogMsg("add_domain_to_browser - attempt to add domain %##d already in list", d->c); return mStatus_AlreadyRegistered; } + } + + b = mallocL("browser_t", sizeof(*b)); + if (!b) return mStatus_NoMemoryErr; + AssignDomainName(b->domain, *d); + err = mDNS_StartBrowse(gmDNS, &b->q, &info->regtype, d, info->interface_id, info->ForceMCast, browse_result_callback, info->rstate); + if (err) + { + LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->regtype.c, d->c); + freeL("browser_t", b); + } + else + { + b->next = info->browsers; + info->browsers = b; + } + return err; + } static void handle_browse_request(request_state *request) { @@ -1123,12 +1726,12 @@ static void handle_browse_request(request_state *request) uint32_t interfaceIndex; mDNSInterfaceID InterfaceID; char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; - qlist_t *qlist = NULL, *qlist_elem; - domainname typedn; + domainname typedn, d, temp; + mDNSs32 NumSubTypes; char *ptr; - mStatus result; - DNameListElem *search_domain_list, *sdom, tmp; - browse_termination_context *term; + mStatus err; + DNameListElem *search_domain_list, *sdom; + browser_info_t *info = NULL; if (request->ts != t_complete) { @@ -1144,86 +1747,81 @@ 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; + { err = mStatus_BadParamErr; goto error; } freeL("handle_browse_request", request->msgbuf); request->msgbuf = NULL; InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex); - if (interfaceIndex && !InterfaceID) goto bad_param; + if (interfaceIndex && !InterfaceID) { err = mStatus_BadParamErr; goto error; } + + typedn.c[0] = 0; + NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0 || NumSubTypes > 1) { err = mStatus_BadParamErr; goto error; } + if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) + { err = mStatus_BadParamErr; goto error; } + + if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) { err = mStatus_BadParamErr; goto error; } - if (!MakeDomainNameFromDNSNameString(&typedn, regtype)) goto bad_param; + if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { err = mStatus_BadParamErr; goto error; } + if (temp.c[0] > 15 && domain[0] == 0) strcpy(domain, "local."); // For over-long service types, we only allow domain "local" - //!!!KRS browse locally for ichat - if (!domain[0] && (!strcmp(regtype, "_ichat._tcp.") || !strcmp(regtype, "_presence._tcp."))) - strcpy(domain,"local."); + // allocate and set up browser info + info = mallocL("browser_info_t", sizeof(*info)); + if (!info) { err = mStatus_NoMemoryErr; goto error; } + + request->browser_info = info; + info->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0; + info->interface_id = InterfaceID; + AssignDomainName(info->regtype, typedn); + info->rstate = request; + info->default_domain = !domain[0]; + info->browsers = NULL; + + // setup termination context + request->termination_context = info; + request->terminate = browse_termination_callback; + LogOperation("%3d: DNSServiceBrowse(%##s%s) START", request->sd, info->regtype.c, domain); 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; + if (!MakeDomainNameFromDNSNameString(&d, domain)) { err = mStatus_BadParamErr; goto error; } + err = add_domain_to_browser(info, &d); } - 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; - - // 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) + else + { + search_domain_list = mDNSPlatformGetSearchDomainList(); + for (sdom = search_domain_list; sdom; sdom = sdom->next) { - // 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; + err = add_domain_to_browser(info, &sdom->name); + if (err) + { + if (SameDomainName(&sdom->name, &localdomain)) break; + else err = mStatus_NoError; // suppress errors for non-local "default" domains + } + } - sdom = sdom->next; + mDNS_FreeDNameList(search_domain_list); } - if (search_domain_list != &tmp) mDNS_FreeDNameList(search_domain_list); + deliver_error(request, mStatus_NoError); return; -bad_param: - deliver_error(request, mStatus_BadParamErr); +error: + if (info) freeL("browser_info_t", info); + if (request->termination_context) request->termination_context = NULL; + deliver_error(request, err); abort_request(request); unlink_request(request); } -static void browse_result_callback(mDNS *const m _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +static void browse_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) { - request_state *req; + request_state *req = question->QuestionContext; reply_state *rep; mStatus err; - - req = question->QuestionContext; + (void)m; // Unused + LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s", req->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer)); err = gen_rr_response(&answer->rdata->u.name, answer->InterfaceID, req, &rep); if (err) @@ -1235,78 +1833,139 @@ static void browse_result_callback(mDNS *const m _UNUSED, DNSQuestion *question, } return; } - if (AddRecord) rep->rhdr->flags |= kDNSServiceFlagsAdd; // non-zero TTL indicates add + if (AddRecord) rep->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsAdd); // non-zero TTL indicates add append_reply(req, rep); return; } static void browse_termination_callback(void *context) { - browse_termination_context *t = context; - qlist_t *ptr, *fptr; + browser_info_t *info = context; + browser_t *ptr; - if (!t) return; - - ptr = t->qlist; - t->qlist = NULL; + if (!info) return; - while(ptr) + while(info->browsers) { + ptr = info->browsers; + info->browsers = ptr->next; + LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->rstate->sd, ptr->q.qname.c); mDNS_StopBrowse(gmDNS, &ptr->q); // no need to error-check result - fptr = ptr; - ptr = ptr->next; - freeL("browse_termination_callback", fptr); + freeL("browse_termination_callback", ptr); } - t->rstate->termination_context = NULL; - freeL("browse_termination_callback", t); + + info->rstate->termination_context = NULL; + freeL("browser_info", info); } -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) +mDNSexport void udsserver_default_browse_domain_changed(const domainname *d, mDNSBool add) { - registered_service *r_srv; - int srs_size, i; - char *sub; - mStatus result; - *srv_ptr = NULL; + request_state *r; - 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]); + for (r = all_requests; r; r = r->next) + { + browser_info_t *info = r->browser_info; + + if (!info || !info->default_domain) continue; + if (add) add_domain_to_browser(info, d); + else + { + browser_t *ptr = info->browsers, *prev = NULL; + while (ptr) + { + if (SameDomainName(&ptr->domain, d)) + { + if (prev) prev->next = ptr->next; + else info->browsers = ptr->next; + mDNS_StopBrowse(gmDNS, &ptr->q); + freeL("browser_t", ptr); + break; + } + prev = ptr; + ptr = ptr->next; + } + if (!ptr) LogMsg("Requested removal of default domain %##s not in list for sd %s", d->c, r->sd); + } + } + } - result = mDNS_RegisterService(gmDNS, r_srv->srs, n, t, d, h, port, - txtdata, txtlen, r_srv->subtypes, num_subtypes, InterfaceID, regservice_callback, r_srv); +// Count how many other service records we have locally with the same name, but different rdata. +// For auto-named services, we can have at most one per machine -- if we allowed two auto-named services of +// the same type on the same machine, we'd get into an infinite autoimmune-response loop of continuous renaming. +mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs) + { + int count = 0; + ResourceRecord *r = &srs->RR_SRV.resrec; + AuthRecord *rr; + ServiceRecordSet *s; + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(&rr->resrec.name, &r->name) && !SameRData(&rr->resrec, r)) + count++; - if (result) - free_service_registration(r_srv); - else *srv_ptr = r_srv; + for (rr = m->uDNS_info.RecordRegistrations; rr; rr=rr->next) + if (rr->uDNS_info.state != regState_Unregistered && rr->resrec.rrtype == kDNSType_SRV && SameDomainName(&rr->resrec.name, &r->name) && !SameRData(&rr->resrec, r)) + count++; + + for (s = m->uDNS_info.ServiceRegistrations; s; s = s->next) + if (s->uDNS_info.state != regState_Unregistered && SameDomainName(&s->RR_SRV.resrec.name, &r->name) && !SameRData(&s->RR_SRV.resrec, r)) + count++; + + verbosedebugf("%d peer registrations for %##s", count, r->name.c); + return(count); + } + +mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port) + { + int count = 0; + AuthRecord *rr; + for (rr = gmDNS->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && + rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger && + SameDomainName(&rr->resrec.name, srv)) + count++; + return(count); + } + +static mStatus register_service_instance(request_state *request, const domainname *domain) + { + service_info *info = request->service_registration; + service_instance *ptr, *instance; + int instance_size; + mStatus result; + for (ptr = info->instances; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->domain, domain)) + { LogMsg("register_service_instance: domain %##s already registered", domain->c); return mStatus_AlreadyRegistered; } + } + + instance_size = sizeof(*instance); + if (info->txtlen > sizeof(RDataBody)) instance_size += (info->txtlen - sizeof(RDataBody)); + instance = mallocL("service_instance", instance_size); + if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } + + instance->subtypes = AllocateSubTypes(info->num_subtypes, info->type_as_string); + if (info->num_subtypes && !instance->subtypes) + { free_service_instance(instance); instance = NULL; goto malloc_error; } + instance->request = request; + instance->sd = request->sd; + instance->autoname = info->autoname; + instance->autorename = info->autorename; + instance->allowremotequery = info->allowremotequery; + instance->rename_on_memfree = 0; + instance->name = info->name; + AssignDomainName(instance->domain, *domain); + instance->default_local = (info->default_domain && SameDomainName(domain, &localdomain)); + result = mDNS_RegisterService(gmDNS, &instance->srs, &instance->name, &info->type, domain, info->host.c[0] ? &info->host : NULL, info->port, + info->txtdata, info->txtlen, instance->subtypes, info->num_subtypes, info->InterfaceID, regservice_callback, instance); + + if (result) free_service_instance(instance); + else + { + instance->next = info->instances; + info->instances = instance; + } return result; malloc_error: @@ -1314,24 +1973,58 @@ malloc_error: exit(1); } +mDNSexport void udsserver_default_reg_domain_changed(const domainname *d, mDNSBool add) + { + request_state *rstate; + service_info *info; + + for (rstate = all_requests; rstate; rstate = rstate->next) + { + if (rstate->terminate != regservice_termination_callback) continue; + info = rstate->service_registration; + if (!info) { LogMsg("udsserver_default_reg_domain_changed - NULL service info"); continue; } // this should never happen + if (!info->default_domain) continue; + + // valid default registration + if (add) register_service_instance(rstate, d); + else + { + // find the instance to remove + service_instance *si = rstate->service_registration->instances, *prev = NULL; + while (si) + { + if (SameDomainName(&si->domain, d)) + { + mStatus err; + if (prev) prev->next = si->next; + else info->instances = si->next; + err = mDNS_DeregisterService(gmDNS, &si->srs); + if (err) + { + LogMsg("udsserver_default_reg_domain_changed - mDNS_DeregisterService err %d", err); + free_service_instance(si); + } + break; + } + prev = si; + si = si->next; + } + if (!si) LogMsg("udsserver_default_reg_domain_changed - domain %##s not registered", d->c); + } + } + } // 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, *sub; - domainlabel n; - domainname d, h, t, srv; + char name[256], domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME]; + char *ptr; + domainname d, srv; mStatus result; - mDNSInterfaceID InterfaceID; - int num_subtypes; - char *rtype_ptr; - + service_info *service = NULL; + if (request->ts != t_complete) { LogMsg("ERROR: handle_regservice_request - transfer state != t_complete"); @@ -1340,53 +2033,98 @@ static void handle_regservice_request(request_state *request) return; } + service = mallocL("service_info", sizeof(*service)); + if (!service) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; } + + service->instances = NULL; + service->request = request; + request->service_registration = service; + request->termination_context = request->service_registration; + request->terminate = regservice_termination_callback; + // extract data from message ptr = request->msgdata; flags = get_flags(&ptr); ifi = get_long(&ptr); - InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi); - if (ifi && !InterfaceID) goto bad_param; + service->InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi); + if (ifi && !service->InterfaceID) + { LogMsg("ERROR: handle_regservice_request - Couldn't find InterfaceID for interfaceIndex %d", ifi); goto bad_param; } if (get_string(&ptr, name, 256) < 0 || - get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 || + get_string(&ptr, service->type_as_string, 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; + { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); goto bad_param; } - port.NotAnInteger = get_short(&ptr); - txtlen = get_short(&ptr); - txtdata = get_rdata(&ptr, txtlen); + service->port.b[0] = *ptr++; + service->port.b[1] = *ptr++; + service->txtlen = get_short(&ptr); + service->txtdata = get_rdata(&ptr, service->txtlen); - if (!*regtype || !MakeDomainNameFromDNSNameString(&t, regtype)) goto bad_param; + // Check for sub-types after the service type + service->num_subtypes = ChopSubTypes(service->type_as_string); // Note: Modifies regtype string to remove trailing subtypes + if (service->num_subtypes < 0) + { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", service->type_as_string); 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 = (gmDNS)->nicelabel; - else if (!MakeDomainLabelFromLiteralString(&n, name)) - goto bad_param; - - if ((!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) || - (!ConstructServiceName(&srv, &n, &t, &d))) - goto bad_param; - - 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); + // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic + if (!*service->type_as_string || !MakeDomainNameFromDNSNameString(&service->type, service->type_as_string)) + { LogMsg("ERROR: handle_regservice_request - service->type_as_string bad %s", service->type_as_string); goto bad_param; } - //!!!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.")) + if (!name[0]) + { + service->name = (gmDNS)->nicelabel; + service->autoname = mDNStrue; + } + else + { + if (!MakeDomainLabelFromLiteralString(&service->name, name)) + { LogMsg("ERROR: handle_regservice_request - name bad %s", name); goto bad_param; } + service->autoname = mDNSfalse; + } + + if (*domain) + { + service->default_domain = mDNSfalse; + if (!MakeDomainNameFromDNSNameString(&d, domain)) + { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); goto bad_param; } + } + else { - 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 + service->default_domain = mDNStrue; + MakeDomainNameFromDNSNameString(&d, "local."); } + + if (!ConstructServiceName(&srv, &service->name, &service->type, &d)) + { LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", service->name.c, service->type.c, d.c); goto bad_param; } - request->termination_context = &request->servicepair; - request->terminate = regservice_termination_callback; - + if (!MakeDomainNameFromDNSNameString(&service->host, host)) + { LogMsg("ERROR: handle_regservice_request - host bad %s", host); goto bad_param; } + service->autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0; + service->allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0; + + // 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 (service->port.NotAnInteger) + { + int count = CountExistingRegistrations(&srv, service->port); + if (count) + LogMsg("Client application registered %d identical instances of service %##s port %u.", + count+1, srv.c, mDNSVal16(service->port)); + } + + LogOperation("%3d: DNSServiceRegister(%##s, %u) START", request->sd, srv.c, mDNSVal16(service->port)); + result = register_service_instance(request, &d); + + if (!result && !*domain) + { + DNameListElem *ptr, *def_domains = mDNSPlatformGetRegDomainList(); + for (ptr = def_domains; ptr; ptr = ptr->next) + register_service_instance(request, &ptr->name); + // note that we don't report errors for non-local, non-explicit domains + mDNS_FreeDNameList(def_domains); + } + +finish: deliver_error(request, result); if (result != mStatus_NoError) { @@ -1399,59 +2137,81 @@ static void handle_regservice_request(request_state *request) return; bad_param: + //if (service) freeL("service_info", service); Don't think we should do this -- abort_request will free it a second time and crash deliver_error(request, mStatus_BadParamErr); abort_request(request); unlink_request(request); } - - - // 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 _UNUSED, ServiceRecordSet *const srs, mStatus result) +static void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) { mStatus err; - registered_service *r_srv = srs->ServiceContext; - request_state *rs = r_srv->request; - - 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; - } + service_instance *instance = srs->ServiceContext; + (void)m; // Unused + if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; } + if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; } if (result == mStatus_NoError) - return process_service_registration(srs); + LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED ", instance->sd, srs->RR_SRV.resrec.name.c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port)); + else if (result == mStatus_MemFree) + LogOperation("%3d: DNSServiceRegister(%##s, %u) DEREGISTERED", instance->sd, srs->RR_SRV.resrec.name.c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port)); + else if (result == mStatus_NameConflict) + LogOperation("%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT", instance->sd, srs->RR_SRV.resrec.name.c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port)); + else + LogOperation("%3d: DNSServiceRegister(%##s, %u) CALLBACK %d", instance->sd, srs->RR_SRV.resrec.name.c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), result); + + if (result == mStatus_NoError) + { + if (instance->allowremotequery) + { + srs->RR_ADV.AllowRemoteQuery = mDNStrue; + srs->RR_PTR.AllowRemoteQuery = mDNStrue; + srs->RR_SRV.AllowRemoteQuery = mDNStrue; + srs->RR_TXT.AllowRemoteQuery = mDNStrue; + } + process_service_registration(srs); + if (instance->autoname && CountPeerRegistrations(m, srs) == 0) + RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately + return; + } else if (result == mStatus_MemFree) { - if (r_srv->rename_on_memfree) + if (instance->rename_on_memfree) { - r_srv->rename_on_memfree = 0; - r_srv->name = gmDNS->nicelabel; - err = mDNS_RenameAndReregisterService(gmDNS, srs, &r_srv->name); - if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err); + instance->rename_on_memfree = 0; + instance->name = gmDNS->nicelabel; + err = mDNS_RenameAndReregisterService(gmDNS, srs, &instance->name); + if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %ld", err); // error should never happen - safest to log and continue } else { - free_service_registration(r_srv); + free_service_instance(instance); return; } } else if (result == mStatus_NameConflict) { - if (r_srv->autoname || r_srv->renameonconflict) + if (instance->autoname && CountPeerRegistrations(m, srs) == 0) + { + // On conflict for an autoname service, rename and reregister *all* autoname services + IncrementLabelSuffix(&m->nicelabel, mDNStrue); + m->MainCallback(m, mStatus_ConfigChanged); + } + else if (instance->autoname || instance->autorename) { mDNS_RenameAndReregisterService(gmDNS, srs, mDNSNULL); return; } else { - free_service_registration(r_srv); + request_state *rs = instance->request; + if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; } + free_service_instance(instance); if (deliver_async_error(rs, reg_service_reply, result) < 0) { abort_request(rs); @@ -1462,7 +2222,10 @@ static void regservice_callback(mDNS *const m _UNUSED, ServiceRecordSet *const s } else { - LogMsg("ERROR: unknown result in regservice_callback: %d", result); + request_state *rs = instance->request; + if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; } + if (result != mStatus_NATTraversal) LogMsg("ERROR: unknown result in regservice_callback: %ld", result); + free_service_instance(instance); if (deliver_async_error(rs, reg_service_reply, result) < 0) { abort_request(rs); @@ -1472,37 +2235,47 @@ static void regservice_callback(mDNS *const m _UNUSED, ServiceRecordSet *const s } } -static mStatus add_record_to_service(request_state *rstate, registered_service *r_srv, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl) +mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result) + { + ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext; + (void)m; //unused + + if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; } + + debugf("%##s: MemFree", rr->resrec.name.c); + if (rr->resrec.rdata != &rr->rdatastorage) + freeL("Extra RData", rr->resrec.rdata); + freeL("ExtraResourceRecord", extra); + } + + +static mStatus add_record_to_service(request_state *rstate, service_instance *instance, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl) { - ServiceRecordSet *srs = r_srv->srs; + ServiceRecordSet *srs = &instance->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) + extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); + if (!extra) { my_perror("ERROR: malloc"); - exit(1); + return mStatus_NoMemoryErr; } - bzero(ere, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd - extra = &ere->e; + bzero(extra, sizeof(ExtraResourceRecord)); // OK if oversized rdata not zero'd extra->r.resrec.rrtype = rrtype; - extra->r.rdatastorage.MaxRDLength = size; + extra->r.rdatastorage.MaxRDLength = (mDNSu16) 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; } + if (result) { freeL("ExtraResourceRecord", extra); return result; } - ere->key = rstate->hdr.reg_index; - ere->next = r_srv->extras; - r_srv->extras = ere; + extra->ClientID = rstate->hdr.reg_index; return result; } @@ -1512,29 +2285,31 @@ static void handle_add_request(request_state *rstate) uint32_t ttl; uint16_t rrtype, rdlen; char *ptr, *rdata; - mStatus result; + mStatus result = mStatus_UnknownErr; DNSServiceFlags flags; - registered_service *local, *global; - - local = rstate->servicepair.local; - global = rstate->servicepair.global; + service_info *srvinfo = rstate->service_registration; + service_instance *i; - if (!local) - { - LogMsg("ERROR: handle_add_request - no service registered"); - deliver_error(rstate, mStatus_UnknownErr); - return; - } + if (!srvinfo) { LogMsg("handle_add_request called with NULL service_registration"); return; } - ptr = rstate->msgdata; + ptr = rstate->msgdata; flags = get_flags(&ptr); rrtype = get_short(&ptr); rdlen = get_short(&ptr); rdata = get_rdata(&ptr, rdlen); ttl = get_long(&ptr); - - 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 + + if (!ttl) ttl = DefaultTTLforRRType(rrtype); + + LogOperation("%3d: DNSServiceAddRecord(%##s, %s)", rstate->sd, + (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name.c : NULL, DNSTypeName(rrtype)); + + for (i = srvinfo->instances; i; i = i->next) + { + result = add_record_to_service(rstate, i, rrtype, rdlen, rdata, ttl); + if (result && i->default_local) break; + else result = mStatus_NoError; // suppress non-local default errors + } deliver_error(rstate, result); reset_connected_rstate(rstate); @@ -1554,87 +2329,76 @@ static mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32 my_perror("ERROR: malloc"); exit(1); } - newrd->MaxRDLength = rdsize; + newrd->MaxRDLength = (mDNSu16) 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); } + if (result) { LogMsg("ERROR: mDNS_Update - %ld", 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 *lRR, *gRR = NULL; - uint16_t rdlen; + uint16_t rdlen; char *ptr, *rdata; uint32_t ttl; - mStatus result; - - if (rstate->hdr.reg_index == TXT_RECORD_INDEX) - { - if (!rstate->servicepair.local) - { - deliver_error(rstate, mStatus_BadParamErr); - return; - } - lRR = &rstate->servicepair.local->srs->RR_TXT; - if (rstate->servicepair.global) gRR = &rstate->servicepair.global->srs->RR_TXT; - } - else - { - if (rstate->servicepair.local) // registered service + mStatus result = mStatus_BadReferenceErr; + service_info *srvinfo = rstate->service_registration; + service_instance *i; + AuthRecord *rr = NULL; + + // 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 (rstate->reg_recs) + { + // update an individually registered record + registered_record_entry *reptr; + for (reptr = rstate->reg_recs; reptr; reptr = reptr->next) { - if (find_extras_by_key(rstate, &lRR, &gRR)) - { deliver_error(rstate, mStatus_BadReferenceErr); return; } + if (reptr->key == rstate->hdr.reg_index) + { + result = update_record(reptr->rr, rdlen, rdata, ttl); + goto end; + } } + result = mStatus_BadReferenceErr; + goto end; + } + + // update a record from a service record set + if (!srvinfo) { result = mStatus_BadReferenceErr; goto end; } + for (i = srvinfo->instances; i; i = i->next) + { + if (rstate->hdr.reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT; 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; + ExtraResourceRecord *e; + for (e = i->srs.Extras; e; e = e->next) + if (e->ClientID == rstate->hdr.reg_index) { rr = &e->r; break; } } + + if (!rr) { result = mStatus_BadReferenceErr; goto end; } + result = update_record(rr, rdlen, rdata, ttl); + if (result && i->default_local) goto end; + else result = mStatus_NoError; // suppress non-local default errors } - // 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); +end: + LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", rstate->sd, + (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name.c : NULL, + rr ? DNSTypeName(rr->resrec.rrtype) : ""); - 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 _UNUSED, AuthRecord *const rr, RData *oldrd) +static void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd) { + (void)m; // Unused if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd); } @@ -1643,8 +2407,8 @@ 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; + service_instance *instance = srs->ServiceContext; + request_state *req = instance->request; err = gen_rr_response(&srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, req, &rep); @@ -1668,59 +2432,63 @@ static void process_service_registration(ServiceRecordSet *const srs) else append_reply(req, rep); } -static void free_service_registration(registered_service *srv) +static void free_service_instance(service_instance *srv) { request_state *rstate = srv->request; - extra_record_entry *extra; + ExtraResourceRecord *e = srv->srs.Extras, *tmp; // 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; + service_instance *ptr = rstate->service_registration->instances, *prev = NULL; + while (ptr) + { + if (ptr == srv) + { + if (prev) prev->next = ptr->next; + else rstate->service_registration->instances = ptr->next; + break; + } + prev = ptr; + ptr = ptr->next; + } } - - while (srv->extras) + + while(e) { - 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); + e->r.RecordContext = e; + tmp = e; + e = e->next; + FreeExtraRR(gmDNS, &tmp->r, mStatus_MemFree); } 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) { - 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 (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; + service_info *info = context; + service_instance *i, *p; + if (!info) { LogMsg("regservice_termination_callback context is NULL"); return; } + if (!info->request) { LogMsg("regservice_termination_callback info->request is NULL"); return; } + i = info->instances; + while (i) + { + p = i; + i = i->next; + p->request = NULL; // clear back pointer + // only safe to free memory if registration is not valid, ie deregister fails (which invalidates p) + LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP", info->request->sd, p->srs.RR_SRV.resrec.name.c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port)); + if (mDNS_DeregisterService(gmDNS, &p->srs)) free_service_instance(p); + } + info->request->service_registration = NULL; // clear pointer from request back to info + freeL("service_info", info); } - static void handle_regrecord_request(request_state *rstate) { AuthRecord *rr; - regrecord_callback_context *rcc; registered_record_entry *re; mStatus result; @@ -1738,19 +2506,16 @@ static void handle_regrecord_request(request_state *rstate) deliver_error(rstate, mStatus_BadParamErr); return; } - - rcc = mallocL("hanlde_regrecord_request", sizeof(regrecord_callback_context)); - if (!rcc) goto malloc_error; - rcc->rstate = rstate; - rcc->client_context = rstate->hdr.client_context; - rr->RecordContext = rcc; - rr->RecordCallback = regrecord_callback; // allocate registration entry, link into list - re = mallocL("hanlde_regrecord_request", sizeof(registered_record_entry)); + re = mallocL("handle_regrecord_request", sizeof(registered_record_entry)); if (!re) goto malloc_error; re->key = rstate->hdr.reg_index; re->rr = rr; + re->rstate = rstate; + re->client_context = rstate->hdr.client_context; + rr->RecordContext = re; + rr->RecordCallback = regrecord_callback; re->next = rstate->reg_recs; rstate->reg_recs = re; @@ -1760,6 +2525,10 @@ static void handle_regrecord_request(request_state *rstate) rstate->termination_context = rstate; } + if (rr->resrec.rroriginalttl == 0) + rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); + + LogOperation("%3d: DNSServiceRegisterRecord %s", rstate->sd, RRDisplayString(gmDNS, &rr->resrec)); result = mDNS_Register(gmDNS, rr); deliver_error(rstate, result); reset_connected_rstate(rstate); @@ -1770,41 +2539,57 @@ malloc_error: return; } -static void regrecord_callback(mDNS *const m _UNUSED, AuthRecord *const rr, mStatus result) +static void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result) { - regrecord_callback_context *rcc = rr->RecordContext; + registered_record_entry *re = rr->RecordContext; + request_state *rstate = re ? re->rstate : NULL; int len; reply_state *reply; transfer_state ts; + (void)m; // Unused - if (result == mStatus_MemFree) - { - freeL("regrecord_callback", rcc); - rr->RecordContext = NULL; - freeL("regrecord_callback", rr); - return; - } - + if (!re) + { + // parent struct alreadt freed by termination callback + if (!result) LogMsg("Error: regrecord_callback: successful registration of orphaned record"); + else + { + if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result); + freeL("regrecord_callback", rr); + } + return; + } + // 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(gmDNS, rr->resrec.InterfaceID); - reply->rhdr->error = result; + reply = create_reply(reg_record_reply, len, rstate); + reply->mhdr->client_context = re->client_context; + reply->rhdr->flags = dnssd_htonl(0); + reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID)); + reply->rhdr->error = dnssd_htonl(result); + if (result) + { + // unlink from list, free memory + registered_record_entry **ptr = &re->rstate->reg_recs; + while (*ptr && (*ptr) != re) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; } + *ptr = (*ptr)->next; + freeL("regrecord_callback", re->rr); + re->rr = rr = NULL; + freeL("regrecord_callback", re); + re = NULL; + } + ts = send_msg(reply); - if (ts == t_error || ts == t_terminated) - { - abort_request(rcc->rstate); - unlink_request(rcc->rstate); - } + + if (ts == t_error || ts == t_terminated) { abort_request(rstate); unlink_request(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 - } + else if (ts == t_morecoming) append_reply(rstate, reply); // client is blocked, link reply into list + } static void connected_registration_termination(void *context) { @@ -1815,32 +2600,36 @@ static void connected_registration_termination(void *context) 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; - } + fptr->rr->RecordContext = NULL; + mDNS_Deregister(gmDNS, fptr->rr); freeL("connected_registration_termination", fptr); - } - } + } + } - - static void handle_removerecord_request(request_state *rstate) { - mStatus err; + mStatus err = mStatus_BadReferenceErr; char *ptr; + service_info *srvinfo = rstate->service_registration; ptr = rstate->msgdata; get_flags(&ptr); // flags unused - if (rstate->servicepair.local) err = remove_extra_rr_from_service(rstate); - else err = remove_record(rstate); - + if (rstate->reg_recs) err = remove_record(rstate); // remove individually registered record + else if (!srvinfo) LogOperation("%3d: DNSServiceRemoveRecord (bad ref)", rstate->sd); + else + { + LogOperation("%3d: DNSServiceRemoveRecord(%##s, %s)", rstate->sd, + (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name.c : NULL); + service_instance *i; + for (i = srvinfo->instances; i; i = i->next) + { + err = remove_extra(rstate, i); + if (err && i->default_local) break; + else err = mStatus_NoError; // suppress non-local default errors + } + } + reset_connected_rstate(rstate); if (deliver_error(rstate, err) < 0) { @@ -1853,76 +2642,41 @@ static void handle_removerecord_request(request_state *rstate) 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; - 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; - } - return err; + registered_record_entry *e, **ptr = &rstate->reg_recs; + + while(*ptr && (*ptr)->key != rstate->hdr.reg_index) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("DNSServiceRemoveRecord - bad reference"); return mStatus_BadReferenceErr; } + e = *ptr; + *ptr = e->next; // unlink + + LogOperation("%3d: DNSServiceRemoveRecord(%#s)", rstate->sd, e->rr->resrec.name.c); + shared = e->rr->resrec.RecordType == kDNSRecordTypeShared; + e->rr->RecordContext = NULL; + err = mDNS_Deregister(gmDNS, e->rr); + if (err) + { + LogMsg("ERROR: remove_record, mDNS_Deregister: %ld", err); + freeL("remove_record", e->rr); + freeL("remove_record", e); + } + return err; } -static mStatus remove_extra(request_state *rstate, registered_service *serv) +static mStatus remove_extra(request_state *rstate, service_instance *serv) { mStatus err = mStatus_BadReferenceErr; - extra_record_entry *ptr, *prev = NULL; - - ptr = serv->extras; - while (ptr) + ExtraResourceRecord *ptr; + + for (ptr = serv->srs.Extras; ptr; ptr = ptr->next) { - 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; + if (ptr->ClientID == rstate->hdr.reg_index) // found match + return mDNS_RemoveRecordFromService(gmDNS, &serv->srs, ptr, FreeExtraRR, ptr); } 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) { @@ -1956,7 +2710,7 @@ static void handle_enum_request(request_state *rstate) } // allocate context structures - def = mallocL("hanlde_enum_request", sizeof(domain_enum_t)); + 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) @@ -1985,6 +2739,7 @@ static void handle_enum_request(request_state *rstate) if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly; // make the calls + LogOperation("%3d: DNSServiceEnumerateDomains(%X)", rstate->sd, flags); err = mDNS_GetDomains(gmDNS, &all->question, all->type, NULL, InterfaceID, enum_result_callback, all); if (err == mStatus_NoError) err = mDNS_GetDomains(gmDNS, &def->question, def->type, NULL, InterfaceID, enum_result_callback, def); @@ -2013,12 +2768,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 _UNUSED, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord) +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; + (void)m; // Unused if (answer->rrtype != kDNSType_PTR) return; if (AddRecord) @@ -2028,7 +2784,10 @@ static void enum_result_callback(mDNS *const m _UNUSED, DNSQuestion *question, c flags |= kDNSServiceFlagsDefault; } ConvertDomainNameToCString(&answer->rdata->u.name, domain); - reply = format_enumeration_reply(de->rstate, domain, flags, mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID), kDNSServiceErr_NoError); + // note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from + // a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the + // network, so we just pass kDNSServiceInterfaceIndexAny + reply = format_enumeration_reply(de->rstate, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError); if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); @@ -2041,7 +2800,7 @@ static void enum_result_callback(mDNS *const m _UNUSED, DNSQuestion *question, c static reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err) { - int len; + size_t len; reply_state *reply; char *data; @@ -2052,9 +2811,9 @@ static reply_state *format_enumeration_reply(request_state *rstate, const char * len += strlen(domain) + 1; reply = create_reply(enumeration_reply, len, rstate); - reply->rhdr->flags = flags; - reply->rhdr->ifi = ifi; - reply->rhdr->error = err; + reply->rhdr->flags = dnssd_htonl(flags); + reply->rhdr->ifi = dnssd_htonl(ifi); + reply->rhdr->error = dnssd_htonl(err); data = reply->sdata; put_string(domain, &data); return reply; @@ -2079,6 +2838,7 @@ static void handle_reconfirm_request(request_state *rstate) rr = read_rr_from_ipc_msg(rstate->msgdata, 0, 1); if (!rr) return; + LogOperation("%3d: DNSServiceReconfirmRecord(%##s) %s", rstate->sd, RRDisplayString(gmDNS, &rr->resrec)); mDNS_ReconfirmByValue(gmDNS, &rr->resrec); abort_request(rstate); unlink_request(rstate); @@ -2102,7 +2862,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, int validate_flags) +static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int GetTTL, int validate_flags) { char *rdata, name[256]; AuthRecord *rr; @@ -2140,25 +2900,23 @@ static AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl, int validate_flag exit(1); } bzero(rr, sizeof(AuthRecord)); // ok if oversized rdata not zero'd - rr->resrec.rdata = &rr->rdatastorage; - rr->resrec.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex); + + mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex), + type, 0, (flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); + 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; + if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue; 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) + if (GetTTL) { rr->resrec.rroriginalttl = get_long(&msgbuf); } @@ -2193,14 +2951,16 @@ static mStatus gen_rr_response(domainname *servicename, mDNSInterfaceID id, requ len = sizeof(DNSServiceFlags); len += sizeof(uint32_t); // if index len += sizeof(DNSServiceErrorType); - len += strlen(namestr) + 1; - len += strlen(typestr) + 1; - len += strlen(domstr) + 1; + len += (int) (strlen(namestr) + 1); + len += (int) (strlen(typestr) + 1); + len += (int) (strlen(domstr) + 1); *rep = create_reply(query_reply, len, request); - (*rep)->rhdr->flags = 0; - (*rep)->rhdr->ifi = mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, id); - (*rep)->rhdr->error = kDNSServiceErr_NoError; + + (*rep)->rhdr->flags = dnssd_htonl(0); + (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, id)); + (*rep)->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); + data = (*rep)->sdata; put_string(namestr, &data); @@ -2279,6 +3039,16 @@ static int read_msg(request_state *rs) 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)) + { + ConvertHeaderBytes(&rs->hdr); + if (rs->hdr.version != VERSION) + { + LogMsg("ERROR: read_msg - client version 0x%08X does not match daemon version 0x%08X", rs->hdr.version, VERSION); + rs->ts = t_error; + return t_error; + } + } if (rs->hdr_bytes > sizeof(ipc_msg_hdr)) { LogMsg("ERROR: read_msg - read too many header bytes"); @@ -2329,7 +3099,7 @@ static int read_msg(request_state *rs) return rs->ts; rerror: - if (errno == EAGAIN || errno == EINTR) return rs->ts; + if (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return t_morecoming; my_perror("ERROR: read_msg"); rs->ts = t_error; return t_error; @@ -2353,13 +3123,16 @@ static int send_msg(reply_state *rs) return t_complete; } + ConvertHeaderBytes(rs->mhdr); nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0); + ConvertHeaderBytes(rs->mhdr); if (nwriten < 0) { - if (errno == EINTR || errno == EAGAIN) nwriten = 0; + if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK) nwriten = 0; else { - if (errno == EPIPE) +#if !defined(PLATFORM_NO_EPIPE) + if (dnssd_errno() == EPIPE) { LogMsg("broken pipe - cleanup should be handled by run-loop read wakeup"); rs->ts = t_terminated; @@ -2367,6 +3140,7 @@ static int send_msg(reply_state *rs) return t_terminated; } else +#endif { my_perror("ERROR: send\n"); rs->ts = t_error; @@ -2386,7 +3160,7 @@ static int send_msg(reply_state *rs) -static reply_state *create_reply(reply_op_t op, int datalen, request_state *request) +static reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request) { reply_state *reply; int totallen; @@ -2398,7 +3172,7 @@ static reply_state *create_reply(reply_op_t op, int datalen, request_state *requ return NULL; } - totallen = datalen + sizeof(ipc_msg_hdr); + totallen = (int) (datalen + sizeof(ipc_msg_hdr)); reply = mallocL("create_reply", sizeof(reply_state)); if (!reply) { @@ -2421,7 +3195,7 @@ static reply_state *create_reply(reply_op_t op, int datalen, request_state *requ 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->op = op; reply->mhdr->datalen = totallen - sizeof(ipc_msg_hdr); return reply; } @@ -2432,10 +3206,11 @@ static int deliver_error(request_state *rstate, mStatus err) int nwritten = -1; undelivered_error_t *undeliv; + err = dnssd_htonl(err); nwritten = send(rstate->errfd, &err, sizeof(mStatus), 0); if (nwritten < (int)sizeof(mStatus)) { - if (errno == EINTR || errno == EAGAIN) + if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK) nwritten = 0; if (nwritten < 0) { @@ -2455,36 +3230,32 @@ static int deliver_error(request_state *rstate, mStatus err) rstate->u_err = undeliv; return 0; } - if (rstate->errfd != rstate->sd) close(rstate->errfd); return 0; error: - if (rstate->errfd != rstate->sd) close(rstate->errfd); return -1; } -// returns 0 on success, -1 if send is incomplete, or on terminal failre (request is aborted) +// returns 0 on success, -1 if send is incomplete, or on terminal failure (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); + nwritten = send(rs->u_err->sd, (char *)(&rs->u_err->err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0); if (nwritten < 0) { - if (errno == EINTR || errno == EAGAIN) + if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK) 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; @@ -2506,7 +3277,7 @@ static int deliver_async_error(request_state *rs, reply_op_t op, mStatus err) 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; + reply->rhdr->error = dnssd_htonl(err); ts = send_msg(reply); if (ts == t_error || ts == t_terminated) { @@ -2525,10 +3296,11 @@ 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); + LogOperation("%3d: Removing FD", rs->sd); udsSupportRemoveFDFromEventLoop(rs->sd); - rs->sd = -1; - if (rs->errfd >= 0) close(rs->errfd); - rs->errfd = -1; + rs->sd = dnssd_InvalidSocket; + if (rs->errfd != rs->sd && rs->errfd != dnssd_InvalidSocket) dnssd_close(rs->errfd); + rs->errfd = dnssd_InvalidSocket; // free pending replies rep = rs->replies; @@ -2572,7 +3344,7 @@ static void unlink_request(request_state *rs) //hack to search-replace perror's to LogMsg's static void my_perror(char *errmsg) { - LogMsg("%s: %s", errmsg, strerror(errno)); + LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno())); } // check that the message delivered by the client is sufficiently long to extract the required data from the buffer @@ -2583,7 +3355,7 @@ static int validate_message(request_state *rstate) { uint32_t min_size; - switch(rstate->hdr.op.request_op) + switch(rstate->hdr.op) { case resolve_request: min_size = sizeof(DNSServiceFlags) + // flags sizeof(uint32_t) + // interface @@ -2626,8 +3398,11 @@ static int validate_message(request_state *rstate) 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); + break; + case setdomain_request: min_size = sizeof(DNSServiceFlags) + sizeof(char); // flags + domain + break; + default: + LogMsg("ERROR: validate_message - unsupported request type: %d", rstate->hdr.op); return -1; } @@ -2636,3 +3411,48 @@ static int validate_message(request_state *rstate) } +static uint32_t dnssd_htonl(uint32_t l) + { + uint32_t ret; + char * data; + + data = (char*) &ret; + + put_long(l, &data); + + return ret; + } + + +#if defined(_WIN32) + +static char * win32_strerror(int inErrorCode) + { + static char buffer[1024]; + DWORD n; + + memset(buffer, 0, sizeof(buffer)); + + 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'; + } + } + + return buffer; + } + +#endif diff --git a/mDNSShared/uds_daemon.h b/mDNSShared/uds_daemon.h index 0e25022..0b9dfab 100644 --- a/mDNSShared/uds_daemon.h +++ b/mDNSShared/uds_daemon.h @@ -3,8 +3,6 @@ * * @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 @@ -31,6 +29,40 @@ Change History (most recent first): $Log: uds_daemon.h,v $ +Revision 1.13 2004/12/10 05:27:26 cheshire + Guard against multiple autoname services of the same type on the same machine + +Revision 1.12 2004/12/10 04:28:28 cheshire + User not notified of name changes for services using new UDS API + +Revision 1.11 2004/12/06 21:15:23 ksekar + mDNSResponder crashed in CheckServiceRegistrations + +Revision 1.10 2004/10/26 04:31:44 cheshire +Rename CountSubTypes() as ChopSubTypes() + +Revision 1.9 2004/09/30 00:25:00 ksekar + Dynamically update default registration domains on config change + +Revision 1.8 2004/09/21 21:05:11 cheshire +Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c, +into mDNSShared/uds_daemon.c + +Revision 1.7 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.6 2004/08/11 01:58:49 cheshire +Remove "mDNS *globalInstance" parameter from udsserver_init() + +Revision 1.5 2004/06/18 04:44:58 rpantos +Use platform layer for socket types + +Revision 1.4 2004/06/12 00:51:58 cheshire +Changes for Windows compatibility + Revision 1.3 2004/01/25 00:03:21 cheshire Change to use mDNSVal16() instead of private PORT_AS_NUM() macro @@ -42,28 +74,43 @@ Changes necessary to support mDNSResponder on Linux. */ -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" +#include "dnssd_ipc.h" /* Client interface: */ #define SRS_PORT(S) mDNSVal16((S)->RR_SRV.resrec.rdata->u.srv.port) -extern int udsserver_init( mDNS *globalInstance); +extern int udsserver_init(void); // 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_info(mDNS *const m); // print out info about current state extern void udsserver_handle_configchange(void); extern int udsserver_exit(void); // should be called prior to app exit +extern void udsserver_default_reg_domain_changed(const domainname *d, mDNSBool add); +extern void udsserver_default_browse_domain_changed(const domainname *d, mDNSBool add); /* 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); +extern mStatus udsSupportAddFDToEventLoop(dnssd_sock_t fd, udsEventCallback callback, void *context); +extern mStatus udsSupportRemoveFDFromEventLoop(dnssd_sock_t fd); + +// RecordUpdatedNiceLabel() can be a no-op on platforms that don't care about updating the machine's +// global default service name (was OS X calls the "Computer Name") in response to name conflicts. +extern void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay); + +// Globals and functions defined in uds_daemon.c and also shared with the old "daemon.c" on OS X +extern mDNS mDNSStorage; +extern mDNSs32 ChopSubTypes(char *regtype); +extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p); +extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port); +extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result); +extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs); diff --git a/mDNSVxWorks/mDNSVxWorks.c b/mDNSVxWorks/mDNSVxWorks.c index 049f41c..c93e9cb 100644 --- a/mDNSVxWorks/mDNSVxWorks.c +++ b/mDNSVxWorks/mDNSVxWorks.c @@ -3,8 +3,6 @@ * * @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 @@ -29,6 +27,42 @@ Change History (most recent first): $Log: mDNSVxWorks.c,v $ +Revision 1.26 2004/10/28 02:00:35 cheshire + Call pipeDevDelete when disposing of commandPipe + +Revision 1.25 2004/10/16 00:17:01 cheshire + Replace IP TTL 255 check with local subnet source address check + +Revision 1.24 2004/09/21 21:02:56 cheshire +Set up ifname before calling mDNS_RegisterInterface() + +Revision 1.23 2004/09/17 01:08:57 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.22 2004/09/17 00:19:11 cheshire +For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4 + +Revision 1.21 2004/09/16 00:24:50 cheshire + Fix unsafe use of mDNSPlatformTimeNow() + +Revision 1.20 2004/09/14 23:42:36 cheshire + Need to seed random number generator from platform-layer data + +Revision 1.19 2004/09/14 23:16:09 cheshire +mDNS_SetFQDNs has been renamed to mDNS_SetFQDN + +Revision 1.18 2004/08/14 03:22:42 cheshire + Dynamic DNS UI <-> mDNSResponder glue +Add GetUserSpecifiedDDNSName() routine +Convert ServiceRegDomain to domainname instead of C string +Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs + +Revision 1.17 2004/07/29 19:26:03 ksekar +Plaform-level changes for NATPMP support + Revision 1.16 2004/04/22 05:11:28 bradley Added mDNSPlatformUTC for TSIG signed dynamic updates. @@ -54,7 +88,7 @@ Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 2 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. +Best solution is just to combine mDNSEmbeddedAPI.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. @@ -63,7 +97,7 @@ Revision 1.7 2003/08/20 05:58:54 bradley Removed dependence on modified mDNSCore: define structures/prototypes locally. Revision 1.6 2003/08/18 23:19:05 cheshire - mDNSResponder divide by zero in mDNSPlatformTimeNow() + mDNSResponder divide by zero in mDNSPlatformRawTime() Revision 1.5 2003/08/15 00:05:04 bradley Updated to use name/InterfaceID from new AuthRecord resrec field. Added output of new record sizes. @@ -132,7 +166,7 @@ mDNS platform plugin for VxWorks. #include "Support/MiscUtilities.h" #endif -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #include "mDNSVxWorks.h" @@ -284,7 +318,7 @@ mDNSlocal mStatus TearDownTask( mDNS * const inMDNS ); mDNSlocal void Task( mDNS *inMDNS ); mDNSlocal mStatus TaskInit( mDNS *inMDNS ); mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSocket ); -mDNSlocal void TaskSetupTimeout( mDNSs32 inNextTaskTime, struct timeval *outTimeout ); +mDNSlocal void TaskSetupTimeout( mDNS *inMDNS, mDNSs32 inNextTaskTime, struct timeval *outTimeout ); mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSSocketRef inSocketRef ); // Utilities @@ -461,7 +495,7 @@ void mDNSPlatformClose( mDNS * const inMDNS ) mStatus mDNSPlatformSendUDP( const mDNS * const inMDNS, - const DNSMessage * const inMsg, + const void * const inMsg, const mDNSu8 * const inMsgEnd, mDNSInterfaceID inInterfaceID, const mDNSAddr * inDstIP, @@ -599,7 +633,7 @@ void mDNSPlatformUnlock( const mDNS * const inMDNS ) // (a) handle immediate work (if any) resulting from this API call // (b) calculate the next sleep time between now and the next interesting event - if( ( mDNSPlatformTimeNow() - inMDNS->NextScheduledEvent ) >= 0 ) + if( ( mDNS_TimeNow(inMDNS) - inMDNS->NextScheduledEvent ) >= 0 ) { // We only need to send the reschedule event when called from a task other than the mDNS task since if we are // called from mDNS task, we'll loop back and call mDNS_Execute. This avoids filling up the command queue. @@ -701,25 +735,30 @@ mDNSexport void mDNSPlatformMemFree( void *inMem ) free( inMem ); } +//=========================================================================================================================== +// mDNSPlatformRandomSeed +//=========================================================================================================================== + +mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) +{ + return( tickGet() ); +} + //=========================================================================================================================== // mDNSPlatformTimeInit //=========================================================================================================================== -mDNSexport mStatus mDNSPlatformTimeInit( mDNSs32 *outTimeNow ) +mDNSexport mStatus mDNSPlatformTimeInit( void ) { - check( outTimeNow ); - // No special setup is required on VxWorks -- we just use tickGet(). - - *outTimeNow = mDNSPlatformTimeNow(); return( mStatus_NoError ); } //=========================================================================================================================== -// mDNSPlatformTimeNow +// mDNSPlatformRawTime //=========================================================================================================================== -mDNSs32 mDNSPlatformTimeNow( void ) +mDNSs32 mDNSPlatformRawTime( void ) { return( (mDNSs32) tickGet() ); } @@ -921,7 +960,7 @@ mDNSlocal void SetupNames( mDNS * const inMDNS ) } check( inMDNS->hostlabel.c[ 0 ] > 0 ); - mDNS_GenerateFQDN( inMDNS ); + mDNS_SetFQDN( inMDNS ); dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] ); dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] ); @@ -1020,13 +1059,14 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inA mStatus err; MDNSInterfaceItem * item; MDNSSocketRef socketRef; - const struct sockaddr_in * ipv4; + const struct sockaddr_in * ipv4, *mask; dlog( kDebugLevelVerbose, DEBUG_NAME "setting up interface (name=%s)\n", inAddr->ifa_name ); check( inMDNS ); check( inAddr ); check( inAddr->ifa_addr ); ipv4 = (const struct sockaddr_in *) inAddr->ifa_addr; + mask = (const struct sockaddr_in *) inAddr->ifa_netmask; check( outItem ); // Allocate memory for the info item. @@ -1051,11 +1091,14 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inA // Register this interface with mDNS. - item->hostSet.InterfaceID = (mDNSInterfaceID) item; - item->hostSet.ip.type = mDNSAddrType_IPv4; - item->hostSet.ip.ip.v4.NotAnInteger = ipv4->sin_addr.s_addr; - item->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses; - item->hostSet.McastTxRx = mDNStrue; + item->hostSet.InterfaceID = (mDNSInterfaceID) item; + item->hostSet.ip .type = mDNSAddrType_IPv4; + item->hostSet.ip .ip.v4.NotAnInteger = ipv4->sin_addr.s_addr; + item->hostSet.mask.type = mDNSAddrType_IPv4; + item->hostSet.mask.ip.v4.NotAnInteger = mask->sin_addr.s_addr; + item->hostSet.ifname[0] = 0; + item->hostSet.Advertise = inMDNS->AdvertiseLocalAddresses; + item->hostSet.McastTxRx = mDNStrue; err = mDNS_RegisterInterface( inMDNS, &item->hostSet ); require_noerr( err, exit ); @@ -1177,7 +1220,7 @@ mDNSlocal mStatus // Join the all-DNS multicast group so we receive Multicast DNS packets. ip.NotAnInteger = ipv4->sin_addr.s_addr; - mreq.imr_multiaddr.s_addr = AllDNSLinkGroup.NotAnInteger; + mreq.imr_multiaddr.s_addr = AllDNSLinkGroupv4.NotAnInteger; mreq.imr_interface.s_addr = ip.NotAnInteger; err = setsockopt( socketRef, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof( mreq ) ); check_errno( err, errno ); @@ -1187,7 +1230,7 @@ mDNSlocal mStatus memset( &addr, 0, sizeof( addr ) ); addr.sin_family = AF_INET; addr.sin_port = inPort.NotAnInteger; - addr.sin_addr.s_addr = AllDNSLinkGroup.NotAnInteger; + addr.sin_addr.s_addr = AllDNSLinkGroupv4.NotAnInteger; err = bind( socketRef, (struct sockaddr *) &addr, sizeof( addr ) ); check_errno( err, errno ); @@ -1292,6 +1335,10 @@ mDNSlocal mStatus TearDownCommandPipe( mDNS * const inMDNS ) if( inMDNS->p->commandPipe != ERROR ) { close( inMDNS->p->commandPipe ); +#ifdef _WRS_VXWORKS_5_X + // pipeDevDelete is not defined in older versions of VxWorks + pipeDevDelete( kMDNSPipeName, FALSE ); +#endif inMDNS->p->commandPipe = ERROR; } return( mStatus_NoError ); @@ -1511,7 +1558,7 @@ mDNSlocal void Task( mDNS *inMDNS ) inMDNS->p->rescheduled = 0; nextTaskTime = mDNS_Execute( inMDNS ); - TaskSetupTimeout( nextTaskTime, &timeout ); + TaskSetupTimeout( inMDNS, nextTaskTime, &timeout ); // Wait until something occurs (e.g. command, incoming packet, or timeout). @@ -1524,7 +1571,7 @@ mDNSlocal void Task( mDNS *inMDNS ) { // Next task timeout occurred. Loop back up to give mDNS core a chance to work. - dlog( kDebugLevelChatty, DEBUG_NAME "next task timeout occurred (%ld)\n", mDNSPlatformTimeNow() ); + dlog( kDebugLevelChatty, DEBUG_NAME "next task timeout occurred (%ld)\n", mDNS_TimeNow(inMDNS) ); continue; } @@ -1639,13 +1686,13 @@ mDNSlocal void TaskSetupReadSet( mDNS *inMDNS, fd_set *outReadSet, int *outMaxSo // TaskSetupTimeout //=========================================================================================================================== -mDNSlocal void TaskSetupTimeout( mDNSs32 inNextTaskTime, struct timeval *outTimeout ) +mDNSlocal void TaskSetupTimeout( mDNS *inMDNS, mDNSs32 inNextTaskTime, struct timeval *outTimeout ) { mDNSs32 delta; // Calculate how long to wait before performing idle processing. - delta = inNextTaskTime - mDNSPlatformTimeNow(); + delta = inNextTaskTime - mDNS_TimeNow(inMDNS); if( delta <= 0 ) { // The next task time is now or in the past. Set the timeout to fire immediately. @@ -1702,7 +1749,7 @@ mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSS srcAddr.ip.v4.NotAnInteger = addr.sin_addr.s_addr; srcPort.NotAnInteger = addr.sin_port; dstAddr.type = mDNSAddrType_IPv4; - dstAddr.ip.v4 = AllDNSLinkGroup; + dstAddr.ip.v4 = AllDNSLinkGroupv4; dstPort = MulticastDNSPort; dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" ); @@ -1719,7 +1766,7 @@ mDNSlocal void TaskProcessPacket( mDNS *inMDNS, MDNSInterfaceItem *inItem, MDNSS // Dispatch the packet to mDNS. packetEndPtr = ( (mDNSu8 *) &packet ) + n; - mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inItem->hostSet.InterfaceID, 255 ); + mDNSCoreReceive( inMDNS, &packet, packetEndPtr, &srcAddr, srcPort, &dstAddr, dstPort, inItem->hostSet.InterfaceID ); } // Update counters. diff --git a/mDNSVxWorks/mDNSVxWorks.h b/mDNSVxWorks/mDNSVxWorks.h index 537c0ee..9526ae5 100644 --- a/mDNSVxWorks/mDNSVxWorks.h +++ b/mDNSVxWorks/mDNSVxWorks.h @@ -3,8 +3,6 @@ * * @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 @@ -29,6 +27,12 @@ Change History (most recent first): $Log: mDNSVxWorks.h,v $ +Revision 1.3 2004/09/17 01:08:57 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + Revision 1.2 2003/08/12 19:56:27 cheshire Update to APSL 2.0 @@ -43,7 +47,7 @@ mDNS platform plugin for VxWorks. #include "vxWorks.h" #include "semLib.h" -#include "mDNSClientAPI.h" +#include "mDNSEmbeddedAPI.h" #ifdef __cplusplus extern "C" { diff --git a/mDNSWindows/Applications/DNSServiceTest/ToolWin32.mcp b/mDNSWindows/Applications/DNSServiceTest/ToolWin32.mcp deleted file mode 100644 index 21a4ed63564df5c789d5ab609f17095235e893fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 284642 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 diff --git a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.sln b/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.sln deleted file mode 100644 index 24abed3..0000000 --- a/mDNSWindows/Applications/ExplorerPlugin/ExplorerPlugin.sln +++ /dev/null @@ -1,21 +0,0 @@ -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/NSPTool/Tool.mcp b/mDNSWindows/Applications/NSPTool/Tool.mcp deleted file mode 100644 index a6dcb6f918cb8f4777fa05e4ded5c27a2b0e51a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 306146 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 A+yDRo diff --git a/mDNSWindows/Applications/SystemService/Service.c b/mDNSWindows/Applications/SystemService/Service.c deleted file mode 100644 index d65fa58..0000000 --- a/mDNSWindows/Applications/SystemService/Service.c +++ /dev/null @@ -1,829 +0,0 @@ -/* - * 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 deleted file mode 100644 index 0347957cafe95e43f4aebadde6e2a867f60be7dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299890 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